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 ),
2006 #endif // MDNSRESPONDER_PROJECT
2008 //===========================================================================================================================
2010 //===========================================================================================================================
2012 static OSStatus
VersionOptionCallback( CLIOption
*inOption
, const char *inArg
, int inUnset
);
2014 static void BrowseCmd( void );
2015 static void GetAddrInfoCmd( void );
2016 static void QueryRecordCmd( void );
2017 static void RegisterCmd( void );
2018 static void RegisterRecordCmd( void );
2019 static void ResolveCmd( void );
2020 static void ReconfirmCmd( void );
2021 static void GetAddrInfoPOSIXCmd( void );
2022 static void ReverseLookupCmd( void );
2023 static void PortMappingCmd( void );
2024 static void BrowseAllCmd( void );
2025 static void GetAddrInfoStressCmd( void );
2026 static void DNSQueryCmd( void );
2027 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2028 static void DNSCryptCmd( void );
2030 static void MDNSQueryCmd( void );
2031 static void PIDToUUIDCmd( void );
2032 static void DaemonVersionCmd( void );
2034 static CLIOption kGlobalOpts
[] =
2036 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback
, NULL
, NULL
,
2037 kCLIOptionFlags_NoArgument
| kCLIOptionFlags_GlobalOnly
, "Displays the version of this tool.", NULL
),
2042 Command( "browse", BrowseCmd
, kBrowseOpts
, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
2043 Command( "getAddrInfo", GetAddrInfoCmd
, kGetAddrInfoOpts
, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
2044 Command( "queryRecord", QueryRecordCmd
, kQueryRecordOpts
, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
2045 Command( "register", RegisterCmd
, kRegisterOpts
, "Uses DNSServiceRegister() to register a service.", false ),
2046 Command( "registerRecord", RegisterRecordCmd
, kRegisterRecordOpts
, "Uses DNSServiceRegisterRecord() to register a record.", false ),
2047 Command( "resolve", ResolveCmd
, kResolveOpts
, "Uses DNSServiceResolve() to resolve a service.", false ),
2048 Command( "reconfirm", ReconfirmCmd
, kReconfirmOpts
, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
2049 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd
, kGetAddrInfoPOSIXOpts
, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
2050 Command( "reverseLookup", ReverseLookupCmd
, kReverseLookupOpts
, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
2051 Command( "portMapping", PortMappingCmd
, kPortMappingOpts
, "Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ),
2052 Command( "browseAll", BrowseAllCmd
, kBrowseAllOpts
, "Browse and resolve all (or specific) services and, optionally, attempt connections.", false ),
2054 // Uncommon commands.
2056 Command( "getnameinfo", GetNameInfoCmd
, kGetNameInfoOpts
, "Calls getnameinfo() and prints results.", true ),
2057 Command( "getAddrInfoStress", GetAddrInfoStressCmd
, kGetAddrInfoStressOpts
, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
2058 Command( "DNSQuery", DNSQueryCmd
, kDNSQueryOpts
, "Crafts and sends a DNS query.", true ),
2059 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2060 Command( "DNSCrypt", DNSCryptCmd
, kDNSCryptOpts
, "Crafts and sends a DNSCrypt query.", true ),
2062 Command( "mdnsquery", MDNSQueryCmd
, kMDNSQueryOpts
, "Crafts and sends an mDNS query over the specified interface.", true ),
2063 Command( "mdnscollider", MDNSColliderCmd
, kMDNSColliderOpts
, "Creates record name collision scenarios.", true ),
2064 Command( "pid2uuid", PIDToUUIDCmd
, kPIDToUUIDOpts
, "Prints the UUID of a process.", true ),
2065 Command( "server", DNSServerCmd
, kDNSServerOpts
, "DNS server for testing.", true ),
2066 Command( "mdnsreplier", MDNSReplierCmd
, kMDNSReplierOpts
, "Responds to mDNS queries for a set of authoritative resource records.", true ),
2067 Command( "test", NULL
, kTestOpts
, "Commands for testing DNS-SD.", true ),
2068 Command( "ssdp", NULL
, kSSDPOpts
, "Simple Service Discovery Protocol (SSDP).", true ),
2069 #if( TARGET_OS_DARWIN )
2070 Command( "legacy", NULL
, kLegacyOpts
, "Legacy DNS API.", true ),
2071 Command( "dnsconfig", NULL
, kDNSConfigOpts
, "Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ),
2072 Command( "xpcsend", XPCSendCmd
, kXPCSendOpts
, "Sends a message to an XPC service.", true ),
2074 #if( MDNSRESPONDER_PROJECT )
2075 Command( "interfaceMonitor", InterfaceMonitorCmd
, kInterfaceMonitorOpts
, "mDNSResponder's interface monitor.", true ),
2076 Command( "dnsproxy", DNSProxyCmd
, kDNSProxyOpts
, "Enables mDNSResponder's DNS proxy.", true ),
2078 Command( "daemonVersion", DaemonVersionCmd
, NULL
, "Prints the version of the DNS-SD daemon.", true ),
2084 //===========================================================================================================================
2085 // Helper Prototypes
2086 //===========================================================================================================================
2088 #define kExitReason_OneShotDone "one-shot done"
2089 #define kExitReason_ReceivedResponse "received response"
2090 #define kExitReason_SIGINT "interrupt signal"
2091 #define kExitReason_Timeout "timeout"
2092 #define kExitReason_TimeLimit "time limit"
2094 static void Exit( void *inContext
) ATTRIBUTE_NORETURN
;
2096 static DNSServiceFlags
GetDNSSDFlagsFromOpts( void );
2100 kConnectionType_None
= 0,
2101 kConnectionType_Normal
= 1,
2102 kConnectionType_DelegatePID
= 2,
2103 kConnectionType_DelegateUUID
= 3
2109 ConnectionType type
;
2120 CreateConnectionFromArgString(
2121 const char * inString
,
2122 dispatch_queue_t inQueue
,
2123 DNSServiceRef
* outSDRef
,
2124 ConnectionDesc
* outDesc
);
2125 static OSStatus
InterfaceIndexFromArgString( const char *inString
, uint32_t *outIndex
);
2126 static OSStatus
RecordDataFromArgString( const char *inString
, uint8_t **outDataPtr
, size_t *outDataLen
);
2127 static OSStatus
RecordTypeFromArgString( const char *inString
, uint16_t *outValue
);
2128 static OSStatus
RecordClassFromArgString( const char *inString
, uint16_t *outValue
);
2130 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
2132 static char * InterfaceIndexToName( uint32_t inIfIndex
, char inNameBuf
[ kInterfaceNameBufLen
] );
2133 static const char * RecordTypeToString( unsigned int inValue
);
2136 DNSRecordDataToString(
2137 const void * inRDataPtr
,
2139 unsigned int inRDataType
,
2140 const void * inMsgPtr
,
2142 char ** outString
);
2146 const uint8_t * inMsgPtr
,
2153 WriteDNSQueryMessage(
2154 uint8_t inMsg
[ kDNSQueryMessageMaxLen
],
2157 const char * inQName
,
2160 size_t * outMsgLen
);
2164 typedef void ( *DispatchHandler
)( void *inContext
);
2167 DispatchSignalSourceCreate(
2169 DispatchHandler inEventHandler
,
2171 dispatch_source_t
* outSource
);
2173 DispatchSocketSourceCreate(
2175 dispatch_source_type_t inType
,
2176 dispatch_queue_t inQueue
,
2177 DispatchHandler inEventHandler
,
2178 DispatchHandler inCancelHandler
,
2180 dispatch_source_t
* outSource
);
2182 #define DispatchReadSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2183 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_READ, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2185 #define DispatchWriteSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2186 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_WRITE, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2189 DispatchTimerCreate(
2190 dispatch_time_t inStart
,
2191 uint64_t inIntervalNs
,
2192 uint64_t inLeewayNs
,
2193 dispatch_queue_t inQueue
,
2194 DispatchHandler inEventHandler
,
2195 DispatchHandler inCancelHandler
,
2197 dispatch_source_t
* outTimer
);
2199 #define DispatchTimerOneShotCreate( IN_START, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, IN_CONTEXT, OUT_TIMER ) \
2200 DispatchTimerCreate( IN_START, DISPATCH_TIME_FOREVER, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, NULL, IN_CONTEXT, OUT_TIMER )
2203 DispatchProcessMonitorCreate(
2205 unsigned long inFlags
,
2206 dispatch_queue_t inQueue
,
2207 DispatchHandler inEventHandler
,
2208 DispatchHandler inCancelHandler
,
2210 dispatch_source_t
* outMonitor
);
2212 static const char * ServiceTypeDescription( const char *inName
);
2216 SocketRef sock
; // Socket.
2217 void * userContext
; // User context.
2218 int32_t refCount
; // Reference count.
2222 static OSStatus
SocketContextCreate( SocketRef inSock
, void * inUserContext
, SocketContext
**outContext
);
2223 static SocketContext
* SocketContextRetain( SocketContext
*inContext
);
2224 static void SocketContextRelease( SocketContext
*inContext
);
2225 static void SocketContextCancelHandler( void *inContext
);
2227 #define ForgetSocketContext( X ) ForgetCustom( X, SocketContextRelease )
2229 static OSStatus
StringToInt32( const char *inString
, int32_t *outValue
);
2230 static OSStatus
StringToUInt32( const char *inString
, uint32_t *outValue
);
2231 #if( TARGET_OS_DARWIN )
2232 static int64_t _StringToInt64( const char *inString
, OSStatus
*outError
);
2233 static uint64_t _StringToUInt64( const char *inString
, OSStatus
*outError
);
2234 static pid_t
_StringToPID( const char *inString
, OSStatus
*outError
);
2236 _ParseEscapedString(
2239 const char * inDelimiters
,
2242 size_t * outCopiedLen
,
2243 size_t * outActualLen
,
2244 const char ** outPtr
);
2246 static OSStatus
StringToARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2247 static OSStatus
StringToAAAARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2248 static OSStatus
StringToDomainName( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2249 #if( TARGET_OS_DARWIN )
2250 static OSStatus
GetDefaultDNSServer( sockaddr_ip
*outAddr
);
2253 _ServerSocketOpenEx2(
2257 const void * inAddr
,
2261 Boolean inNoPortReuse
,
2262 SocketRef
* outSock
);
2264 static const struct sockaddr
* GetMDNSMulticastAddrV4( void );
2265 static const struct sockaddr
* GetMDNSMulticastAddrV6( void );
2268 CreateMulticastSocket(
2269 const struct sockaddr
* inAddr
,
2271 const char * inIfName
,
2275 SocketRef
* outSock
);
2277 static OSStatus
DecimalTextToUInt32( const char *inSrc
, const char *inEnd
, uint32_t *outValue
, const char **outPtr
);
2278 static OSStatus
CheckIntegerArgument( int inArgValue
, const char *inArgName
, int inMin
, int inMax
);
2279 static OSStatus
CheckDoubleArgument( double inArgValue
, const char *inArgName
, double inMin
, double inMax
);
2280 static OSStatus
CheckRootUser( void );
2281 static OSStatus
SpawnCommand( pid_t
*outPID
, const char *inFormat
, ... );
2282 static OSStatus
OutputFormatFromArgString( const char *inArgString
, OutputFormatType
*outFormat
);
2283 static OSStatus
OutputPropertyList( CFPropertyListRef inPList
, OutputFormatType inType
, const char *inOutputFilePath
);
2284 static OSStatus
CreateSRVRecordDataFromString( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2285 static OSStatus
CreateTXTRecordDataFromString( const char *inString
, int inDelimiter
, uint8_t **outPtr
, size_t *outLen
);
2287 CreateNSECRecordData(
2288 const uint8_t * inNextDomainName
,
2291 unsigned int inTypeCount
,
2296 const uint8_t * inNamePtr
,
2301 const uint8_t * inMName
,
2302 const uint8_t * inRName
,
2307 uint32_t inMinimumTTL
,
2310 CreateSOARecordData(
2311 const uint8_t * inMName
,
2312 const uint8_t * inRName
,
2317 uint32_t inMinimumTTL
,
2321 _DataBuffer_AppendDNSQuestion(
2323 const uint8_t * inNamePtr
,
2328 _DataBuffer_AppendDNSRecord(
2330 const uint8_t * inNamePtr
,
2335 const uint8_t * inRDataPtr
,
2336 size_t inRDataLen
);
2337 static char * _NanoTime64ToTimestamp( NanoTime64 inTime
, char *inBuf
, size_t inMaxLen
);
2339 typedef struct MDNSInterfaceItem MDNSInterfaceItem
;
2340 struct MDNSInterfaceItem
2342 MDNSInterfaceItem
* next
;
2353 kMDNSInterfaceSubset_All
= 0, // All mDNS-capable interfaces.
2354 kMDNSInterfaceSubset_AWDL
= 1, // All mDNS-capable AWDL interfaces.
2355 kMDNSInterfaceSubset_NonAWDL
= 2 // All mDNS-capable non-AWDL iterfaces.
2357 } MDNSInterfaceSubset
;
2359 static OSStatus
_MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset
, size_t inItemSize
, MDNSInterfaceItem
**outList
);
2360 static void _MDNSInterfaceListFree( MDNSInterfaceItem
*inList
);
2361 #define _MDNSInterfaceListForget( X ) ForgetCustom( X, _MDNSInterfaceListFree )
2362 static OSStatus
_MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset
, char inNameBuf
[ IF_NAMESIZE
+ 1 ], uint32_t *outIndex
);
2364 static OSStatus
_SetComputerName( CFStringRef inComputerName
, CFStringEncoding inEncoding
);
2365 static OSStatus
_SetComputerNameWithUTF8CString( const char *inComputerName
);
2366 static OSStatus
_SetLocalHostName( CFStringRef inLocalHostName
);
2367 static OSStatus
_SetLocalHostNameWithUTF8CString( const char *inLocalHostName
);
2369 static OSStatus
_SocketWriteAll( SocketRef inSock
, const void *inData
, size_t inSize
, int32_t inTimeoutSecs
);
2371 _StringToIPv4Address(
2373 StringToIPAddressFlags inFlags
,
2376 uint32_t * outSubnet
,
2377 uint32_t * outRouter
,
2378 const char ** outStr
);
2379 static void _StringArray_Free( char **inArray
, size_t inCount
);
2381 _StringToIPv6Address(
2383 StringToIPAddressFlags inFlags
,
2384 uint8_t outIPv6
[ 16 ],
2385 uint32_t * outScope
,
2388 const char ** outStr
);
2390 _ParseQuotedEscapedString(
2393 const char * inDelimiters
,
2396 size_t * outCopiedLen
,
2397 size_t * outTotalLen
,
2398 const char ** outSrc
);
2399 static void * _memdup( const void *inPtr
, size_t inLen
);
2400 static int _memicmp( const void *inP1
, const void *inP2
, size_t inLen
);
2401 static uint32_t _FNV1( const void *inData
, size_t inSize
);
2403 #define Unused( X ) (void)(X)
2405 //===========================================================================================================================
2407 //===========================================================================================================================
2409 typedef struct MDNSColliderPrivate
* MDNSColliderRef
;
2411 typedef uint32_t MDNSColliderProtocols
;
2412 #define kMDNSColliderProtocol_None 0
2413 #define kMDNSColliderProtocol_IPv4 ( 1 << 0 )
2414 #define kMDNSColliderProtocol_IPv6 ( 1 << 1 )
2416 typedef void ( *MDNSColliderStopHandler_f
)( void *inContext
, OSStatus inError
);
2418 static OSStatus
MDNSColliderCreate( dispatch_queue_t inQueue
, MDNSColliderRef
*outCollider
);
2419 static OSStatus
MDNSColliderStart( MDNSColliderRef inCollider
);
2420 static void MDNSColliderStop( MDNSColliderRef inCollider
);
2421 static void MDNSColliderSetProtocols( MDNSColliderRef inCollider
, MDNSColliderProtocols inProtocols
);
2422 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef inCollider
, uint32_t inInterfaceIndex
);
2423 static OSStatus
MDNSColliderSetProgram( MDNSColliderRef inCollider
, const char *inProgramStr
);
2425 MDNSColliderSetStopHandler(
2426 MDNSColliderRef inCollider
,
2427 MDNSColliderStopHandler_f inStopHandler
,
2428 void * inStopContext
);
2430 MDNSColliderSetRecord(
2431 MDNSColliderRef inCollider
,
2432 const uint8_t * inName
,
2434 const void * inRDataPtr
,
2435 size_t inRDataLen
);
2436 static CFTypeID
MDNSColliderGetTypeID( void );
2438 //===========================================================================================================================
2440 //===========================================================================================================================
2442 typedef struct ServiceBrowserPrivate
* ServiceBrowserRef
;
2443 typedef struct ServiceBrowserResults ServiceBrowserResults
;
2444 typedef struct SBRDomain SBRDomain
;
2445 typedef struct SBRServiceType SBRServiceType
;
2446 typedef struct SBRServiceInstance SBRServiceInstance
;
2447 typedef struct SBRIPAddress SBRIPAddress
;
2449 typedef void ( *ServiceBrowserCallback_f
)( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
);
2451 struct ServiceBrowserResults
2453 SBRDomain
* domainList
; // List of domains in which services were found.
2458 SBRDomain
* next
; // Next domain in list.
2459 char * name
; // Name of domain represented by this object.
2460 SBRServiceType
* typeList
; // List of service types in this domain.
2463 struct SBRServiceType
2465 SBRServiceType
* next
; // Next service type in list.
2466 char * name
; // Name of service type represented by this object.
2467 SBRServiceInstance
* instanceList
; // List of service instances of this service type.
2470 struct SBRServiceInstance
2472 SBRServiceInstance
* next
; // Next service instance in list.
2473 char * name
; // Name of service instance represented by this object.
2474 char * hostname
; // Target from service instance's SRV record.
2475 uint32_t ifIndex
; // Index of interface over which this service instance was discovered.
2476 uint16_t port
; // Port from service instance's SRV record.
2477 uint8_t * txtPtr
; // Service instance's TXT record data.
2478 size_t txtLen
; // Service instance's TXT record data length.
2479 SBRIPAddress
* ipaddrList
; // List of IP addresses that the hostname resolved to.
2480 uint64_t discoverTimeUs
; // Time it took to discover this service instance in microseconds.
2481 uint64_t resolveTimeUs
; // Time it took to resolve this service instance in microseconds.
2486 SBRIPAddress
* next
; // Next IP address in list.
2487 sockaddr_ip sip
; // IPv4 or IPv6 address.
2488 uint64_t resolveTimeUs
; // Time it took to resolve this IP address in microseconds.
2491 static CFTypeID
ServiceBrowserGetTypeID( void );
2493 ServiceBrowserCreate(
2494 dispatch_queue_t inQueue
,
2495 uint32_t inInterfaceIndex
,
2496 const char * inDomain
,
2497 unsigned int inBrowseTimeSecs
,
2498 Boolean inIncludeAWDL
,
2499 ServiceBrowserRef
* outBrowser
);
2500 static void ServiceBrowserStart( ServiceBrowserRef inBrowser
);
2501 static OSStatus
ServiceBrowserAddServiceType( ServiceBrowserRef inBrowser
, const char *inServiceType
);
2503 ServiceBrowserSetCallback(
2504 ServiceBrowserRef inBrowser
,
2505 ServiceBrowserCallback_f inCallback
,
2507 static void ServiceBrowserResultsRetain( ServiceBrowserResults
*inResults
);
2508 static void ServiceBrowserResultsRelease( ServiceBrowserResults
*inResults
);
2510 #define ForgetServiceBrowserResults( X ) ForgetCustom( X, ServiceBrowserResultsRelease )
2512 //===========================================================================================================================
2514 //===========================================================================================================================
2516 #define _PRINTF_EXTENSION_HANDLER_DECLARE( NAME ) \
2518 _PrintFExtension ## NAME ## Handler( \
2519 PrintFContext * inContext, \
2520 PrintFFormat * inFormat, \
2521 PrintFVAList * inArgs, \
2522 void * inUserContext )
2524 _PRINTF_EXTENSION_HANDLER_DECLARE( Timestamp
);
2525 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSMessage
);
2526 _PRINTF_EXTENSION_HANDLER_DECLARE( CallbackFlags
);
2527 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSRecordData
);
2529 int main( int argc
, const char **argv
)
2533 // Route DebugServices logging output to stderr.
2535 dlog_control( "DebugServices:output=file;stderr" );
2537 PrintFRegisterExtension( "du:time", _PrintFExtensionTimestampHandler
, NULL
);
2538 PrintFRegisterExtension( "du:dnsmsg", _PrintFExtensionDNSMessageHandler
, NULL
);
2539 PrintFRegisterExtension( "du:cbflags", _PrintFExtensionCallbackFlagsHandler
, NULL
);
2540 PrintFRegisterExtension( "du:rdata", _PrintFExtensionDNSRecordDataHandler
, NULL
);
2541 CLIInit( argc
, argv
);
2542 err
= CLIParse( kGlobalOpts
, kCLIFlags_None
);
2543 if( err
) exit( 1 );
2545 return( gExitCode
);
2548 //===========================================================================================================================
2549 // VersionOptionCallback
2550 //===========================================================================================================================
2552 static OSStatus
VersionOptionCallback( CLIOption
*inOption
, const char *inArg
, int inUnset
)
2554 const char * srcVers
;
2555 #if( MDNSRESPONDER_PROJECT )
2563 #if( MDNSRESPONDER_PROJECT )
2564 srcVers
= SourceVersionToCString( _DNS_SD_H
, srcStr
);
2566 srcVers
= DNSSDUTIL_SOURCE_VERSION
;
2568 FPrintF( stdout
, "%s version %v (%s)\n", gProgramName
, kDNSSDUtilNumVersion
, srcVers
);
2570 return( kEndingErr
);
2573 //===========================================================================================================================
2575 //===========================================================================================================================
2577 typedef struct BrowseResolveOp BrowseResolveOp
;
2579 struct BrowseResolveOp
2581 BrowseResolveOp
* next
; // Next resolve operation in list.
2582 DNSServiceRef sdRef
; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
2583 char * fullName
; // Full name of the service to resolve.
2584 uint32_t interfaceIndex
; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
2589 DNSServiceRef mainRef
; // Main sdRef for shared connection.
2590 DNSServiceRef
* opRefs
; // Array of sdRefs for individual Browse operarions.
2591 size_t opRefsCount
; // Count of array of sdRefs for non-shared connections.
2592 const char * domain
; // Domain for DNSServiceBrowse operation(s).
2593 DNSServiceFlags flags
; // Flags for DNSServiceBrowse operation(s).
2594 char ** serviceTypes
; // Array of service types to browse for.
2595 size_t serviceTypesCount
; // Count of array of service types to browse for.
2596 int timeLimitSecs
; // Time limit of DNSServiceBrowse operation in seconds.
2597 BrowseResolveOp
* resolveList
; // List of resolve and/or TXT record query operations.
2598 uint32_t ifIndex
; // Interface index of DNSServiceBrowse operation(s).
2599 Boolean printedHeader
; // True if results header has been printed.
2600 Boolean doResolve
; // True if service instances are to be resolved.
2601 Boolean doResolveTXTOnly
; // True if TXT records of service instances are to be queried.
2605 static void BrowsePrintPrologue( const BrowseContext
*inContext
);
2606 static void BrowseContextFree( BrowseContext
*inContext
);
2607 static OSStatus
BrowseResolveOpCreate( const char *inFullName
, uint32_t inInterfaceIndex
, BrowseResolveOp
**outOp
);
2608 static void BrowseResolveOpFree( BrowseResolveOp
*inOp
);
2609 static void DNSSD_API
2611 DNSServiceRef inSDRef
,
2612 DNSServiceFlags inFlags
,
2613 uint32_t inInterfaceIndex
,
2614 DNSServiceErrorType inError
,
2615 const char * inName
,
2616 const char * inRegType
,
2617 const char * inDomain
,
2619 static void DNSSD_API
2620 BrowseResolveCallback(
2621 DNSServiceRef inSDRef
,
2622 DNSServiceFlags inFlags
,
2623 uint32_t inInterfaceIndex
,
2624 DNSServiceErrorType inError
,
2625 const char * inFullName
,
2626 const char * inHostname
,
2629 const unsigned char * inTXTPtr
,
2631 static void DNSSD_API
2632 BrowseQueryRecordCallback(
2633 DNSServiceRef inSDRef
,
2634 DNSServiceFlags inFlags
,
2635 uint32_t inInterfaceIndex
,
2636 DNSServiceErrorType inError
,
2637 const char * inFullName
,
2640 uint16_t inRDataLen
,
2641 const void * inRDataPtr
,
2645 static void BrowseCmd( void )
2649 BrowseContext
* context
= NULL
;
2650 dispatch_source_t signalSource
= NULL
;
2651 int useMainConnection
;
2653 // Set up SIGINT handler.
2655 signal( SIGINT
, SIG_IGN
);
2656 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
2657 require_noerr( err
, exit
);
2658 dispatch_resume( signalSource
);
2662 context
= (BrowseContext
*) calloc( 1, sizeof( *context
) );
2663 require_action( context
, exit
, err
= kNoMemoryErr
);
2665 context
->opRefs
= (DNSServiceRef
*) calloc( gBrowse_ServiceTypesCount
, sizeof( DNSServiceRef
) );
2666 require_action( context
->opRefs
, exit
, err
= kNoMemoryErr
);
2667 context
->opRefsCount
= gBrowse_ServiceTypesCount
;
2669 // Check command parameters.
2671 if( gBrowse_TimeLimitSecs
< 0 )
2673 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs
);
2678 // Create main connection.
2680 if( gConnectionOpt
)
2682 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
2683 require_noerr_quiet( err
, exit
);
2684 useMainConnection
= true;
2688 useMainConnection
= false;
2693 context
->flags
= GetDNSSDFlagsFromOpts();
2694 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
2698 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
2699 require_noerr_quiet( err
, exit
);
2701 // Set remaining parameters.
2703 context
->serviceTypes
= gBrowse_ServiceTypes
;
2704 context
->serviceTypesCount
= gBrowse_ServiceTypesCount
;
2705 context
->domain
= gBrowse_Domain
;
2706 context
->doResolve
= gBrowse_DoResolve
? true : false;
2707 context
->timeLimitSecs
= gBrowse_TimeLimitSecs
;
2708 context
->doResolveTXTOnly
= gBrowse_QueryTXT
? true : false;
2712 BrowsePrintPrologue( context
);
2714 // Start operation(s).
2716 for( i
= 0; i
< context
->serviceTypesCount
; ++i
)
2718 DNSServiceRef sdRef
;
2720 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
2721 err
= DNSServiceBrowse( &sdRef
, context
->flags
, context
->ifIndex
, context
->serviceTypes
[ i
], context
->domain
,
2722 BrowseCallback
, context
);
2723 require_noerr( err
, exit
);
2725 context
->opRefs
[ i
] = sdRef
;
2726 if( !useMainConnection
)
2728 err
= DNSServiceSetDispatchQueue( context
->opRefs
[ i
], dispatch_get_main_queue() );
2729 require_noerr( err
, exit
);
2735 if( context
->timeLimitSecs
> 0 )
2737 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
2738 kExitReason_TimeLimit
, Exit
);
2743 dispatch_source_forget( &signalSource
);
2744 if( context
) BrowseContextFree( context
);
2745 if( err
) exit( 1 );
2748 //===========================================================================================================================
2749 // BrowsePrintPrologue
2750 //===========================================================================================================================
2752 static void BrowsePrintPrologue( const BrowseContext
*inContext
)
2754 const int timeLimitSecs
= inContext
->timeLimitSecs
;
2755 const char * const * ptr
= (const char **) inContext
->serviceTypes
;
2756 const char * const * const end
= (const char **) inContext
->serviceTypes
+ inContext
->serviceTypesCount
;
2757 char ifName
[ kInterfaceNameBufLen
];
2759 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
2761 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
2762 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
2763 FPrintF( stdout
, "Service types: %s", *ptr
++ );
2764 while( ptr
< end
) FPrintF( stdout
, ", %s", *ptr
++ );
2765 FPrintF( stdout
, "\n" );
2766 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "<NULL> (default domains)" );
2767 FPrintF( stdout
, "Time limit: " );
2768 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
2769 else FPrintF( stdout
, "∞\n" );
2770 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
2771 FPrintF( stdout
, "---\n" );
2774 //===========================================================================================================================
2775 // BrowseContextFree
2776 //===========================================================================================================================
2778 static void BrowseContextFree( BrowseContext
*inContext
)
2782 for( i
= 0; i
< inContext
->opRefsCount
; ++i
)
2784 DNSServiceForget( &inContext
->opRefs
[ i
] );
2786 if( inContext
->serviceTypes
)
2788 _StringArray_Free( inContext
->serviceTypes
, inContext
->serviceTypesCount
);
2789 inContext
->serviceTypes
= NULL
;
2790 inContext
->serviceTypesCount
= 0;
2792 DNSServiceForget( &inContext
->mainRef
);
2796 //===========================================================================================================================
2797 // BrowseResolveOpCreate
2798 //===========================================================================================================================
2800 static OSStatus
BrowseResolveOpCreate( const char *inFullName
, uint32_t inInterfaceIndex
, BrowseResolveOp
**outOp
)
2803 BrowseResolveOp
* resolveOp
;
2805 resolveOp
= (BrowseResolveOp
*) calloc( 1, sizeof( *resolveOp
) );
2806 require_action( resolveOp
, exit
, err
= kNoMemoryErr
);
2808 resolveOp
->fullName
= strdup( inFullName
);
2809 require_action( resolveOp
->fullName
, exit
, err
= kNoMemoryErr
);
2811 resolveOp
->interfaceIndex
= inInterfaceIndex
;
2818 if( resolveOp
) BrowseResolveOpFree( resolveOp
);
2822 //===========================================================================================================================
2823 // BrowseResolveOpFree
2824 //===========================================================================================================================
2826 static void BrowseResolveOpFree( BrowseResolveOp
*inOp
)
2828 DNSServiceForget( &inOp
->sdRef
);
2829 ForgetMem( &inOp
->fullName
);
2833 //===========================================================================================================================
2835 //===========================================================================================================================
2837 static void DNSSD_API
2839 DNSServiceRef inSDRef
,
2840 DNSServiceFlags inFlags
,
2841 uint32_t inInterfaceIndex
,
2842 DNSServiceErrorType inError
,
2843 const char * inName
,
2844 const char * inRegType
,
2845 const char * inDomain
,
2848 BrowseContext
* const context
= (BrowseContext
*) inContext
;
2850 BrowseResolveOp
* newOp
= NULL
;
2851 BrowseResolveOp
** p
;
2852 char fullName
[ kDNSServiceMaxDomainName
];
2857 gettimeofday( &now
, NULL
);
2860 require_noerr( err
, exit
);
2862 if( !context
->printedHeader
)
2864 FPrintF( stdout
, "%-26s %-16s IF %-20s %-20s Instance Name\n", "Timestamp", "Flags", "Domain", "Service Type" );
2865 context
->printedHeader
= true;
2867 FPrintF( stdout
, "%{du:time} %{du:cbflags} %2d %-20s %-20s %s\n",
2868 &now
, inFlags
, (int32_t) inInterfaceIndex
, inDomain
, inRegType
, inName
);
2870 if( !context
->doResolve
&& !context
->doResolveTXTOnly
) goto exit
;
2872 err
= DNSServiceConstructFullName( fullName
, inName
, inRegType
, inDomain
);
2873 require_noerr( err
, exit
);
2875 if( inFlags
& kDNSServiceFlagsAdd
)
2877 DNSServiceRef sdRef
;
2878 DNSServiceFlags flags
;
2880 err
= BrowseResolveOpCreate( fullName
, inInterfaceIndex
, &newOp
);
2881 require_noerr( err
, exit
);
2883 if( context
->mainRef
)
2885 sdRef
= context
->mainRef
;
2886 flags
= kDNSServiceFlagsShareConnection
;
2892 if( context
->doResolve
)
2894 err
= DNSServiceResolve( &sdRef
, flags
, inInterfaceIndex
, inName
, inRegType
, inDomain
, BrowseResolveCallback
,
2896 require_noerr( err
, exit
);
2900 err
= DNSServiceQueryRecord( &sdRef
, flags
, inInterfaceIndex
, fullName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
,
2901 BrowseQueryRecordCallback
, NULL
);
2902 require_noerr( err
, exit
);
2905 newOp
->sdRef
= sdRef
;
2906 if( !context
->mainRef
)
2908 err
= DNSServiceSetDispatchQueue( newOp
->sdRef
, dispatch_get_main_queue() );
2909 require_noerr( err
, exit
);
2911 for( p
= &context
->resolveList
; *p
; p
= &( *p
)->next
) {}
2917 BrowseResolveOp
* resolveOp
;
2919 for( p
= &context
->resolveList
; ( resolveOp
= *p
) != NULL
; p
= &resolveOp
->next
)
2921 if( ( resolveOp
->interfaceIndex
== inInterfaceIndex
) && ( strcasecmp( resolveOp
->fullName
, fullName
) == 0 ) )
2928 *p
= resolveOp
->next
;
2929 BrowseResolveOpFree( resolveOp
);
2934 if( newOp
) BrowseResolveOpFree( newOp
);
2935 if( err
) exit( 1 );
2938 //===========================================================================================================================
2939 // BrowseQueryRecordCallback
2940 //===========================================================================================================================
2942 static void DNSSD_API
2943 BrowseQueryRecordCallback(
2944 DNSServiceRef inSDRef
,
2945 DNSServiceFlags inFlags
,
2946 uint32_t inInterfaceIndex
,
2947 DNSServiceErrorType inError
,
2948 const char * inFullName
,
2951 uint16_t inRDataLen
,
2952 const void * inRDataPtr
,
2962 Unused( inContext
);
2964 gettimeofday( &now
, NULL
);
2967 require_noerr( err
, exit
);
2968 require_action( inType
== kDNSServiceType_TXT
, exit
, err
= kTypeErr
);
2970 FPrintF( stdout
, "%{du:time} %s %s TXT on interface %d\n TXT: %#{txt}\n",
2971 &now
, DNSServiceFlagsToAddRmvStr( inFlags
), inFullName
, (int32_t) inInterfaceIndex
, inRDataPtr
,
2972 (size_t) inRDataLen
);
2975 if( err
) exit( 1 );
2978 //===========================================================================================================================
2979 // BrowseResolveCallback
2980 //===========================================================================================================================
2982 static void DNSSD_API
2983 BrowseResolveCallback(
2984 DNSServiceRef inSDRef
,
2985 DNSServiceFlags inFlags
,
2986 uint32_t inInterfaceIndex
,
2987 DNSServiceErrorType inError
,
2988 const char * inFullName
,
2989 const char * inHostname
,
2992 const unsigned char * inTXTPtr
,
2996 char errorStr
[ 64 ];
3000 Unused( inContext
);
3002 gettimeofday( &now
, NULL
);
3004 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " error %#m", inError
);
3006 FPrintF( stdout
, "%{du:time} %s can be reached at %s:%u (interface %d)%?s\n",
3007 &now
, inFullName
, inHostname
, ntohs( inPort
), (int32_t) inInterfaceIndex
, inError
, errorStr
);
3010 FPrintF( stdout
, " TXT record: %#H\n", inTXTPtr
, (int) inTXTLen
, INT_MAX
);
3014 FPrintF( stdout
, " TXT record: %#{txt}\n", inTXTPtr
, (size_t) inTXTLen
);
3018 //===========================================================================================================================
3020 //===========================================================================================================================
3024 DNSServiceRef mainRef
; // Main sdRef for shared connection.
3025 DNSServiceRef opRef
; // sdRef for the DNSServiceGetAddrInfo operation.
3026 const char * name
; // Hostname to resolve.
3027 DNSServiceFlags flags
; // Flags argument for DNSServiceGetAddrInfo().
3028 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceGetAddrInfo().
3029 uint32_t ifIndex
; // Interface index argument for DNSServiceGetAddrInfo().
3030 int timeLimitSecs
; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
3031 Boolean printedHeader
; // True if the results header has been printed.
3032 Boolean oneShotMode
; // True if command is done after the first set of results (one-shot mode).
3033 Boolean needIPv4
; // True if in one-shot mode and an IPv4 result is needed.
3034 Boolean needIPv6
; // True if in one-shot mode and an IPv6 result is needed.
3036 } GetAddrInfoContext
;
3038 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext
*inContext
);
3039 static void GetAddrInfoContextFree( GetAddrInfoContext
*inContext
);
3040 static void DNSSD_API
3041 GetAddrInfoCallback(
3042 DNSServiceRef inSDRef
,
3043 DNSServiceFlags inFlags
,
3044 uint32_t inInterfaceIndex
,
3045 DNSServiceErrorType inError
,
3046 const char * inHostname
,
3047 const struct sockaddr
* inSockAddr
,
3051 static void GetAddrInfoCmd( void )
3054 DNSServiceRef sdRef
;
3055 GetAddrInfoContext
* context
= NULL
;
3056 dispatch_source_t signalSource
= NULL
;
3057 int useMainConnection
;
3059 // Set up SIGINT handler.
3061 signal( SIGINT
, SIG_IGN
);
3062 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3063 require_noerr( err
, exit
);
3064 dispatch_resume( signalSource
);
3066 // Check command parameters.
3068 if( gGetAddrInfo_TimeLimitSecs
< 0 )
3070 FPrintF( stderr
, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs
);
3077 context
= (GetAddrInfoContext
*) calloc( 1, sizeof( *context
) );
3078 require_action( context
, exit
, err
= kNoMemoryErr
);
3080 // Create main connection.
3082 if( gConnectionOpt
)
3084 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
3085 require_noerr_quiet( err
, exit
);
3086 useMainConnection
= true;
3090 useMainConnection
= false;
3095 context
->flags
= GetDNSSDFlagsFromOpts();
3096 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
3100 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3101 require_noerr_quiet( err
, exit
);
3103 // Set remaining parameters.
3105 context
->name
= gGetAddrInfo_Name
;
3106 context
->timeLimitSecs
= gGetAddrInfo_TimeLimitSecs
;
3107 if( gGetAddrInfo_ProtocolIPv4
) context
->protocols
|= kDNSServiceProtocol_IPv4
;
3108 if( gGetAddrInfo_ProtocolIPv6
) context
->protocols
|= kDNSServiceProtocol_IPv6
;
3109 if( gGetAddrInfo_OneShot
)
3111 context
->oneShotMode
= true;
3112 context
->needIPv4
= ( gGetAddrInfo_ProtocolIPv4
|| !gGetAddrInfo_ProtocolIPv6
) ? true : false;
3113 context
->needIPv6
= ( gGetAddrInfo_ProtocolIPv6
|| !gGetAddrInfo_ProtocolIPv4
) ? true : false;
3118 GetAddrInfoPrintPrologue( context
);
3122 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
3123 err
= DNSServiceGetAddrInfo( &sdRef
, context
->flags
, context
->ifIndex
, context
->protocols
, context
->name
,
3124 GetAddrInfoCallback
, context
);
3125 require_noerr( err
, exit
);
3127 context
->opRef
= sdRef
;
3128 if( !useMainConnection
)
3130 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3131 require_noerr( err
, exit
);
3136 if( context
->timeLimitSecs
> 0 )
3138 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
3139 kExitReason_TimeLimit
, Exit
);
3144 dispatch_source_forget( &signalSource
);
3145 if( context
) GetAddrInfoContextFree( context
);
3146 if( err
) exit( 1 );
3149 //===========================================================================================================================
3150 // GetAddrInfoPrintPrologue
3151 //===========================================================================================================================
3153 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext
*inContext
)
3155 const int timeLimitSecs
= inContext
->timeLimitSecs
;
3156 char ifName
[ kInterfaceNameBufLen
];
3158 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3160 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3161 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3162 FPrintF( stdout
, "Protocols: %#{flags}\n", inContext
->protocols
, kDNSServiceProtocolDescriptors
);
3163 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
3164 FPrintF( stdout
, "Mode: %s\n", inContext
->oneShotMode
? "one-shot" : "continuous" );
3165 FPrintF( stdout
, "Time limit: " );
3166 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
3167 else FPrintF( stdout
, "∞\n" );
3168 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3169 FPrintF( stdout
, "---\n" );
3172 //===========================================================================================================================
3173 // GetAddrInfoContextFree
3174 //===========================================================================================================================
3176 static void GetAddrInfoContextFree( GetAddrInfoContext
*inContext
)
3178 DNSServiceForget( &inContext
->opRef
);
3179 DNSServiceForget( &inContext
->mainRef
);
3183 //===========================================================================================================================
3184 // GetAddrInfoCallback
3185 //===========================================================================================================================
3187 static void DNSSD_API
3188 GetAddrInfoCallback(
3189 DNSServiceRef inSDRef
,
3190 DNSServiceFlags inFlags
,
3191 uint32_t inInterfaceIndex
,
3192 DNSServiceErrorType inError
,
3193 const char * inHostname
,
3194 const struct sockaddr
* inSockAddr
,
3198 GetAddrInfoContext
* const context
= (GetAddrInfoContext
*) inContext
;
3201 const char * addrStr
;
3202 char addrStrBuf
[ kSockAddrStringMaxSize
];
3206 gettimeofday( &now
, NULL
);
3210 case kDNSServiceErr_NoError
:
3211 case kDNSServiceErr_NoSuchRecord
:
3215 case kDNSServiceErr_Timeout
:
3216 Exit( kExitReason_Timeout
);
3223 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
3225 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
3232 err
= SockAddrToString( inSockAddr
, kSockAddrStringFlagsNone
, addrStrBuf
);
3233 require_noerr( err
, exit
);
3234 addrStr
= addrStrBuf
;
3238 addrStr
= ( inSockAddr
->sa_family
== AF_INET
) ? kNoSuchRecordAStr
: kNoSuchRecordAAAAStr
;
3241 if( !context
->printedHeader
)
3243 FPrintF( stdout
, "%-26s %-16s IF %-30s %-34s %6s\n", "Timestamp", "Flags", "Hostname", "Address", "TTL" );
3244 context
->printedHeader
= true;
3246 FPrintF( stdout
, "%{du:time} %{du:cbflags} %2d %-30s %-34s %6u\n",
3247 &now
, inFlags
, (int32_t) inInterfaceIndex
, inHostname
, addrStr
, inTTL
);
3249 if( context
->oneShotMode
)
3251 if( inFlags
& kDNSServiceFlagsAdd
)
3253 if( inSockAddr
->sa_family
== AF_INET
) context
->needIPv4
= false;
3254 else context
->needIPv6
= false;
3256 if( !( inFlags
& kDNSServiceFlagsMoreComing
) && !context
->needIPv4
&& !context
->needIPv6
)
3258 Exit( kExitReason_OneShotDone
);
3263 if( err
) exit( 1 );
3266 //===========================================================================================================================
3268 //===========================================================================================================================
3272 DNSServiceRef mainRef
; // Main sdRef for shared connection.
3273 DNSServiceRef opRef
; // sdRef for the DNSServiceQueryRecord operation.
3274 const char * recordName
; // Resource record name argument for DNSServiceQueryRecord().
3275 DNSServiceFlags flags
; // Flags argument for DNSServiceQueryRecord().
3276 uint32_t ifIndex
; // Interface index argument for DNSServiceQueryRecord().
3277 int timeLimitSecs
; // Time limit for the DNSServiceQueryRecord() operation in seconds.
3278 uint16_t recordType
; // Resource record type argument for DNSServiceQueryRecord().
3279 Boolean printedHeader
; // True if the results header was printed.
3280 Boolean oneShotMode
; // True if command is done after the first set of results (one-shot mode).
3281 Boolean gotRecord
; // True if in one-shot mode and received at least one record of the desired type.
3282 Boolean printRawRData
; // True if RDATA results are not to be formatted when printed.
3284 } QueryRecordContext
;
3286 static void QueryRecordPrintPrologue( const QueryRecordContext
*inContext
);
3287 static void QueryRecordContextFree( QueryRecordContext
*inContext
);
3288 static void DNSSD_API
3289 QueryRecordCallback(
3290 DNSServiceRef inSDRef
,
3291 DNSServiceFlags inFlags
,
3292 uint32_t inInterfaceIndex
,
3293 DNSServiceErrorType inError
,
3294 const char * inFullName
,
3297 uint16_t inRDataLen
,
3298 const void * inRDataPtr
,
3302 static void QueryRecordCmd( void )
3305 DNSServiceRef sdRef
;
3306 QueryRecordContext
* context
= NULL
;
3307 dispatch_source_t signalSource
= NULL
;
3308 int useMainConnection
;
3310 // Set up SIGINT handler.
3312 signal( SIGINT
, SIG_IGN
);
3313 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3314 require_noerr( err
, exit
);
3315 dispatch_resume( signalSource
);
3319 context
= (QueryRecordContext
*) calloc( 1, sizeof( *context
) );
3320 require_action( context
, exit
, err
= kNoMemoryErr
);
3322 // Check command parameters.
3324 if( gQueryRecord_TimeLimitSecs
< 0 )
3326 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs
);
3331 // Create main connection.
3333 if( gConnectionOpt
)
3335 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
3336 require_noerr_quiet( err
, exit
);
3337 useMainConnection
= true;
3341 useMainConnection
= false;
3346 context
->flags
= GetDNSSDFlagsFromOpts();
3347 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
3351 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3352 require_noerr_quiet( err
, exit
);
3356 err
= RecordTypeFromArgString( gQueryRecord_Type
, &context
->recordType
);
3357 require_noerr( err
, exit
);
3359 // Set remaining parameters.
3361 context
->recordName
= gQueryRecord_Name
;
3362 context
->timeLimitSecs
= gQueryRecord_TimeLimitSecs
;
3363 context
->oneShotMode
= gQueryRecord_OneShot
? true : false;
3364 context
->printRawRData
= gQueryRecord_RawRData
? true : false;
3368 QueryRecordPrintPrologue( context
);
3372 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
3373 err
= DNSServiceQueryRecord( &sdRef
, context
->flags
, context
->ifIndex
, context
->recordName
, context
->recordType
,
3374 kDNSServiceClass_IN
, QueryRecordCallback
, context
);
3375 require_noerr( err
, exit
);
3377 context
->opRef
= sdRef
;
3378 if( !useMainConnection
)
3380 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3381 require_noerr( err
, exit
);
3386 if( context
->timeLimitSecs
> 0 )
3388 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_TimeLimit
,
3394 dispatch_source_forget( &signalSource
);
3395 if( context
) QueryRecordContextFree( context
);
3396 if( err
) exit( 1 );
3399 //===========================================================================================================================
3400 // QueryRecordContextFree
3401 //===========================================================================================================================
3403 static void QueryRecordContextFree( QueryRecordContext
*inContext
)
3405 DNSServiceForget( &inContext
->opRef
);
3406 DNSServiceForget( &inContext
->mainRef
);
3410 //===========================================================================================================================
3411 // QueryRecordPrintPrologue
3412 //===========================================================================================================================
3414 static void QueryRecordPrintPrologue( const QueryRecordContext
*inContext
)
3416 const int timeLimitSecs
= inContext
->timeLimitSecs
;
3417 char ifName
[ kInterfaceNameBufLen
];
3419 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3421 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3422 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3423 FPrintF( stdout
, "Name: %s\n", inContext
->recordName
);
3424 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->recordType
), inContext
->recordType
);
3425 FPrintF( stdout
, "Mode: %s\n", inContext
->oneShotMode
? "one-shot" : "continuous" );
3426 FPrintF( stdout
, "Time limit: " );
3427 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
3428 else FPrintF( stdout
, "∞\n" );
3429 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3430 FPrintF( stdout
, "---\n" );
3434 //===========================================================================================================================
3435 // QueryRecordCallback
3436 //===========================================================================================================================
3438 static void DNSSD_API
3439 QueryRecordCallback(
3440 DNSServiceRef inSDRef
,
3441 DNSServiceFlags inFlags
,
3442 uint32_t inInterfaceIndex
,
3443 DNSServiceErrorType inError
,
3444 const char * inFullName
,
3447 uint16_t inRDataLen
,
3448 const void * inRDataPtr
,
3452 QueryRecordContext
* const context
= (QueryRecordContext
*) inContext
;
3455 char * rdataStr
= NULL
;
3459 gettimeofday( &now
, NULL
);
3463 case kDNSServiceErr_NoError
:
3464 case kDNSServiceErr_NoSuchRecord
:
3468 case kDNSServiceErr_Timeout
:
3469 Exit( kExitReason_Timeout
);
3476 if( inError
!= kDNSServiceErr_NoSuchRecord
)
3478 if( !context
->printRawRData
) DNSRecordDataToString( inRDataPtr
, inRDataLen
, inType
, NULL
, 0, &rdataStr
);
3481 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, INT_MAX
);
3482 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
3486 if( !context
->printedHeader
)
3488 FPrintF( stdout
, "%-26s %-16s IF %-32s %-5s %-5s %6s RData\n",
3489 "Timestamp", "Flags", "Name", "Type", "Class", "TTL" );
3490 context
->printedHeader
= true;
3492 FPrintF( stdout
, "%{du:time} %{du:cbflags} %2d %-32s %-5s %?-5s%?5u %6u %s\n",
3493 &now
, inFlags
, (int32_t) inInterfaceIndex
, inFullName
, RecordTypeToString( inType
),
3494 ( inClass
== kDNSServiceClass_IN
), "IN", ( inClass
!= kDNSServiceClass_IN
), inClass
, inTTL
,
3495 rdataStr
? rdataStr
: kNoSuchRecordStr
);
3497 if( context
->oneShotMode
)
3499 if( ( inFlags
& kDNSServiceFlagsAdd
) &&
3500 ( ( context
->recordType
== kDNSServiceType_ANY
) || ( context
->recordType
== inType
) ) )
3502 context
->gotRecord
= true;
3504 if( !( inFlags
& kDNSServiceFlagsMoreComing
) && context
->gotRecord
) Exit( kExitReason_OneShotDone
);
3508 FreeNullSafe( rdataStr
);
3509 if( err
) exit( 1 );
3512 //===========================================================================================================================
3514 //===========================================================================================================================
3518 DNSRecordRef recordRef
; // Reference returned by DNSServiceAddRecord().
3519 uint8_t * dataPtr
; // Record data.
3520 size_t dataLen
; // Record data length.
3521 uint32_t ttl
; // Record TTL value.
3522 uint16_t type
; // Record type.
3528 DNSServiceRef opRef
; // sdRef for DNSServiceRegister operation.
3529 const char * name
; // Service name argument for DNSServiceRegister().
3530 const char * type
; // Service type argument for DNSServiceRegister().
3531 const char * domain
; // Domain in which advertise the service.
3532 uint8_t * txtPtr
; // Service TXT record data. (malloc'd)
3533 size_t txtLen
; // Service TXT record data len.
3534 ExtraRecord
* extraRecords
; // Array of extra records to add to registered service.
3535 size_t extraRecordsCount
; // Number of extra records.
3536 uint8_t * updateTXTPtr
; // Pointer to record data for TXT record update. (malloc'd)
3537 size_t updateTXTLen
; // Length of record data for TXT record update.
3538 uint32_t updateTTL
; // TTL of updated TXT record.
3539 int updateDelayMs
; // Post-registration TXT record update delay in milliseconds.
3540 DNSServiceFlags flags
; // Flags argument for DNSServiceRegister().
3541 uint32_t ifIndex
; // Interface index argument for DNSServiceRegister().
3542 int lifetimeMs
; // Lifetime of the record registration in milliseconds.
3543 uint16_t port
; // Service instance's port number.
3544 Boolean printedHeader
; // True if results header was printed.
3545 Boolean didRegister
; // True if service was registered.
3549 static void RegisterPrintPrologue( const RegisterContext
*inContext
);
3550 static void RegisterContextFree( RegisterContext
*inContext
);
3551 static void DNSSD_API
3553 DNSServiceRef inSDRef
,
3554 DNSServiceFlags inFlags
,
3555 DNSServiceErrorType inError
,
3556 const char * inName
,
3557 const char * inType
,
3558 const char * inDomain
,
3560 static void RegisterUpdate( void *inContext
);
3562 static void RegisterCmd( void )
3565 RegisterContext
* context
= NULL
;
3566 dispatch_source_t signalSource
= NULL
;
3568 // Set up SIGINT handler.
3570 signal( SIGINT
, SIG_IGN
);
3571 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3572 require_noerr( err
, exit
);
3573 dispatch_resume( signalSource
);
3577 context
= (RegisterContext
*) calloc( 1, sizeof( *context
) );
3578 require_action( context
, exit
, err
= kNoMemoryErr
);
3580 // Check command parameters.
3582 if( ( gRegister_Port
< 0 ) || ( gRegister_Port
> UINT16_MAX
) )
3584 FPrintF( stderr
, "Port number %d is out-of-range.\n", gRegister_Port
);
3589 if( ( gAddRecord_DataCount
!= gAddRecord_TypesCount
) || ( gAddRecord_TTLsCount
!= gAddRecord_TypesCount
) )
3591 FPrintF( stderr
, "There are missing additional record parameters.\n" );
3598 context
->flags
= GetDNSSDFlagsFromOpts();
3600 // Get interface index.
3602 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3603 require_noerr_quiet( err
, exit
);
3605 // Get TXT record data.
3609 err
= RecordDataFromArgString( gRegister_TXT
, &context
->txtPtr
, &context
->txtLen
);
3610 require_noerr_quiet( err
, exit
);
3613 // Set remaining parameters.
3615 context
->name
= gRegister_Name
;
3616 context
->type
= gRegister_Type
;
3617 context
->domain
= gRegister_Domain
;
3618 context
->port
= (uint16_t) gRegister_Port
;
3619 context
->lifetimeMs
= gRegister_LifetimeMs
;
3621 if( gAddRecord_TypesCount
> 0 )
3625 context
->extraRecords
= (ExtraRecord
*) calloc( gAddRecord_TypesCount
, sizeof( ExtraRecord
) );
3626 require_action( context
, exit
, err
= kNoMemoryErr
);
3627 context
->extraRecordsCount
= gAddRecord_TypesCount
;
3629 for( i
= 0; i
< gAddRecord_TypesCount
; ++i
)
3631 ExtraRecord
* const extraRecord
= &context
->extraRecords
[ i
];
3633 err
= RecordTypeFromArgString( gAddRecord_Types
[ i
], &extraRecord
->type
);
3634 require_noerr( err
, exit
);
3636 err
= StringToUInt32( gAddRecord_TTLs
[ i
], &extraRecord
->ttl
);
3639 FPrintF( stderr
, "Invalid TTL value: %s\n", gAddRecord_TTLs
[ i
] );
3644 err
= RecordDataFromArgString( gAddRecord_Data
[ i
], &extraRecord
->dataPtr
, &extraRecord
->dataLen
);
3645 require_noerr_quiet( err
, exit
);
3649 if( gUpdateRecord_Data
)
3651 err
= RecordDataFromArgString( gUpdateRecord_Data
, &context
->updateTXTPtr
, &context
->updateTXTLen
);
3652 require_noerr_quiet( err
, exit
);
3654 context
->updateTTL
= (uint32_t) gUpdateRecord_TTL
;
3655 context
->updateDelayMs
= gUpdateRecord_DelayMs
;
3660 RegisterPrintPrologue( context
);
3664 err
= DNSServiceRegister( &context
->opRef
, context
->flags
, context
->ifIndex
, context
->name
, context
->type
,
3665 context
->domain
, NULL
, htons( context
->port
), (uint16_t) context
->txtLen
, context
->txtPtr
,
3666 RegisterCallback
, context
);
3667 ForgetMem( &context
->txtPtr
);
3668 require_noerr( err
, exit
);
3670 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3671 require_noerr( err
, exit
);
3676 dispatch_source_forget( &signalSource
);
3677 if( context
) RegisterContextFree( context
);
3678 if( err
) exit( 1 );
3681 //===========================================================================================================================
3682 // RegisterPrintPrologue
3683 //===========================================================================================================================
3685 static void RegisterPrintPrologue( const RegisterContext
*inContext
)
3689 char ifName
[ kInterfaceNameBufLen
];
3691 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3693 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3694 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3695 FPrintF( stdout
, "Name: %s\n", inContext
->name
? inContext
->name
: "<NULL>" );
3696 FPrintF( stdout
, "Type: %s\n", inContext
->type
);
3697 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "<NULL> (default domains)" );
3698 FPrintF( stdout
, "Port: %u\n", inContext
->port
);
3699 FPrintF( stdout
, "TXT data: %#{txt}\n", inContext
->txtPtr
, inContext
->txtLen
);
3700 infinite
= ( inContext
->lifetimeMs
< 0 ) ? true : false;
3701 FPrintF( stdout
, "Lifetime: %?s%?d ms\n", infinite
, "∞", !infinite
, inContext
->lifetimeMs
);
3702 if( inContext
->updateTXTPtr
)
3704 FPrintF( stdout
, "\nUpdate record:\n" );
3705 FPrintF( stdout
, " Delay: %d ms\n", ( inContext
->updateDelayMs
> 0 ) ? inContext
->updateDelayMs
: 0 );
3706 FPrintF( stdout
, " TTL: %u%?s\n",
3707 inContext
->updateTTL
, inContext
->updateTTL
== 0, " (system will use a default value.)" );
3708 FPrintF( stdout
, " TXT data: %#{txt}\n", inContext
->updateTXTPtr
, inContext
->updateTXTLen
);
3710 if( inContext
->extraRecordsCount
> 0 ) FPrintF( stdout
, "\n" );
3711 for( i
= 0; i
< inContext
->extraRecordsCount
; ++i
)
3713 const ExtraRecord
* record
= &inContext
->extraRecords
[ i
];
3715 FPrintF( stdout
, "Extra record %zu:\n", i
+ 1 );
3716 FPrintF( stdout
, " Type: %s (%u)\n", RecordTypeToString( record
->type
), record
->type
);
3717 FPrintF( stdout
, " TTL: %u%?s\n", record
->ttl
, record
->ttl
== 0, " (system will use a default value.)" );
3718 FPrintF( stdout
, " RData: %#H\n\n", record
->dataPtr
, (int) record
->dataLen
, INT_MAX
);
3720 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3721 FPrintF( stdout
, "---\n" );
3724 //===========================================================================================================================
3725 // RegisterContextFree
3726 //===========================================================================================================================
3728 static void RegisterContextFree( RegisterContext
*inContext
)
3730 ExtraRecord
* record
;
3731 const ExtraRecord
* const end
= inContext
->extraRecords
+ inContext
->extraRecordsCount
;
3733 DNSServiceForget( &inContext
->opRef
);
3734 ForgetMem( &inContext
->txtPtr
);
3735 for( record
= inContext
->extraRecords
; record
< end
; ++record
)
3737 check( !record
->recordRef
);
3738 ForgetMem( &record
->dataPtr
);
3740 ForgetMem( &inContext
->extraRecords
);
3741 ForgetMem( &inContext
->updateTXTPtr
);
3745 //===========================================================================================================================
3747 //===========================================================================================================================
3749 static void DNSSD_API
3751 DNSServiceRef inSDRef
,
3752 DNSServiceFlags inFlags
,
3753 DNSServiceErrorType inError
,
3754 const char * inName
,
3755 const char * inType
,
3756 const char * inDomain
,
3759 RegisterContext
* const context
= (RegisterContext
*) inContext
;
3765 gettimeofday( &now
, NULL
);
3767 if( !context
->printedHeader
)
3769 FPrintF( stdout
, "%-26s %-16s Service\n", "Timestamp", "Flags" );
3770 context
->printedHeader
= true;
3772 FPrintF( stdout
, "%{du:time} %{du:cbflags} %s.%s%s %?#m\n", &now
, inFlags
, inName
, inType
, inDomain
, inError
, inError
);
3774 require_noerr_action_quiet( inError
, exit
, err
= inError
);
3776 if( !context
->didRegister
&& ( inFlags
& kDNSServiceFlagsAdd
) )
3778 context
->didRegister
= true;
3779 if( context
->updateTXTPtr
)
3781 if( context
->updateDelayMs
> 0 )
3783 dispatch_after_f( dispatch_time_milliseconds( context
->updateDelayMs
), dispatch_get_main_queue(),
3784 context
, RegisterUpdate
);
3788 RegisterUpdate( context
);
3791 if( context
->extraRecordsCount
> 0 )
3793 ExtraRecord
* record
;
3794 const ExtraRecord
* const end
= context
->extraRecords
+ context
->extraRecordsCount
;
3796 for( record
= context
->extraRecords
; record
< end
; ++record
)
3798 err
= DNSServiceAddRecord( context
->opRef
, &record
->recordRef
, 0, record
->type
,
3799 (uint16_t) record
->dataLen
, record
->dataPtr
, record
->ttl
);
3800 require_noerr( err
, exit
);
3803 if( context
->lifetimeMs
== 0 )
3805 Exit( kExitReason_TimeLimit
);
3807 else if( context
->lifetimeMs
> 0 )
3809 dispatch_after_f( dispatch_time_milliseconds( context
->lifetimeMs
), dispatch_get_main_queue(),
3810 kExitReason_TimeLimit
, Exit
);
3816 if( err
) exit( 1 );
3819 //===========================================================================================================================
3821 //===========================================================================================================================
3823 static void RegisterUpdate( void *inContext
)
3826 RegisterContext
* const context
= (RegisterContext
*) inContext
;
3828 err
= DNSServiceUpdateRecord( context
->opRef
, NULL
, 0, (uint16_t) context
->updateTXTLen
, context
->updateTXTPtr
,
3829 context
->updateTTL
);
3830 require_noerr( err
, exit
);
3833 if( err
) exit( 1 );
3836 //===========================================================================================================================
3837 // RegisterRecordCmd
3838 //===========================================================================================================================
3842 DNSServiceRef conRef
; // sdRef to be initialized by DNSServiceCreateConnection().
3843 DNSRecordRef recordRef
; // Registered record reference.
3844 const char * recordName
; // Name of resource record.
3845 uint8_t * dataPtr
; // Pointer to resource record data.
3846 size_t dataLen
; // Length of resource record data.
3847 uint32_t ttl
; // TTL value of resource record in seconds.
3848 uint32_t ifIndex
; // Interface index argument for DNSServiceRegisterRecord().
3849 DNSServiceFlags flags
; // Flags argument for DNSServiceRegisterRecord().
3850 int lifetimeMs
; // Lifetime of the record registration in milliseconds.
3851 uint16_t recordType
; // Resource record type.
3852 uint8_t * updateDataPtr
; // Pointer to data for record update. (malloc'd)
3853 size_t updateDataLen
; // Length of data for record update.
3854 uint32_t updateTTL
; // TTL for updated record.
3855 int updateDelayMs
; // Post-registration record update delay in milliseconds.
3856 Boolean didRegister
; // True if the record was registered.
3858 } RegisterRecordContext
;
3860 static void RegisterRecordPrintPrologue( const RegisterRecordContext
*inContext
);
3861 static void RegisterRecordContextFree( RegisterRecordContext
*inContext
);
3862 static void DNSSD_API
3863 RegisterRecordCallback(
3864 DNSServiceRef inSDRef
,
3865 DNSRecordRef inRecordRef
,
3866 DNSServiceFlags inFlags
,
3867 DNSServiceErrorType inError
,
3869 static void RegisterRecordUpdate( void *inContext
);
3871 static void RegisterRecordCmd( void )
3874 RegisterRecordContext
* context
= NULL
;
3875 dispatch_source_t signalSource
= NULL
;
3877 // Set up SIGINT handler.
3879 signal( SIGINT
, SIG_IGN
);
3880 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3881 require_noerr( err
, exit
);
3882 dispatch_resume( signalSource
);
3886 context
= (RegisterRecordContext
*) calloc( 1, sizeof( *context
) );
3887 require_action( context
, exit
, err
= kNoMemoryErr
);
3889 // Create connection.
3891 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->conRef
, NULL
);
3892 require_noerr_quiet( err
, exit
);
3896 context
->flags
= GetDNSSDFlagsFromOpts();
3900 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3901 require_noerr_quiet( err
, exit
);
3905 err
= RecordTypeFromArgString( gRegisterRecord_Type
, &context
->recordType
);
3906 require_noerr( err
, exit
);
3910 if( gRegisterRecord_Data
)
3912 err
= RecordDataFromArgString( gRegisterRecord_Data
, &context
->dataPtr
, &context
->dataLen
);
3913 require_noerr_quiet( err
, exit
);
3916 // Set remaining parameters.
3918 context
->recordName
= gRegisterRecord_Name
;
3919 context
->ttl
= (uint32_t) gRegisterRecord_TTL
;
3920 context
->lifetimeMs
= gRegisterRecord_LifetimeMs
;
3924 if( gRegisterRecord_UpdateData
)
3926 err
= RecordDataFromArgString( gRegisterRecord_UpdateData
, &context
->updateDataPtr
, &context
->updateDataLen
);
3927 require_noerr_quiet( err
, exit
);
3929 context
->updateTTL
= (uint32_t) gRegisterRecord_UpdateTTL
;
3930 context
->updateDelayMs
= gRegisterRecord_UpdateDelayMs
;
3935 RegisterRecordPrintPrologue( context
);
3939 err
= DNSServiceRegisterRecord( context
->conRef
, &context
->recordRef
, context
->flags
, context
->ifIndex
,
3940 context
->recordName
, context
->recordType
, kDNSServiceClass_IN
, (uint16_t) context
->dataLen
, context
->dataPtr
,
3941 context
->ttl
, RegisterRecordCallback
, context
);
3944 FPrintF( stderr
, "DNSServiceRegisterRecord() returned %#m\n", err
);
3951 dispatch_source_forget( &signalSource
);
3952 if( context
) RegisterRecordContextFree( context
);
3953 if( err
) exit( 1 );
3956 //===========================================================================================================================
3957 // RegisterRecordPrintPrologue
3958 //===========================================================================================================================
3960 static void RegisterRecordPrintPrologue( const RegisterRecordContext
*inContext
)
3963 char ifName
[ kInterfaceNameBufLen
];
3965 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3967 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3968 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3969 FPrintF( stdout
, "Name: %s\n", inContext
->recordName
);
3970 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->recordType
), inContext
->recordType
);
3971 FPrintF( stdout
, "TTL: %u\n", inContext
->ttl
);
3972 FPrintF( stdout
, "Data: %#H\n", inContext
->dataPtr
, (int) inContext
->dataLen
, INT_MAX
);
3973 infinite
= ( inContext
->lifetimeMs
< 0 ) ? true : false;
3974 FPrintF( stdout
, "Lifetime: %?s%?d ms\n", infinite
, "∞", !infinite
, inContext
->lifetimeMs
);
3975 if( inContext
->updateDataPtr
)
3977 FPrintF( stdout
, "\nUpdate record:\n" );
3978 FPrintF( stdout
, " Delay: %d ms\n", ( inContext
->updateDelayMs
>= 0 ) ? inContext
->updateDelayMs
: 0 );
3979 FPrintF( stdout
, " TTL: %u%?s\n",
3980 inContext
->updateTTL
, inContext
->updateTTL
== 0, " (system will use a default value.)" );
3981 FPrintF( stdout
, " RData: %#H\n", inContext
->updateDataPtr
, (int) inContext
->updateDataLen
, INT_MAX
);
3983 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3984 FPrintF( stdout
, "---\n" );
3987 //===========================================================================================================================
3988 // RegisterRecordContextFree
3989 //===========================================================================================================================
3991 static void RegisterRecordContextFree( RegisterRecordContext
*inContext
)
3993 DNSServiceForget( &inContext
->conRef
);
3994 ForgetMem( &inContext
->dataPtr
);
3995 ForgetMem( &inContext
->updateDataPtr
);
3999 //===========================================================================================================================
4000 // RegisterRecordCallback
4001 //===========================================================================================================================
4004 RegisterRecordCallback(
4005 DNSServiceRef inSDRef
,
4006 DNSRecordRef inRecordRef
,
4007 DNSServiceFlags inFlags
,
4008 DNSServiceErrorType inError
,
4011 RegisterRecordContext
* context
= (RegisterRecordContext
*) inContext
;
4015 Unused( inRecordRef
);
4019 gettimeofday( &now
, NULL
);
4020 FPrintF( stdout
, "%{du:time} Record registration result (error %#m)\n", &now
, inError
);
4022 if( !context
->didRegister
&& !inError
)
4024 context
->didRegister
= true;
4025 if( context
->updateDataPtr
)
4027 if( context
->updateDelayMs
> 0 )
4029 dispatch_after_f( dispatch_time_milliseconds( context
->updateDelayMs
), dispatch_get_main_queue(),
4030 context
, RegisterRecordUpdate
);
4034 RegisterRecordUpdate( context
);
4037 if( context
->lifetimeMs
== 0 )
4039 Exit( kExitReason_TimeLimit
);
4041 else if( context
->lifetimeMs
> 0 )
4043 dispatch_after_f( dispatch_time_milliseconds( context
->lifetimeMs
), dispatch_get_main_queue(),
4044 kExitReason_TimeLimit
, Exit
);
4049 //===========================================================================================================================
4050 // RegisterRecordUpdate
4051 //===========================================================================================================================
4053 static void RegisterRecordUpdate( void *inContext
)
4056 RegisterRecordContext
* const context
= (RegisterRecordContext
*) inContext
;
4058 err
= DNSServiceUpdateRecord( context
->conRef
, context
->recordRef
, 0, (uint16_t) context
->updateDataLen
,
4059 context
->updateDataPtr
, context
->updateTTL
);
4060 require_noerr( err
, exit
);
4063 if( err
) exit( 1 );
4066 //===========================================================================================================================
4068 //===========================================================================================================================
4072 DNSServiceRef mainRef
; // Main sdRef for shared connections.
4073 DNSServiceRef opRef
; // sdRef for the DNSServiceResolve operation.
4074 DNSServiceFlags flags
; // Flags argument for DNSServiceResolve().
4075 const char * name
; // Service name argument for DNSServiceResolve().
4076 const char * type
; // Service type argument for DNSServiceResolve().
4077 const char * domain
; // Domain argument for DNSServiceResolve().
4078 uint32_t ifIndex
; // Interface index argument for DNSServiceResolve().
4079 int timeLimitSecs
; // Time limit for the DNSServiceResolve operation in seconds.
4083 static void ResolvePrintPrologue( const ResolveContext
*inContext
);
4084 static void ResolveContextFree( ResolveContext
*inContext
);
4085 static void DNSSD_API
4087 DNSServiceRef inSDRef
,
4088 DNSServiceFlags inFlags
,
4089 uint32_t inInterfaceIndex
,
4090 DNSServiceErrorType inError
,
4091 const char * inFullName
,
4092 const char * inHostname
,
4095 const unsigned char * inTXTPtr
,
4098 static void ResolveCmd( void )
4101 DNSServiceRef sdRef
;
4102 ResolveContext
* context
= NULL
;
4103 dispatch_source_t signalSource
= NULL
;
4104 int useMainConnection
;
4106 // Set up SIGINT handler.
4108 signal( SIGINT
, SIG_IGN
);
4109 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
4110 require_noerr( err
, exit
);
4111 dispatch_resume( signalSource
);
4115 context
= (ResolveContext
*) calloc( 1, sizeof( *context
) );
4116 require_action( context
, exit
, err
= kNoMemoryErr
);
4118 // Check command parameters.
4120 if( gResolve_TimeLimitSecs
< 0 )
4122 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs
);
4127 // Create main connection.
4129 if( gConnectionOpt
)
4131 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
4132 require_noerr_quiet( err
, exit
);
4133 useMainConnection
= true;
4137 useMainConnection
= false;
4142 context
->flags
= GetDNSSDFlagsFromOpts();
4143 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
4145 // Get interface index.
4147 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
4148 require_noerr_quiet( err
, exit
);
4150 // Set remaining parameters.
4152 context
->name
= gResolve_Name
;
4153 context
->type
= gResolve_Type
;
4154 context
->domain
= gResolve_Domain
;
4155 context
->timeLimitSecs
= gResolve_TimeLimitSecs
;
4159 ResolvePrintPrologue( context
);
4163 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
4164 err
= DNSServiceResolve( &sdRef
, context
->flags
, context
->ifIndex
, context
->name
, context
->type
, context
->domain
,
4165 ResolveCallback
, NULL
);
4166 require_noerr( err
, exit
);
4168 context
->opRef
= sdRef
;
4169 if( !useMainConnection
)
4171 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
4172 require_noerr( err
, exit
);
4177 if( context
->timeLimitSecs
> 0 )
4179 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
4180 kExitReason_TimeLimit
, Exit
);
4185 dispatch_source_forget( &signalSource
);
4186 if( context
) ResolveContextFree( context
);
4187 if( err
) exit( 1 );
4190 //===========================================================================================================================
4192 //===========================================================================================================================
4194 static void ReconfirmCmd( void )
4197 uint8_t * rdataPtr
= NULL
;
4198 size_t rdataLen
= 0;
4199 DNSServiceFlags flags
;
4201 uint16_t type
, class;
4202 char ifName
[ kInterfaceNameBufLen
];
4206 flags
= GetDNSSDFlagsFromOpts();
4208 // Get interface index.
4210 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
4211 require_noerr_quiet( err
, exit
);
4215 err
= RecordTypeFromArgString( gReconfirmRecord_Type
, &type
);
4216 require_noerr( err
, exit
);
4220 if( gReconfirmRecord_Data
)
4222 err
= RecordDataFromArgString( gReconfirmRecord_Data
, &rdataPtr
, &rdataLen
);
4223 require_noerr_quiet( err
, exit
);
4226 // Get record class.
4228 if( gReconfirmRecord_Class
)
4230 err
= RecordClassFromArgString( gReconfirmRecord_Class
, &class );
4231 require_noerr( err
, exit
);
4235 class = kDNSServiceClass_IN
;
4240 FPrintF( stdout
, "Flags: %#{flags}\n", flags
, kDNSServiceFlagsDescriptors
);
4241 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
4242 FPrintF( stdout
, "Name: %s\n", gReconfirmRecord_Name
);
4243 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
4244 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
4245 FPrintF( stdout
, "Data: %#H\n", rdataPtr
, (int) rdataLen
, INT_MAX
);
4246 FPrintF( stdout
, "---\n" );
4248 err
= DNSServiceReconfirmRecord( flags
, ifIndex
, gReconfirmRecord_Name
, type
, class, (uint16_t) rdataLen
, rdataPtr
);
4249 FPrintF( stdout
, "Error: %#m\n", err
);
4252 FreeNullSafe( rdataPtr
);
4253 if( err
) exit( 1 );
4256 //===========================================================================================================================
4257 // ResolvePrintPrologue
4258 //===========================================================================================================================
4260 static void ResolvePrintPrologue( const ResolveContext
*inContext
)
4262 const int timeLimitSecs
= inContext
->timeLimitSecs
;
4263 char ifName
[ kInterfaceNameBufLen
];
4265 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
4267 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
4268 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
4269 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
4270 FPrintF( stdout
, "Type: %s\n", inContext
->type
);
4271 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
);
4272 FPrintF( stdout
, "Time limit: " );
4273 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
4274 else FPrintF( stdout
, "∞\n" );
4275 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4276 FPrintF( stdout
, "---\n" );
4279 //===========================================================================================================================
4280 // ResolveContextFree
4281 //===========================================================================================================================
4283 static void ResolveContextFree( ResolveContext
*inContext
)
4285 DNSServiceForget( &inContext
->opRef
);
4286 DNSServiceForget( &inContext
->mainRef
);
4290 //===========================================================================================================================
4292 //===========================================================================================================================
4294 static void DNSSD_API
4296 DNSServiceRef inSDRef
,
4297 DNSServiceFlags inFlags
,
4298 uint32_t inInterfaceIndex
,
4299 DNSServiceErrorType inError
,
4300 const char * inFullName
,
4301 const char * inHostname
,
4304 const unsigned char * inTXTPtr
,
4308 char errorStr
[ 64 ];
4312 Unused( inContext
);
4314 gettimeofday( &now
, NULL
);
4316 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " error %#m", inError
);
4318 FPrintF( stdout
, "%{du:time}: %s can be reached at %s:%u (interface %d)%?s\n",
4319 &now
, inFullName
, inHostname
, ntohs( inPort
), (int32_t) inInterfaceIndex
, inError
, errorStr
);
4322 FPrintF( stdout
, " TXT record: %#H\n", inTXTPtr
, (int) inTXTLen
, INT_MAX
);
4326 FPrintF( stdout
, " TXT record: %#{txt}\n", inTXTPtr
, (size_t) inTXTLen
);
4330 //===========================================================================================================================
4331 // GetAddrInfoPOSIXCmd
4332 //===========================================================================================================================
4334 #define AddressFamilyStr( X ) ( \
4335 ( (X) == AF_INET ) ? "inet" : \
4336 ( (X) == AF_INET6 ) ? "inet6" : \
4337 ( (X) == AF_UNSPEC ) ? "unspec" : \
4347 #define CaseFlagStringify( X ) { (X), # X }
4349 const FlagStringPair kGAIPOSIXFlagStringPairs
[] =
4351 #if( defined( AI_UNUSABLE ) )
4352 CaseFlagStringify( AI_UNUSABLE
),
4354 CaseFlagStringify( AI_NUMERICSERV
),
4355 CaseFlagStringify( AI_V4MAPPED
),
4356 CaseFlagStringify( AI_ADDRCONFIG
),
4357 #if( defined( AI_V4MAPPED_CFG ) )
4358 CaseFlagStringify( AI_V4MAPPED_CFG
),
4360 CaseFlagStringify( AI_ALL
),
4361 CaseFlagStringify( AI_NUMERICHOST
),
4362 CaseFlagStringify( AI_CANONNAME
),
4363 CaseFlagStringify( AI_PASSIVE
),
4367 static void GetAddrInfoPOSIXCmd( void )
4370 struct addrinfo hints
;
4372 const struct addrinfo
* addrInfo
;
4373 struct addrinfo
* addrInfoList
= NULL
;
4374 const FlagStringPair
* pair
;
4376 memset( &hints
, 0, sizeof( hints
) );
4377 hints
.ai_socktype
= SOCK_STREAM
;
4379 // Set hints address family.
4381 if( !gGAIPOSIX_Family
) hints
.ai_family
= AF_UNSPEC
;
4382 else if( strcasecmp( gGAIPOSIX_Family
, "inet" ) == 0 ) hints
.ai_family
= AF_INET
;
4383 else if( strcasecmp( gGAIPOSIX_Family
, "inet6" ) == 0 ) hints
.ai_family
= AF_INET6
;
4384 else if( strcasecmp( gGAIPOSIX_Family
, "unspec" ) == 0 ) hints
.ai_family
= AF_UNSPEC
;
4387 FPrintF( stderr
, "Invalid address family: %s.\n", gGAIPOSIX_Family
);
4394 if( gGAIPOSIXFlag_AddrConfig
) hints
.ai_flags
|= AI_ADDRCONFIG
;
4395 if( gGAIPOSIXFlag_All
) hints
.ai_flags
|= AI_ALL
;
4396 if( gGAIPOSIXFlag_CanonName
) hints
.ai_flags
|= AI_CANONNAME
;
4397 if( gGAIPOSIXFlag_NumericHost
) hints
.ai_flags
|= AI_NUMERICHOST
;
4398 if( gGAIPOSIXFlag_NumericServ
) hints
.ai_flags
|= AI_NUMERICSERV
;
4399 if( gGAIPOSIXFlag_Passive
) hints
.ai_flags
|= AI_PASSIVE
;
4400 if( gGAIPOSIXFlag_V4Mapped
) hints
.ai_flags
|= AI_V4MAPPED
;
4401 #if( defined( AI_V4MAPPED_CFG ) )
4402 if( gGAIPOSIXFlag_V4MappedCFG
) hints
.ai_flags
|= AI_V4MAPPED_CFG
;
4404 #if( defined( AI_DEFAULT ) )
4405 if( gGAIPOSIXFlag_Default
) hints
.ai_flags
|= AI_DEFAULT
;
4407 #if( defined( AI_UNUSABLE ) )
4408 if( gGAIPOSIXFlag_Unusable
) hints
.ai_flags
|= AI_UNUSABLE
;
4413 FPrintF( stdout
, "Hostname: %s\n", gGAIPOSIX_HostName
);
4414 FPrintF( stdout
, "Servname: %s\n", gGAIPOSIX_ServName
);
4415 FPrintF( stdout
, "Address family: %s\n", AddressFamilyStr( hints
.ai_family
) );
4416 FPrintF( stdout
, "Flags: 0x%X < ", hints
.ai_flags
);
4417 for( pair
= kGAIPOSIXFlagStringPairs
; pair
->str
!= NULL
; ++pair
)
4419 if( ( (unsigned int) hints
.ai_flags
) & pair
->flag
) FPrintF( stdout
, "%s ", pair
->str
);
4421 FPrintF( stdout
, ">\n" );
4422 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4423 FPrintF( stdout
, "---\n" );
4425 // Call getaddrinfo().
4427 err
= getaddrinfo( gGAIPOSIX_HostName
, gGAIPOSIX_ServName
, &hints
, &addrInfoList
);
4428 gettimeofday( &now
, NULL
);
4431 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
4437 for( addrInfo
= addrInfoList
; addrInfo
; addrInfo
= addrInfo
->ai_next
) { ++addrCount
; }
4439 FPrintF( stdout
, "Addresses (%d total):\n", addrCount
);
4440 for( addrInfo
= addrInfoList
; addrInfo
; addrInfo
= addrInfo
->ai_next
)
4442 FPrintF( stdout
, "%##a\n", addrInfo
->ai_addr
);
4445 FPrintF( stdout
, "---\n" );
4446 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
4449 if( addrInfoList
) freeaddrinfo( addrInfoList
);
4450 if( err
) exit( 1 );
4453 //===========================================================================================================================
4455 //===========================================================================================================================
4457 #define kIP6ARPADomainStr "ip6.arpa."
4459 static void ReverseLookupCmd( void )
4462 QueryRecordContext
* context
= NULL
;
4463 DNSServiceRef sdRef
;
4464 dispatch_source_t signalSource
= NULL
;
4466 uint8_t ipv6Addr
[ 16 ];
4467 char recordName
[ ( 16 * 4 ) + sizeof( kIP6ARPADomainStr
) ];
4468 int useMainConnection
;
4469 const char * endPtr
;
4471 // Set up SIGINT handler.
4473 signal( SIGINT
, SIG_IGN
);
4474 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
4475 require_noerr( err
, exit
);
4476 dispatch_resume( signalSource
);
4480 context
= (QueryRecordContext
*) calloc( 1, sizeof( *context
) );
4481 require_action( context
, exit
, err
= kNoMemoryErr
);
4483 // Check command parameters.
4485 if( gReverseLookup_TimeLimitSecs
< 0 )
4487 FPrintF( stderr
, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs
);
4492 // Create main connection.
4494 if( gConnectionOpt
)
4496 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
4497 require_noerr_quiet( err
, exit
);
4498 useMainConnection
= true;
4502 useMainConnection
= false;
4507 context
->flags
= GetDNSSDFlagsFromOpts();
4508 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
4510 // Get interface index.
4512 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
4513 require_noerr_quiet( err
, exit
);
4515 // Create reverse lookup record name.
4517 err
= _StringToIPv4Address( gReverseLookup_IPAddr
, kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
,
4518 &ipv4Addr
, NULL
, NULL
, NULL
, &endPtr
);
4519 if( err
|| ( *endPtr
!= '\0' ) )
4524 err
= _StringToIPv6Address( gReverseLookup_IPAddr
,
4525 kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
| kStringToIPAddressFlagsNoScope
,
4526 ipv6Addr
, NULL
, NULL
, NULL
, &endPtr
);
4527 if( err
|| ( *endPtr
!= '\0' ) )
4529 FPrintF( stderr
, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr
);
4534 for( i
= 15; i
>= 0; --i
)
4536 *dst
++ = kHexDigitsLowercase
[ ipv6Addr
[ i
] & 0x0F ];
4538 *dst
++ = kHexDigitsLowercase
[ ipv6Addr
[ i
] >> 4 ];
4541 strcpy_literal( dst
, kIP6ARPADomainStr
);
4542 check( ( strlen( recordName
) + 1 ) <= sizeof( recordName
) );
4546 SNPrintF( recordName
, sizeof( recordName
), "%u.%u.%u.%u.in-addr.arpa.",
4548 ( ipv4Addr
>> 8 ) & 0xFF,
4549 ( ipv4Addr
>> 16 ) & 0xFF,
4550 ( ipv4Addr
>> 24 ) & 0xFF );
4553 // Set remaining parameters.
4555 context
->recordName
= recordName
;
4556 context
->recordType
= kDNSServiceType_PTR
;
4557 context
->timeLimitSecs
= gReverseLookup_TimeLimitSecs
;
4558 context
->oneShotMode
= gReverseLookup_OneShot
? true : false;
4562 QueryRecordPrintPrologue( context
);
4566 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
4567 err
= DNSServiceQueryRecord( &sdRef
, context
->flags
, context
->ifIndex
, context
->recordName
, context
->recordType
,
4568 kDNSServiceClass_IN
, QueryRecordCallback
, context
);
4569 require_noerr( err
, exit
);
4571 context
->opRef
= sdRef
;
4572 if( !useMainConnection
)
4574 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
4575 require_noerr( err
, exit
);
4580 if( context
->timeLimitSecs
> 0 )
4582 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
4583 kExitReason_TimeLimit
, Exit
);
4588 dispatch_source_forget( &signalSource
);
4589 if( context
) QueryRecordContextFree( context
);
4590 if( err
) exit( 1 );
4593 //===========================================================================================================================
4595 //===========================================================================================================================
4599 DNSServiceRef mainRef
; // Main sdRef for shared connection.
4600 DNSServiceRef opRef
; // sdRef for the DNSServiceNATPortMappingCreate operation.
4601 DNSServiceFlags flags
; // Flags for DNSServiceNATPortMappingCreate operation.
4602 uint32_t ifIndex
; // Interface index argument for DNSServiceNATPortMappingCreate operation.
4603 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceNATPortMappingCreate operation.
4604 uint32_t ttl
; // TTL argument for DNSServiceNATPortMappingCreate operation.
4605 uint16_t internalPort
; // Internal port argument for DNSServiceNATPortMappingCreate operation.
4606 uint16_t externalPort
; // External port argument for DNSServiceNATPortMappingCreate operation.
4607 Boolean printedHeader
; // True if results header was printed.
4609 } PortMappingContext
;
4611 static void PortMappingPrintPrologue( const PortMappingContext
*inContext
);
4612 static void PortMappingContextFree( PortMappingContext
*inContext
);
4613 static void DNSSD_API
4614 PortMappingCallback(
4615 DNSServiceRef inSDRef
,
4616 DNSServiceFlags inFlags
,
4617 uint32_t inInterfaceIndex
,
4618 DNSServiceErrorType inError
,
4619 uint32_t inExternalIPv4Address
,
4620 DNSServiceProtocol inProtocol
,
4621 uint16_t inInternalPort
,
4622 uint16_t inExternalPort
,
4626 static void PortMappingCmd( void )
4629 PortMappingContext
* context
= NULL
;
4630 DNSServiceRef sdRef
;
4631 dispatch_source_t signalSource
= NULL
;
4632 int useMainConnection
;
4634 // Set up SIGINT handler.
4636 signal( SIGINT
, SIG_IGN
);
4637 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
4638 require_noerr( err
, exit
);
4639 dispatch_resume( signalSource
);
4643 context
= (PortMappingContext
*) calloc( 1, sizeof( *context
) );
4644 require_action( context
, exit
, err
= kNoMemoryErr
);
4646 // Check command parameters.
4648 if( ( gPortMapping_InternalPort
< 0 ) || ( gPortMapping_InternalPort
> UINT16_MAX
) )
4650 FPrintF( stderr
, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort
);
4655 if( ( gPortMapping_ExternalPort
< 0 ) || ( gPortMapping_ExternalPort
> UINT16_MAX
) )
4657 FPrintF( stderr
, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort
);
4662 // Create main connection.
4664 if( gConnectionOpt
)
4666 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
4667 require_noerr_quiet( err
, exit
);
4668 useMainConnection
= true;
4672 useMainConnection
= false;
4677 context
->flags
= GetDNSSDFlagsFromOpts();
4678 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
4680 // Get interface index.
4682 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
4683 require_noerr_quiet( err
, exit
);
4685 // Set remaining parameters.
4687 if( gPortMapping_ProtocolTCP
) context
->protocols
|= kDNSServiceProtocol_TCP
;
4688 if( gPortMapping_ProtocolUDP
) context
->protocols
|= kDNSServiceProtocol_UDP
;
4689 context
->ttl
= (uint32_t) gPortMapping_TTL
;
4690 context
->internalPort
= (uint16_t) gPortMapping_InternalPort
;
4691 context
->externalPort
= (uint16_t) gPortMapping_ExternalPort
;
4695 PortMappingPrintPrologue( context
);
4699 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
4700 err
= DNSServiceNATPortMappingCreate( &sdRef
, context
->flags
, context
->ifIndex
, context
->protocols
,
4701 htons( context
->internalPort
), htons( context
->externalPort
), context
->ttl
, PortMappingCallback
, context
);
4702 require_noerr( err
, exit
);
4704 context
->opRef
= sdRef
;
4705 if( !useMainConnection
)
4707 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
4708 require_noerr( err
, exit
);
4714 dispatch_source_forget( &signalSource
);
4715 if( context
) PortMappingContextFree( context
);
4716 if( err
) exit( 1 );
4719 //===========================================================================================================================
4720 // PortMappingPrintPrologue
4721 //===========================================================================================================================
4723 static void PortMappingPrintPrologue( const PortMappingContext
*inContext
)
4725 char ifName
[ kInterfaceNameBufLen
];
4727 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
4729 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
4730 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
4731 FPrintF( stdout
, "Protocols: %#{flags}\n", inContext
->protocols
, kDNSServiceProtocolDescriptors
);
4732 FPrintF( stdout
, "Internal Port: %u\n", inContext
->internalPort
);
4733 FPrintF( stdout
, "External Port: %u\n", inContext
->externalPort
);
4734 FPrintF( stdout
, "TTL: %u%?s\n", inContext
->ttl
, !inContext
->ttl
,
4735 " (system will use a default value.)" );
4736 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4737 FPrintF( stdout
, "---\n" );
4741 //===========================================================================================================================
4742 // PortMappingContextFree
4743 //===========================================================================================================================
4745 static void PortMappingContextFree( PortMappingContext
*inContext
)
4747 DNSServiceForget( &inContext
->opRef
);
4748 DNSServiceForget( &inContext
->mainRef
);
4752 //===========================================================================================================================
4753 // PortMappingCallback
4754 //===========================================================================================================================
4756 static void DNSSD_API
4757 PortMappingCallback(
4758 DNSServiceRef inSDRef
,
4759 DNSServiceFlags inFlags
,
4760 uint32_t inInterfaceIndex
,
4761 DNSServiceErrorType inError
,
4762 uint32_t inExternalIPv4Address
,
4763 DNSServiceProtocol inProtocol
,
4764 uint16_t inInternalPort
,
4765 uint16_t inExternalPort
,
4769 PortMappingContext
* const context
= (PortMappingContext
*) inContext
;
4771 char errorStr
[ 128 ];
4776 gettimeofday( &now
, NULL
);
4778 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " (error: %#m)", inError
);
4779 if( !context
->printedHeader
)
4781 FPrintF( stdout
, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" );
4782 context
->printedHeader
= true;
4784 FPrintF( stdout
, "%{du:time} %2u %7u %15.4a %7u %6u %#{flags}%?s\n",
4785 &now
, inInterfaceIndex
, ntohs( inInternalPort
), &inExternalIPv4Address
, ntohs( inExternalPort
), inTTL
,
4786 inProtocol
, kDNSServiceProtocolDescriptors
, inError
, errorStr
);
4789 //===========================================================================================================================
4791 //===========================================================================================================================
4793 typedef struct BrowseAllConnection BrowseAllConnection
;
4797 ServiceBrowserRef browser
; // Service browser.
4798 ServiceBrowserResults
* results
; // Results from the service browser.
4799 BrowseAllConnection
* connectionList
; // List of connections.
4800 dispatch_source_t connectionTimer
; // Timer for connection timeout.
4801 int connectionPendingCount
; // Number of pending connections.
4802 int connectionTimeoutSecs
; // Timeout value for connections in seconds.
4806 struct BrowseAllConnection
4808 BrowseAllConnection
* next
; // Next connection object in list.
4809 sockaddr_ip sip
; // IPv4 or IPv6 address to connect to.
4810 uint16_t port
; // TCP port to connect to.
4811 AsyncConnectionRef asyncCnx
; // AsyncConnection object to handle the actual connection.
4812 OSStatus status
; // Status of connection. NoErr means connection succeeded.
4813 CFTimeInterval connectTimeSecs
; // Time it took to connect in seconds.
4814 int32_t refCount
; // This object's reference count.
4815 BrowseAllContext
* context
; // Back pointer to parent context.
4818 static void _BrowseAllContextFree( BrowseAllContext
*inContext
);
4819 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
);
4821 _BrowseAllConnectionCreate(
4822 const struct sockaddr
* inSockAddr
,
4824 BrowseAllContext
* inContext
,
4825 BrowseAllConnection
** outConnection
);
4826 static void _BrowseAllConnectionRetain( BrowseAllConnection
*inConnection
);
4827 static void _BrowseAllConnectionRelease( BrowseAllConnection
*inConnection
);
4828 static void _BrowseAllConnectionProgress( int inPhase
, const void *inDetails
, void *inArg
);
4829 static void _BrowseAllConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
);
4830 static void _BrowseAllExit( void *inContext
);
4832 static Boolean
_IsServiceTypeTCP( const char *inServiceType
);
4834 static void BrowseAllCmd( void )
4837 BrowseAllContext
* context
= NULL
;
4840 char ifName
[ kInterfaceNameBufLen
];
4842 // Check parameters.
4844 if( gBrowseAll_BrowseTimeSecs
<= 0 )
4846 FPrintF( stdout
, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs
);
4851 context
= (BrowseAllContext
*) calloc( 1, sizeof( *context
) );
4852 require_action( context
, exit
, err
= kNoMemoryErr
);
4854 context
->connectionTimeoutSecs
= gBrowseAll_ConnectTimeout
;
4855 #if( TARGET_OS_POSIX )
4856 // Increase the open file descriptor limit for connection sockets.
4858 if( context
->connectionTimeoutSecs
> 0 )
4860 struct rlimit fdLimits
;
4862 err
= getrlimit( RLIMIT_NOFILE
, &fdLimits
);
4863 err
= map_global_noerr_errno( err
);
4864 require_noerr( err
, exit
);
4866 if( fdLimits
.rlim_cur
< 4096 )
4868 fdLimits
.rlim_cur
= 4096;
4869 err
= setrlimit( RLIMIT_NOFILE
, &fdLimits
);
4870 err
= map_global_noerr_errno( err
);
4871 require_noerr( err
, exit
);
4876 // Get interface index.
4878 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
4879 require_noerr_quiet( err
, exit
);
4883 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
4884 FPrintF( stdout
, "Service types: ");
4885 if( gBrowseAll_ServiceTypesCount
> 0 )
4887 FPrintF( stdout
, "%s", gBrowseAll_ServiceTypes
[ 0 ] );
4888 for( i
= 1; i
< gBrowseAll_ServiceTypesCount
; ++i
)
4890 FPrintF( stdout
, ", %s", gBrowseAll_ServiceTypes
[ i
] );
4892 FPrintF( stdout
, "\n" );
4896 FPrintF( stdout
, "all services\n" );
4898 FPrintF( stdout
, "Domain: %s\n", gBrowseAll_Domain
? gBrowseAll_Domain
: "default domains" );
4899 FPrintF( stdout
, "Browse time: %d second%?c\n", gBrowseAll_BrowseTimeSecs
, gBrowseAll_BrowseTimeSecs
!= 1, 's' );
4900 FPrintF( stdout
, "Connect timeout: %d second%?c\n",
4901 context
->connectionTimeoutSecs
, context
->connectionTimeoutSecs
!= 1, 's' );
4902 FPrintF( stdout
, "IncludeAWDL: %s\n", gDNSSDFlag_IncludeAWDL
? "yes" : "no" );
4903 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4904 FPrintF( stdout
, "---\n" );
4906 err
= ServiceBrowserCreate( dispatch_get_main_queue(), ifIndex
, gBrowseAll_Domain
,
4907 (unsigned int) gBrowseAll_BrowseTimeSecs
, gDNSSDFlag_IncludeAWDL
? true : false, &context
->browser
);
4908 require_noerr( err
, exit
);
4910 for( i
= 0; i
< gBrowseAll_ServiceTypesCount
; ++i
)
4912 err
= ServiceBrowserAddServiceType( context
->browser
, gBrowseAll_ServiceTypes
[ i
] );
4913 require_noerr( err
, exit
);
4915 ServiceBrowserSetCallback( context
->browser
, _BrowseAllServiceBrowserCallback
, context
);
4916 ServiceBrowserStart( context
->browser
);
4920 if( context
) _BrowseAllContextFree( context
);
4921 if( err
) exit( 1 );
4924 //===========================================================================================================================
4925 // _BrowseAllContextFree
4926 //===========================================================================================================================
4928 static void _BrowseAllContextFree( BrowseAllContext
*inContext
)
4930 check( !inContext
->browser
);
4931 check( !inContext
->connectionTimer
);
4932 check( !inContext
->connectionList
);
4933 ForgetServiceBrowserResults( &inContext
->results
);
4937 //===========================================================================================================================
4938 // _BrowseAllServiceBrowserCallback
4939 //===========================================================================================================================
4941 #define kDiscardProtocolPort 9
4943 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
)
4946 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
4948 SBRServiceType
* type
;
4949 SBRServiceInstance
* instance
;
4950 SBRIPAddress
* ipaddr
;
4954 require_action( inResults
, exit
, err
= kUnexpectedErr
);
4956 check( !context
->results
);
4957 context
->results
= inResults
;
4958 ServiceBrowserResultsRetain( context
->results
);
4960 check( context
->connectionPendingCount
== 0 );
4961 if( context
->connectionTimeoutSecs
> 0 )
4963 BrowseAllConnection
* connection
;
4964 BrowseAllConnection
** connectionPtr
= &context
->connectionList
;
4965 char destination
[ kSockAddrStringMaxSize
];
4967 for( domain
= context
->results
->domainList
; domain
; domain
= domain
->next
)
4969 for( type
= domain
->typeList
; type
; type
= type
->next
)
4971 if( !_IsServiceTypeTCP( type
->name
) ) continue;
4972 for( instance
= type
->instanceList
; instance
; instance
= instance
->next
)
4974 if( instance
->port
== kDiscardProtocolPort
) continue;
4975 for( ipaddr
= instance
->ipaddrList
; ipaddr
; ipaddr
= ipaddr
->next
)
4977 err
= _BrowseAllConnectionCreate( &ipaddr
->sip
.sa
, instance
->port
, context
, &connection
);
4978 require_noerr( err
, exit
);
4980 *connectionPtr
= connection
;
4981 connectionPtr
= &connection
->next
;
4983 err
= SockAddrToString( &ipaddr
->sip
, kSockAddrStringFlagsNoPort
, destination
);
4987 err
= AsyncConnection_Connect( &connection
->asyncCnx
, destination
, -instance
->port
,
4988 kAsyncConnectionFlag_P2P
, kAsyncConnectionNoTimeout
,
4989 kSocketBufferSize_DontSet
, kSocketBufferSize_DontSet
,
4990 _BrowseAllConnectionProgress
, connection
, _BrowseAllConnectionHandler
, connection
,
4991 dispatch_get_main_queue() );
4996 _BrowseAllConnectionRetain( connection
);
4997 connection
->status
= kInProgressErr
;
4998 ++context
->connectionPendingCount
;
5002 connection
->status
= err
;
5010 if( context
->connectionPendingCount
> 0 )
5012 check( !context
->connectionTimer
);
5013 err
= DispatchTimerCreate( dispatch_time_seconds( context
->connectionTimeoutSecs
), DISPATCH_TIME_FOREVER
,
5014 100 * kNanosecondsPerMillisecond
, NULL
, _BrowseAllExit
, NULL
, context
, &context
->connectionTimer
);
5015 require_noerr( err
, exit
);
5016 dispatch_resume( context
->connectionTimer
);
5020 dispatch_async_f( dispatch_get_main_queue(), context
, _BrowseAllExit
);
5025 ForgetCF( &context
->browser
);
5026 if( err
) exit( 1 );
5029 //===========================================================================================================================
5030 // _BrowseAllConnectionCreate
5031 //===========================================================================================================================
5034 _BrowseAllConnectionCreate(
5035 const struct sockaddr
* inSockAddr
,
5037 BrowseAllContext
* inContext
,
5038 BrowseAllConnection
** outConnection
)
5041 BrowseAllConnection
* obj
;
5043 obj
= (BrowseAllConnection
*) calloc( 1, sizeof( *obj
) );
5044 require_action( obj
, exit
, err
= kNoMemoryErr
);
5047 SockAddrCopy( inSockAddr
, &obj
->sip
);
5049 obj
->context
= inContext
;
5051 *outConnection
= obj
;
5058 //===========================================================================================================================
5059 // _BrowseAllConnectionRetain
5060 //===========================================================================================================================
5062 static void _BrowseAllConnectionRetain( BrowseAllConnection
*inConnection
)
5064 ++inConnection
->refCount
;
5067 //===========================================================================================================================
5068 // _BrowseAllConnectionRelease
5069 //===========================================================================================================================
5071 static void _BrowseAllConnectionRelease( BrowseAllConnection
*inConnection
)
5073 if( --inConnection
->refCount
== 0 ) free( inConnection
);
5076 //===========================================================================================================================
5077 // _BrowseAllConnectionProgress
5078 //===========================================================================================================================
5080 static void _BrowseAllConnectionProgress( int inPhase
, const void *inDetails
, void *inArg
)
5082 BrowseAllConnection
* const connection
= (BrowseAllConnection
*) inArg
;
5084 if( inPhase
== kAsyncConnectionPhase_Connected
)
5086 const AsyncConnectedInfo
* const info
= (AsyncConnectedInfo
*) inDetails
;
5088 connection
->connectTimeSecs
= info
->connectSecs
;
5092 //===========================================================================================================================
5093 // _BrowseAllConnectionHandler
5094 //===========================================================================================================================
5096 static void _BrowseAllConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
)
5098 BrowseAllConnection
* const connection
= (BrowseAllConnection
*) inArg
;
5099 BrowseAllContext
* const context
= connection
->context
;
5101 connection
->status
= inError
;
5102 ForgetSocket( &inSock
);
5105 check( context
->connectionPendingCount
> 0 );
5106 if( ( --context
->connectionPendingCount
== 0 ) && context
->connectionTimer
)
5108 dispatch_source_forget( &context
->connectionTimer
);
5109 dispatch_async_f( dispatch_get_main_queue(), context
, _BrowseAllExit
);
5112 _BrowseAllConnectionRelease( connection
);
5115 //===========================================================================================================================
5117 //===========================================================================================================================
5119 #define Indent( X ) ( (X) * 4 ), ""
5121 static void _BrowseAllExit( void *inContext
)
5123 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
5125 SBRServiceType
* type
;
5126 SBRServiceInstance
* instance
;
5127 SBRIPAddress
* ipaddr
;
5128 char textBuf
[ 512 ];
5129 #if( TARGET_OS_POSIX )
5130 const Boolean useColor
= isatty( STDOUT_FILENO
) ? true : false;
5133 dispatch_source_forget( &context
->connectionTimer
);
5135 for( domain
= context
->results
->domainList
; domain
; domain
= domain
->next
)
5137 FPrintF( stdout
, "%s\n\n", domain
->name
);
5139 for( type
= domain
->typeList
; type
; type
= type
->next
)
5141 const char * description
;
5142 const Boolean serviceTypeIsTCP
= _IsServiceTypeTCP( type
->name
);
5144 description
= ServiceTypeDescription( type
->name
);
5145 if( description
) FPrintF( stdout
, "%*s" "%s (%s)\n\n", Indent( 1 ), description
, type
->name
);
5146 else FPrintF( stdout
, "%*s" "%s\n\n", Indent( 1 ), type
->name
);
5148 for( instance
= type
->instanceList
; instance
; instance
= instance
->next
)
5150 char * dst
= textBuf
;
5151 char * const lim
= &textBuf
[ countof( textBuf
) ];
5152 char ifname
[ IF_NAMESIZE
+ 1 ];
5154 SNPrintF_Add( &dst
, lim
, "%s via ", instance
->name
);
5155 if( instance
->ifIndex
== 0 )
5157 SNPrintF_Add( &dst
, lim
, "the Internet" );
5159 else if( if_indextoname( instance
->ifIndex
, ifname
) )
5161 NetTransportType netType
;
5163 SocketGetInterfaceInfo( kInvalidSocketRef
, ifname
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &netType
);
5164 SNPrintF_Add( &dst
, lim
, "%s (%s)",
5165 ( netType
== kNetTransportType_Ethernet
) ? "Ethernet" : NetTransportTypeToString( netType
),
5170 SNPrintF_Add( &dst
, lim
, "interface index %u", instance
->ifIndex
);
5172 FPrintF( stdout
, "%*s" "%-55s %4llu.%03llu ms\n\n",
5173 Indent( 2 ), textBuf
, instance
->discoverTimeUs
/ 1000, instance
->discoverTimeUs
% 1000 );
5175 if( instance
->hostname
)
5177 SNPrintF( textBuf
, sizeof( textBuf
), "%s:%u", instance
->hostname
, instance
->port
);
5178 FPrintF( stdout
, "%*s" "%-51s %4llu.%03llu ms\n",
5179 Indent( 3 ), textBuf
, instance
->resolveTimeUs
/ 1000, instance
->resolveTimeUs
% 1000 );
5183 FPrintF( stdout
, "%*s" "%s:%u\n", Indent( 3 ), instance
->hostname
, instance
->port
);
5186 for( ipaddr
= instance
->ipaddrList
; ipaddr
; ipaddr
= ipaddr
->next
)
5188 BrowseAllConnection
* conn
;
5189 BrowseAllConnection
** connPtr
;
5191 FPrintF( stdout
, "%*s" "%-##47a %4llu.%03llu ms",
5192 Indent( 4 ), &ipaddr
->sip
.sa
, ipaddr
->resolveTimeUs
/ 1000, ipaddr
->resolveTimeUs
% 1000 );
5195 if( serviceTypeIsTCP
&& ( instance
->port
!= kDiscardProtocolPort
) )
5197 for( connPtr
= &context
->connectionList
; ( conn
= *connPtr
) != NULL
; connPtr
= &conn
->next
)
5199 if( ( conn
->port
== instance
->port
) &&
5200 ( SockAddrCompareAddr( &conn
->sip
, &ipaddr
->sip
) == 0 ) ) break;
5204 if( conn
->status
== kInProgressErr
) conn
->status
= kTimeoutErr
;
5205 *connPtr
= conn
->next
;
5206 conn
->context
= NULL
;
5207 AsyncConnection_Forget( &conn
->asyncCnx
);
5213 if( conn
->status
== kNoErr
)
5215 FPrintF( stdout
, " (%sconnected%s in %.3f ms)\n",
5216 useColor
? kANSIGreen
: "", useColor
? kANSINormal
: "", conn
->connectTimeSecs
* 1000 );
5220 FPrintF( stdout
, " (%scould not connect%s: %m)\n",
5221 useColor
? kANSIRed
: "", useColor
? kANSINormal
: "", conn
->status
);
5223 _BrowseAllConnectionRelease( conn
);
5227 FPrintF( stdout
, " (no connection attempted)\n" );
5231 FPrintF( stdout
, "\n" );
5232 if( instance
->txtLen
== 0 ) continue;
5234 FPrintF( stdout
, "%*s" "TXT record (%zu byte%?c):\n",
5235 Indent( 3 ), instance
->txtLen
, instance
->txtLen
!= 1, 's' );
5236 if( instance
->txtLen
> 1 )
5238 FPrintF( stdout
, "%3{txt}", instance
->txtPtr
, instance
->txtLen
);
5242 FPrintF( stdout
, "%*s" "%#H\n", Indent( 3 ), instance
->txtPtr
, (int) instance
->txtLen
, INT_MAX
);
5244 FPrintF( stdout
, "\n" );
5246 FPrintF( stdout
, "\n" );
5250 _BrowseAllContextFree( context
);
5254 //===========================================================================================================================
5255 // _IsServiceTypeTCP
5256 //===========================================================================================================================
5258 static Boolean
_IsServiceTypeTCP( const char *inServiceType
)
5261 const uint8_t * secondLabel
;
5262 uint8_t name
[ kDomainNameLengthMax
];
5264 err
= DomainNameFromString( name
, inServiceType
, NULL
);
5267 secondLabel
= DomainNameGetNextLabel( name
);
5268 if( secondLabel
&& DomainNameEqual( secondLabel
, (const uint8_t *) "\x04" "_tcp" ) ) return( true );
5273 //===========================================================================================================================
5275 //===========================================================================================================================
5277 const FlagStringPair kGetNameInfoFlagStringPairs
[] =
5279 CaseFlagStringify( NI_NUMERICSCOPE
),
5280 CaseFlagStringify( NI_DGRAM
),
5281 CaseFlagStringify( NI_NUMERICSERV
),
5282 CaseFlagStringify( NI_NAMEREQD
),
5283 CaseFlagStringify( NI_NUMERICHOST
),
5284 CaseFlagStringify( NI_NOFQDN
),
5288 static void GetNameInfoCmd( void )
5294 const FlagStringPair
* pair
;
5296 char host
[ NI_MAXHOST
];
5297 char serv
[ NI_MAXSERV
];
5299 err
= StringToSockAddr( gGetNameInfo_IPAddress
, &sip
, sizeof( sip
), &sockAddrLen
);
5303 FPrintF( stderr
, "Failed to convert \"%s\" to a sockaddr.\n", gGetNameInfo_IPAddress
);
5308 if( gGetNameInfoFlag_DGram
) flags
|= NI_DGRAM
;
5309 if( gGetNameInfoFlag_NameReqd
) flags
|= NI_NAMEREQD
;
5310 if( gGetNameInfoFlag_NoFQDN
) flags
|= NI_NOFQDN
;
5311 if( gGetNameInfoFlag_NumericHost
) flags
|= NI_NUMERICHOST
;
5312 if( gGetNameInfoFlag_NumericScope
) flags
|= NI_NUMERICSCOPE
;
5313 if( gGetNameInfoFlag_NumericServ
) flags
|= NI_NUMERICSERV
;
5317 FPrintF( stdout
, "SockAddr: %##a\n", &sip
.sa
);
5318 FPrintF( stdout
, "Flags: 0x%X < ", flags
);
5319 for( pair
= kGetNameInfoFlagStringPairs
; pair
->str
!= NULL
; ++pair
)
5321 if( flags
& pair
->flag
) FPrintF( stdout
, "%s ", pair
->str
);
5323 FPrintF( stdout
, ">\n" );
5324 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5325 FPrintF( stdout
, "---\n" );
5327 // Call getnameinfo().
5329 err
= getnameinfo( &sip
.sa
, (socklen_t
) sockAddrLen
, host
, (socklen_t
) sizeof( host
), serv
, (socklen_t
) sizeof( serv
),
5331 gettimeofday( &now
, NULL
);
5334 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
5338 FPrintF( stdout
, "host: %s\n", host
);
5339 FPrintF( stdout
, "serv: %s\n", serv
);
5341 FPrintF( stdout
, "---\n" );
5342 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
5345 gExitCode
= err
? 1 : 0;
5348 //===========================================================================================================================
5349 // GetAddrInfoStressCmd
5350 //===========================================================================================================================
5354 DNSServiceRef mainRef
;
5355 DNSServiceRef sdRef
;
5356 DNSServiceFlags flags
;
5357 unsigned int interfaceIndex
;
5358 unsigned int connectionNumber
;
5359 unsigned int requestCount
;
5360 unsigned int requestCountMax
;
5361 unsigned int requestCountLimit
;
5362 unsigned int durationMinMs
;
5363 unsigned int durationMaxMs
;
5367 static void GetAddrInfoStressEvent( void *inContext
);
5368 static void DNSSD_API
5369 GetAddrInfoStressCallback(
5370 DNSServiceRef inSDRef
,
5371 DNSServiceFlags inFlags
,
5372 uint32_t inInterfaceIndex
,
5373 DNSServiceErrorType inError
,
5374 const char * inHostname
,
5375 const struct sockaddr
* inSockAddr
,
5379 static void GetAddrInfoStressCmd( void )
5382 GAIStressContext
* context
= NULL
;
5384 DNSServiceFlags flags
;
5386 char ifName
[ kInterfaceNameBufLen
];
5388 if( gGAIStress_TestDurationSecs
< 0 )
5390 FPrintF( stdout
, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs
);
5394 if( gGAIStress_ConnectionCount
<= 0 )
5396 FPrintF( stdout
, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount
);
5400 if( gGAIStress_DurationMinMs
<= 0 )
5402 FPrintF( stdout
, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs
);
5406 if( gGAIStress_DurationMaxMs
<= 0 )
5408 FPrintF( stdout
, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs
);
5412 if( gGAIStress_DurationMinMs
> gGAIStress_DurationMaxMs
)
5414 FPrintF( stdout
, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
5415 gGAIStress_DurationMinMs
, gGAIStress_DurationMaxMs
);
5419 if( gGAIStress_RequestCountMax
<= 0 )
5421 FPrintF( stdout
, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax
);
5428 flags
= GetDNSSDFlagsFromOpts();
5430 // Set interface index.
5432 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
5433 require_noerr_quiet( err
, exit
);
5435 for( i
= 0; i
< gGAIStress_ConnectionCount
; ++i
)
5437 context
= (GAIStressContext
*) calloc( 1, sizeof( *context
) );
5438 require_action( context
, exit
, err
= kNoMemoryErr
);
5440 context
->flags
= flags
;
5441 context
->interfaceIndex
= ifIndex
;
5442 context
->connectionNumber
= (unsigned int)( i
+ 1 );
5443 context
->requestCountMax
= (unsigned int) gGAIStress_RequestCountMax
;
5444 context
->durationMinMs
= (unsigned int) gGAIStress_DurationMinMs
;
5445 context
->durationMaxMs
= (unsigned int) gGAIStress_DurationMaxMs
;
5447 dispatch_async_f( dispatch_get_main_queue(), context
, GetAddrInfoStressEvent
);
5451 if( gGAIStress_TestDurationSecs
> 0 )
5453 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs
), dispatch_get_main_queue(), NULL
, Exit
);
5456 FPrintF( stdout
, "Flags: %#{flags}\n", flags
, kDNSServiceFlagsDescriptors
);
5457 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
5458 FPrintF( stdout
, "Test duration: " );
5459 if( gGAIStress_TestDurationSecs
== 0 )
5461 FPrintF( stdout
, "∞\n" );
5465 FPrintF( stdout
, "%d s\n", gGAIStress_TestDurationSecs
);
5467 FPrintF( stdout
, "Connection count: %d\n", gGAIStress_ConnectionCount
);
5468 FPrintF( stdout
, "Request duration min: %d ms\n", gGAIStress_DurationMinMs
);
5469 FPrintF( stdout
, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs
);
5470 FPrintF( stdout
, "Request count max: %d\n", gGAIStress_RequestCountMax
);
5471 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5472 FPrintF( stdout
, "---\n" );
5477 FreeNullSafe( context
);
5478 if( err
) exit( 1 );
5481 //===========================================================================================================================
5482 // GetAddrInfoStressEvent
5483 //===========================================================================================================================
5485 #define kStressRandStrLen 5
5487 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
5489 static void GetAddrInfoStressEvent( void *inContext
)
5491 GAIStressContext
* const context
= (GAIStressContext
*) inContext
;
5493 DNSServiceRef sdRef
;
5494 unsigned int nextMs
;
5495 char randomStr
[ kStressRandStrLen
+ 1 ];
5496 char hostname
[ kStressRandStrLen
+ 4 + 1 ];
5497 Boolean isConnectionNew
= false;
5498 static Boolean printedHeader
= false;
5500 if( !context
->mainRef
|| ( context
->requestCount
>= context
->requestCountLimit
) )
5502 DNSServiceForget( &context
->mainRef
);
5503 context
->sdRef
= NULL
;
5504 context
->requestCount
= 0;
5505 context
->requestCountLimit
= RandomRange( 1, context
->requestCountMax
);
5507 err
= DNSServiceCreateConnection( &context
->mainRef
);
5508 require_noerr( err
, exit
);
5510 err
= DNSServiceSetDispatchQueue( context
->mainRef
, dispatch_get_main_queue() );
5511 require_noerr( err
, exit
);
5513 isConnectionNew
= true;
5516 RandomString( kLowercaseAlphaCharSet
, sizeof_string( kLowercaseAlphaCharSet
), 2, kStressRandStrLen
, randomStr
);
5517 SNPrintF( hostname
, sizeof( hostname
), "%s.com", randomStr
);
5519 nextMs
= RandomRange( context
->durationMinMs
, context
->durationMaxMs
);
5521 if( !printedHeader
)
5523 FPrintF( stdout
, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
5524 printedHeader
= true;
5526 FPrintF( stdout
, "%{du:time} %3u%c %9s %8u\n",
5527 NULL
, context
->connectionNumber
, isConnectionNew
? '*': ' ', hostname
, nextMs
);
5529 DNSServiceForget( &context
->sdRef
);
5530 sdRef
= context
->mainRef
;
5531 err
= DNSServiceGetAddrInfo( &sdRef
, context
->flags
| kDNSServiceFlagsShareConnection
, context
->interfaceIndex
,
5532 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, hostname
, GetAddrInfoStressCallback
, NULL
);
5533 require_noerr( err
, exit
);
5534 context
->sdRef
= sdRef
;
5536 context
->requestCount
++;
5538 dispatch_after_f( dispatch_time_milliseconds( nextMs
), dispatch_get_main_queue(), context
, GetAddrInfoStressEvent
);
5541 if( err
) exit( 1 );
5544 //===========================================================================================================================
5545 // GetAddrInfoStressCallback
5546 //===========================================================================================================================
5548 static void DNSSD_API
5549 GetAddrInfoStressCallback(
5550 DNSServiceRef inSDRef
,
5551 DNSServiceFlags inFlags
,
5552 uint32_t inInterfaceIndex
,
5553 DNSServiceErrorType inError
,
5554 const char * inHostname
,
5555 const struct sockaddr
* inSockAddr
,
5561 Unused( inInterfaceIndex
);
5563 Unused( inHostname
);
5564 Unused( inSockAddr
);
5566 Unused( inContext
);
5569 //===========================================================================================================================
5571 //===========================================================================================================================
5575 sockaddr_ip serverAddr
;
5581 dispatch_source_t readSource
;
5588 Boolean printRawRData
; // True if RDATA results are not to be formatted.
5589 uint8_t msgBuf
[ 512 ];
5593 static void DNSQueryPrintPrologue( const DNSQueryContext
*inContext
);
5594 static void DNSQueryReadHandler( void *inContext
);
5595 static void DNSQueryCancelHandler( void *inContext
);
5597 static void DNSQueryCmd( void )
5600 DNSQueryContext
* context
= NULL
;
5602 size_t msgLen
, sendLen
;
5604 // Check command parameters.
5606 if( gDNSQuery_TimeLimitSecs
< -1 )
5608 FPrintF( stdout
, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs
);
5612 if( ( gDNSQuery_Flags
< INT16_MIN
) || ( gDNSQuery_Flags
> UINT16_MAX
) )
5614 FPrintF( stdout
, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags
);
5621 context
= (DNSQueryContext
*) calloc( 1, sizeof( *context
) );
5622 require_action( context
, exit
, err
= kNoMemoryErr
);
5624 context
->name
= gDNSQuery_Name
;
5625 context
->sock
= kInvalidSocketRef
;
5626 context
->timeLimitSecs
= gDNSQuery_TimeLimitSecs
;
5627 context
->queryID
= (uint16_t) Random32();
5628 context
->useTCP
= gDNSQuery_UseTCP
? true : false;
5629 context
->printRawRData
= gDNSQuery_RawRData
? true : false;
5631 #if( TARGET_OS_DARWIN )
5632 if( gDNSQuery_Server
)
5635 err
= StringToSockAddr( gDNSQuery_Server
, &context
->serverAddr
, sizeof( context
->serverAddr
), NULL
);
5636 require_noerr( err
, exit
);
5638 #if( TARGET_OS_DARWIN )
5641 err
= GetDefaultDNSServer( &context
->serverAddr
);
5642 require_noerr( err
, exit
);
5645 if( SockAddrGetPort( &context
->serverAddr
) == 0 ) SockAddrSetPort( &context
->serverAddr
, kDNSPort
);
5647 err
= RecordTypeFromArgString( gDNSQuery_Type
, &context
->type
);
5648 require_noerr( err
, exit
);
5650 // Write query message.
5652 check_compile_time_code( sizeof( context
->msgBuf
) >= ( kDNSQueryMessageMaxLen
+ 2 ) );
5654 msgPtr
= context
->useTCP
? &context
->msgBuf
[ 2 ] : context
->msgBuf
;
5655 err
= WriteDNSQueryMessage( msgPtr
, context
->queryID
, (uint16_t) gDNSQuery_Flags
, context
->name
, context
->type
,
5656 kDNSServiceClass_IN
, &msgLen
);
5657 require_noerr( err
, exit
);
5658 check( msgLen
<= UINT16_MAX
);
5660 if( context
->useTCP
)
5662 WriteBig16( context
->msgBuf
, msgLen
);
5663 sendLen
= 2 + msgLen
;
5670 DNSQueryPrintPrologue( context
);
5672 if( gDNSQuery_Verbose
)
5674 FPrintF( stdout
, "DNS message to send:\n\n%{du:dnsmsg}", msgPtr
, msgLen
);
5675 FPrintF( stdout
, "---\n" );
5678 if( context
->useTCP
)
5680 // Create TCP socket.
5682 context
->sock
= socket( context
->serverAddr
.sa
.sa_family
, SOCK_STREAM
, IPPROTO_TCP
);
5683 err
= map_socket_creation_errno( context
->sock
);
5684 require_noerr( err
, exit
);
5686 err
= SocketConnect( context
->sock
, &context
->serverAddr
, 5 );
5687 require_noerr( err
, exit
);
5691 // Create UDP socket.
5693 err
= UDPClientSocketOpen( AF_UNSPEC
, &context
->serverAddr
, 0, -1, NULL
, &context
->sock
);
5694 require_noerr( err
, exit
);
5697 context
->sendTicks
= UpTicks();
5698 err
= _SocketWriteAll( context
->sock
, context
->msgBuf
, sendLen
, 5 );
5699 require_noerr( err
, exit
);
5701 if( context
->timeLimitSecs
== 0 ) goto exit
;
5703 err
= DispatchReadSourceCreate( context
->sock
, NULL
, DNSQueryReadHandler
, DNSQueryCancelHandler
, context
,
5704 &context
->readSource
);
5705 require_noerr( err
, exit
);
5706 dispatch_resume( context
->readSource
);
5708 if( context
->timeLimitSecs
> 0 )
5710 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
5718 dispatch_source_forget( &context
->readSource
);
5719 ForgetSocket( &context
->sock
);
5722 if( err
) exit( 1 );
5725 //===========================================================================================================================
5726 // DNSQueryPrintPrologue
5727 //===========================================================================================================================
5729 static void DNSQueryPrintPrologue( const DNSQueryContext
*inContext
)
5731 const int timeLimitSecs
= inContext
->timeLimitSecs
;
5733 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
5734 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->type
), inContext
->type
);
5735 FPrintF( stdout
, "Server: %##a\n", &inContext
->serverAddr
);
5736 FPrintF( stdout
, "Transport: %s\n", inContext
->useTCP
? "TCP" : "UDP" );
5737 FPrintF( stdout
, "Time limit: " );
5738 if( timeLimitSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
5739 else FPrintF( stdout
, "∞\n" );
5740 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5741 FPrintF( stdout
, "---\n" );
5744 //===========================================================================================================================
5745 // DNSQueryReadHandler
5746 //===========================================================================================================================
5748 static void DNSQueryReadHandler( void *inContext
)
5752 const uint64_t nowTicks
= UpTicks();
5753 DNSQueryContext
* const context
= (DNSQueryContext
*) inContext
;
5755 gettimeofday( &now
, NULL
);
5757 if( context
->useTCP
)
5759 if( !context
->haveTCPLen
)
5761 err
= SocketReadData( context
->sock
, &context
->msgBuf
, 2, &context
->msgOffset
);
5762 if( err
== EWOULDBLOCK
) { err
= kNoErr
; goto exit
; }
5763 require_noerr( err
, exit
);
5765 context
->msgOffset
= 0;
5766 context
->msgLen
= ReadBig16( context
->msgBuf
);
5767 context
->haveTCPLen
= true;
5768 if( context
->msgLen
<= sizeof( context
->msgBuf
) )
5770 context
->msgPtr
= context
->msgBuf
;
5774 context
->msgPtr
= (uint8_t *) malloc( context
->msgLen
);
5775 require_action( context
->msgPtr
, exit
, err
= kNoMemoryErr
);
5779 err
= SocketReadData( context
->sock
, context
->msgPtr
, context
->msgLen
, &context
->msgOffset
);
5780 if( err
== EWOULDBLOCK
) { err
= kNoErr
; goto exit
; }
5781 require_noerr( err
, exit
);
5782 context
->msgOffset
= 0;
5783 context
->haveTCPLen
= false;
5787 sockaddr_ip fromAddr
;
5789 context
->msgPtr
= context
->msgBuf
;
5790 err
= SocketRecvFrom( context
->sock
, context
->msgPtr
, sizeof( context
->msgBuf
), &context
->msgLen
, &fromAddr
,
5791 sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
5792 require_noerr( err
, exit
);
5794 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
5797 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
5798 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
5799 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
5800 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
5801 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, context
->msgPtr
, context
->msgLen
);
5803 if( ( context
->msgLen
>= kDNSHeaderLength
) && ( DNSHeaderGetID( (DNSHeader
*) context
->msgPtr
) == context
->queryID
) )
5805 Exit( kExitReason_ReceivedResponse
);
5809 if( err
) dispatch_source_forget( &context
->readSource
);
5812 //===========================================================================================================================
5813 // DNSQueryCancelHandler
5814 //===========================================================================================================================
5816 static void DNSQueryCancelHandler( void *inContext
)
5818 DNSQueryContext
* const context
= (DNSQueryContext
*) inContext
;
5820 check( !context
->readSource
);
5821 ForgetSocket( &context
->sock
);
5822 if( context
->msgPtr
!= context
->msgBuf
) ForgetMem( &context
->msgPtr
);
5824 dispatch_async_f( dispatch_get_main_queue(), NULL
, Exit
);
5827 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
5828 //===========================================================================================================================
5830 //===========================================================================================================================
5832 #define kDNSCryptPort 443
5834 #define kDNSCryptMinPadLength 8
5835 #define kDNSCryptMaxPadLength 256
5836 #define kDNSCryptBlockSize 64
5837 #define kDNSCryptCertMinimumLength 124
5838 #define kDNSCryptClientMagicLength 8
5839 #define kDNSCryptResolverMagicLength 8
5840 #define kDNSCryptHalfNonceLength 12
5841 #define kDNSCryptCertMagicLength 4
5843 check_compile_time( ( kDNSCryptHalfNonceLength
* 2 ) == crypto_box_NONCEBYTES
);
5845 static const uint8_t kDNSCryptCertMagic
[ kDNSCryptCertMagicLength
] = { 'D', 'N', 'S', 'C' };
5846 static const uint8_t kDNSCryptResolverMagic
[ kDNSCryptResolverMagicLength
] =
5848 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
5853 uint8_t certMagic
[ kDNSCryptCertMagicLength
];
5854 uint8_t esVersion
[ 2 ];
5855 uint8_t minorVersion
[ 2 ];
5856 uint8_t signature
[ crypto_sign_BYTES
];
5857 uint8_t publicKey
[ crypto_box_PUBLICKEYBYTES
];
5858 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5859 uint8_t serial
[ 4 ];
5860 uint8_t startTime
[ 4 ];
5861 uint8_t endTime
[ 4 ];
5862 uint8_t extensions
[ 1 ]; // Variably-sized extension data.
5866 check_compile_time( offsetof( DNSCryptCert
, extensions
) == kDNSCryptCertMinimumLength
);
5870 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5871 uint8_t clientPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5872 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5873 uint8_t poly1305MAC
[ 16 ];
5875 } DNSCryptQueryHeader
;
5877 check_compile_time( sizeof( DNSCryptQueryHeader
) == 68 );
5878 check_compile_time( sizeof( DNSCryptQueryHeader
) >= crypto_box_ZEROBYTES
);
5879 check_compile_time( ( sizeof( DNSCryptQueryHeader
) - crypto_box_ZEROBYTES
+ crypto_box_BOXZEROBYTES
) ==
5880 offsetof( DNSCryptQueryHeader
, poly1305MAC
) );
5884 uint8_t resolverMagic
[ kDNSCryptResolverMagicLength
];
5885 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5886 uint8_t resolverNonce
[ kDNSCryptHalfNonceLength
];
5887 uint8_t poly1305MAC
[ 16 ];
5889 } DNSCryptResponseHeader
;
5891 check_compile_time( sizeof( DNSCryptResponseHeader
) == 48 );
5892 check_compile_time( offsetof( DNSCryptResponseHeader
, poly1305MAC
) >= crypto_box_BOXZEROBYTES
);
5893 check_compile_time( ( offsetof( DNSCryptResponseHeader
, poly1305MAC
) - crypto_box_BOXZEROBYTES
+ crypto_box_ZEROBYTES
) ==
5894 sizeof( DNSCryptResponseHeader
) );
5898 sockaddr_ip serverAddr
;
5900 const char * providerName
;
5902 const uint8_t * certPtr
;
5904 dispatch_source_t readSource
;
5909 Boolean printRawRData
;
5910 uint8_t serverPublicSignKey
[ crypto_sign_PUBLICKEYBYTES
];
5911 uint8_t serverPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5912 uint8_t clientPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5913 uint8_t clientSecretKey
[ crypto_box_SECRETKEYBYTES
];
5914 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5915 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5916 uint8_t nmKey
[ crypto_box_BEFORENMBYTES
];
5917 uint8_t msgBuf
[ 512 ];
5921 static void DNSCryptReceiveCertHandler( void *inContext
);
5922 static void DNSCryptReceiveResponseHandler( void *inContext
);
5923 static void DNSCryptProceed( void *inContext
);
5924 static OSStatus
DNSCryptProcessCert( DNSCryptContext
*inContext
);
5925 static OSStatus
DNSCryptBuildQuery( DNSCryptContext
*inContext
);
5926 static OSStatus
DNSCryptSendQuery( DNSCryptContext
*inContext
);
5927 static void DNSCryptPrintCertificate( const DNSCryptCert
*inCert
, size_t inLen
);
5929 static void DNSCryptCmd( void )
5932 DNSCryptContext
* context
= NULL
;
5933 size_t writtenBytes
;
5935 SocketContext
* sockCtx
;
5936 SocketRef sock
= kInvalidSocketRef
;
5939 // Check command parameters.
5941 if( gDNSCrypt_TimeLimitSecs
< -1 )
5943 FPrintF( stdout
, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs
);
5950 context
= (DNSCryptContext
*) calloc( 1, sizeof( *context
) );
5951 require_action( context
, exit
, err
= kNoMemoryErr
);
5953 context
->providerName
= gDNSCrypt_ProviderName
;
5954 context
->qname
= gDNSCrypt_Name
;
5955 context
->timeLimitSecs
= gDNSCrypt_TimeLimitSecs
;
5956 context
->printRawRData
= gDNSCrypt_RawRData
? true : false;
5958 err
= crypto_box_keypair( context
->clientPublicKey
, context
->clientSecretKey
);
5959 require_noerr( err
, exit
);
5961 err
= HexToData( gDNSCrypt_ProviderKey
, kSizeCString
, kHexToData_DefaultFlags
,
5962 context
->serverPublicSignKey
, sizeof( context
->serverPublicSignKey
), &writtenBytes
, &totalBytes
, &ptr
);
5963 if( err
|| ( *ptr
!= '\0' ) )
5965 FPrintF( stderr
, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey
);
5968 else if( totalBytes
!= sizeof( context
->serverPublicSignKey
) )
5970 FPrintF( stderr
, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
5971 totalBytes
, sizeof( context
->serverPublicSignKey
) );
5975 check( writtenBytes
== totalBytes
);
5977 err
= StringToSockAddr( gDNSCrypt_Server
, &context
->serverAddr
, sizeof( context
->serverAddr
), NULL
);
5978 require_noerr( err
, exit
);
5979 if( SockAddrGetPort( &context
->serverAddr
) == 0 ) SockAddrSetPort( &context
->serverAddr
, kDNSCryptPort
);
5981 err
= RecordTypeFromArgString( gDNSCrypt_Type
, &context
->qtype
);
5982 require_noerr( err
, exit
);
5984 // Write query message.
5986 context
->queryID
= (uint16_t) Random32();
5987 err
= WriteDNSQueryMessage( context
->msgBuf
, context
->queryID
, kDNSHeaderFlag_RecursionDesired
, context
->providerName
,
5988 kDNSServiceType_TXT
, kDNSServiceClass_IN
, &context
->msgLen
);
5989 require_noerr( err
, exit
);
5991 // Create UDP socket.
5993 err
= UDPClientSocketOpen( AF_UNSPEC
, &context
->serverAddr
, 0, -1, NULL
, &sock
);
5994 require_noerr( err
, exit
);
5998 context
->sendTicks
= UpTicks();
5999 err
= _SocketWriteAll( sock
, context
->msgBuf
, context
->msgLen
, 5 );
6000 require_noerr( err
, exit
);
6002 err
= SocketContextCreate( sock
, context
, &sockCtx
);
6003 require_noerr( err
, exit
);
6004 sock
= kInvalidSocketRef
;
6006 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, DNSCryptReceiveCertHandler
, SocketContextCancelHandler
, sockCtx
,
6007 &context
->readSource
);
6008 if( err
) ForgetSocketContext( &sockCtx
);
6009 require_noerr( err
, exit
);
6011 dispatch_resume( context
->readSource
);
6013 if( context
->timeLimitSecs
> 0 )
6015 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
6021 if( context
) free( context
);
6022 ForgetSocket( &sock
);
6023 if( err
) exit( 1 );
6026 //===========================================================================================================================
6027 // DNSCryptReceiveCertHandler
6028 //===========================================================================================================================
6030 static void DNSCryptReceiveCertHandler( void *inContext
)
6034 const uint64_t nowTicks
= UpTicks();
6035 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6036 DNSCryptContext
* const context
= (DNSCryptContext
*) sockCtx
->userContext
;
6037 const DNSHeader
* hdr
;
6038 sockaddr_ip fromAddr
;
6039 const uint8_t * ptr
;
6040 const uint8_t * txtPtr
;
6042 unsigned int answerCount
, i
;
6043 uint8_t targetName
[ kDomainNameLengthMax
];
6045 gettimeofday( &now
, NULL
);
6047 dispatch_source_forget( &context
->readSource
);
6049 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &context
->msgLen
,
6050 &fromAddr
, sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6051 require_noerr( err
, exit
);
6052 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
6054 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6055 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
6056 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
6057 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
6058 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, context
->msgBuf
, context
->msgLen
);
6060 require_action_quiet( context
->msgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
6062 hdr
= (DNSHeader
*) context
->msgBuf
;
6063 require_action_quiet( DNSHeaderGetID( hdr
) == context
->queryID
, exit
, err
= kMismatchErr
);
6065 err
= DNSMessageGetAnswerSection( context
->msgBuf
, context
->msgLen
, &ptr
);
6066 require_noerr( err
, exit
);
6068 err
= DomainNameFromString( targetName
, context
->providerName
, NULL
);
6069 require_noerr( err
, exit
);
6071 answerCount
= DNSHeaderGetAnswerCount( hdr
);
6072 for( i
= 0; i
< answerCount
; ++i
)
6076 uint8_t name
[ kDomainNameLengthMax
];
6078 err
= DNSMessageExtractRecord( context
->msgBuf
, context
->msgLen
, ptr
, name
, &type
, &class, NULL
, &txtPtr
, &txtLen
,
6080 require_noerr( err
, exit
);
6082 if( ( type
== kDNSServiceType_TXT
) && ( class == kDNSServiceClass_IN
) && DomainNameEqual( name
, targetName
) )
6088 if( txtLen
< ( 1 + kDNSCryptCertMinimumLength
) )
6090 FPrintF( stderr
, "TXT record length is too short (%u < %u)\n", txtLen
, kDNSCryptCertMinimumLength
+ 1 );
6094 if( txtPtr
[ 0 ] < kDNSCryptCertMinimumLength
)
6096 FPrintF( stderr
, "TXT record value length is too short (%u < %u)\n", txtPtr
[ 0 ], kDNSCryptCertMinimumLength
);
6101 context
->certLen
= txtPtr
[ 0 ];
6102 context
->certPtr
= &txtPtr
[ 1 ];
6104 dispatch_async_f( dispatch_get_main_queue(), context
, DNSCryptProceed
);
6107 if( err
) Exit( NULL
);
6110 //===========================================================================================================================
6111 // DNSCryptReceiveResponseHandler
6112 //===========================================================================================================================
6114 static void DNSCryptReceiveResponseHandler( void *inContext
)
6118 const uint64_t nowTicks
= UpTicks();
6119 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6120 DNSCryptContext
* const context
= (DNSCryptContext
*) sockCtx
->userContext
;
6121 sockaddr_ip fromAddr
;
6122 DNSCryptResponseHeader
* hdr
;
6123 const uint8_t * end
;
6124 uint8_t * ciphertext
;
6125 uint8_t * plaintext
;
6126 const uint8_t * response
;
6127 uint8_t nonce
[ crypto_box_NONCEBYTES
];
6129 gettimeofday( &now
, NULL
);
6131 dispatch_source_forget( &context
->readSource
);
6133 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &context
->msgLen
,
6134 &fromAddr
, sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6135 require_noerr( err
, exit
);
6136 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
6138 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6139 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
6140 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
6141 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
6143 if( context
->msgLen
< sizeof( DNSCryptResponseHeader
) )
6145 FPrintF( stderr
, "DNSCrypt response is too short.\n" );
6150 hdr
= (DNSCryptResponseHeader
*) context
->msgBuf
;
6152 if( memcmp( hdr
->resolverMagic
, kDNSCryptResolverMagic
, kDNSCryptResolverMagicLength
) != 0 )
6154 FPrintF( stderr
, "DNSCrypt response resolver magic %#H != %#H\n",
6155 hdr
->resolverMagic
, kDNSCryptResolverMagicLength
, INT_MAX
,
6156 kDNSCryptResolverMagic
, kDNSCryptResolverMagicLength
, INT_MAX
);
6161 if( memcmp( hdr
->clientNonce
, context
->clientNonce
, kDNSCryptHalfNonceLength
) != 0 )
6163 FPrintF( stderr
, "DNSCrypt response client nonce mismatch.\n" );
6168 memcpy( nonce
, hdr
->clientNonce
, crypto_box_NONCEBYTES
);
6170 ciphertext
= hdr
->poly1305MAC
- crypto_box_BOXZEROBYTES
;
6171 memset( ciphertext
, 0, crypto_box_BOXZEROBYTES
);
6173 plaintext
= (uint8_t *)( hdr
+ 1 ) - crypto_box_ZEROBYTES
;
6174 check( plaintext
== ciphertext
);
6176 end
= context
->msgBuf
+ context
->msgLen
;
6178 err
= crypto_box_open_afternm( plaintext
, ciphertext
, (size_t)( end
- ciphertext
), nonce
, context
->nmKey
);
6179 require_noerr( err
, exit
);
6181 response
= plaintext
+ crypto_box_ZEROBYTES
;
6182 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, response
, (size_t)( end
- response
) );
6183 Exit( kExitReason_ReceivedResponse
);
6186 if( err
) Exit( NULL
);
6189 //===========================================================================================================================
6191 //===========================================================================================================================
6193 static void DNSCryptProceed( void *inContext
)
6196 DNSCryptContext
* const context
= (DNSCryptContext
*) inContext
;
6198 err
= DNSCryptProcessCert( context
);
6199 require_noerr_quiet( err
, exit
);
6201 err
= DNSCryptBuildQuery( context
);
6202 require_noerr_quiet( err
, exit
);
6204 err
= DNSCryptSendQuery( context
);
6205 require_noerr_quiet( err
, exit
);
6208 if( err
) Exit( NULL
);
6211 //===========================================================================================================================
6212 // DNSCryptProcessCert
6213 //===========================================================================================================================
6215 static OSStatus
DNSCryptProcessCert( DNSCryptContext
*inContext
)
6218 const DNSCryptCert
* const cert
= (DNSCryptCert
*) inContext
->certPtr
;
6219 const uint8_t * const certEnd
= inContext
->certPtr
+ inContext
->certLen
;
6221 time_t startTimeSecs
, endTimeSecs
;
6224 unsigned long long tempLen
;
6226 DNSCryptPrintCertificate( cert
, inContext
->certLen
);
6228 if( memcmp( cert
->certMagic
, kDNSCryptCertMagic
, kDNSCryptCertMagicLength
) != 0 )
6230 FPrintF( stderr
, "DNSCrypt certificate magic %#H != %#H\n",
6231 cert
->certMagic
, kDNSCryptCertMagicLength
, INT_MAX
,
6232 kDNSCryptCertMagic
, kDNSCryptCertMagicLength
, INT_MAX
);
6237 startTimeSecs
= (time_t) ReadBig32( cert
->startTime
);
6238 endTimeSecs
= (time_t) ReadBig32( cert
->endTime
);
6240 gettimeofday( &now
, NULL
);
6241 if( now
.tv_sec
< startTimeSecs
)
6243 FPrintF( stderr
, "DNSCrypt certificate start time is in the future.\n" );
6247 if( now
.tv_sec
>= endTimeSecs
)
6249 FPrintF( stderr
, "DNSCrypt certificate has expired.\n" );
6254 signedLen
= (size_t)( certEnd
- cert
->signature
);
6255 tempBuf
= (uint8_t *) malloc( signedLen
);
6256 require_action( tempBuf
, exit
, err
= kNoMemoryErr
);
6257 err
= crypto_sign_open( tempBuf
, &tempLen
, cert
->signature
, signedLen
, inContext
->serverPublicSignKey
);
6261 FPrintF( stderr
, "DNSCrypt certificate failed verification.\n" );
6262 err
= kAuthenticationErr
;
6266 memcpy( inContext
->serverPublicKey
, cert
->publicKey
, crypto_box_PUBLICKEYBYTES
);
6267 memcpy( inContext
->clientMagic
, cert
->clientMagic
, kDNSCryptClientMagicLength
);
6269 err
= crypto_box_beforenm( inContext
->nmKey
, inContext
->serverPublicKey
, inContext
->clientSecretKey
);
6270 require_noerr( err
, exit
);
6272 inContext
->certPtr
= NULL
;
6273 inContext
->certLen
= 0;
6274 inContext
->msgLen
= 0;
6280 //===========================================================================================================================
6281 // DNSCryptBuildQuery
6282 //===========================================================================================================================
6284 static OSStatus
DNSCryptPadQuery( uint8_t *inMsgPtr
, size_t inMsgLen
, size_t inMaxLen
, size_t *outPaddedLen
);
6286 static OSStatus
DNSCryptBuildQuery( DNSCryptContext
*inContext
)
6289 DNSCryptQueryHeader
* const hdr
= (DNSCryptQueryHeader
*) inContext
->msgBuf
;
6290 uint8_t * const queryPtr
= (uint8_t *)( hdr
+ 1 );
6292 size_t paddedQueryLen
;
6293 const uint8_t * const msgLimit
= inContext
->msgBuf
+ sizeof( inContext
->msgBuf
);
6294 const uint8_t * padLimit
;
6295 uint8_t nonce
[ crypto_box_NONCEBYTES
];
6297 check_compile_time_code( sizeof( inContext
->msgBuf
) >= ( sizeof( DNSCryptQueryHeader
) + kDNSQueryMessageMaxLen
) );
6299 inContext
->queryID
= (uint16_t) Random32();
6300 err
= WriteDNSQueryMessage( queryPtr
, inContext
->queryID
, kDNSHeaderFlag_RecursionDesired
, inContext
->qname
,
6301 inContext
->qtype
, kDNSServiceClass_IN
, &queryLen
);
6302 require_noerr( err
, exit
);
6304 padLimit
= &queryPtr
[ queryLen
+ kDNSCryptMaxPadLength
];
6305 if( padLimit
> msgLimit
) padLimit
= msgLimit
;
6307 err
= DNSCryptPadQuery( queryPtr
, queryLen
, (size_t)( padLimit
- queryPtr
), &paddedQueryLen
);
6308 require_noerr( err
, exit
);
6310 memset( queryPtr
- crypto_box_ZEROBYTES
, 0, crypto_box_ZEROBYTES
);
6311 RandomBytes( inContext
->clientNonce
, kDNSCryptHalfNonceLength
);
6312 memcpy( nonce
, inContext
->clientNonce
, kDNSCryptHalfNonceLength
);
6313 memset( &nonce
[ kDNSCryptHalfNonceLength
], 0, kDNSCryptHalfNonceLength
);
6315 err
= crypto_box_afternm( queryPtr
- crypto_box_ZEROBYTES
, queryPtr
- crypto_box_ZEROBYTES
,
6316 paddedQueryLen
+ crypto_box_ZEROBYTES
, nonce
, inContext
->nmKey
);
6317 require_noerr( err
, exit
);
6319 memcpy( hdr
->clientMagic
, inContext
->clientMagic
, kDNSCryptClientMagicLength
);
6320 memcpy( hdr
->clientPublicKey
, inContext
->clientPublicKey
, crypto_box_PUBLICKEYBYTES
);
6321 memcpy( hdr
->clientNonce
, nonce
, kDNSCryptHalfNonceLength
);
6323 inContext
->msgLen
= (size_t)( &queryPtr
[ paddedQueryLen
] - inContext
->msgBuf
);
6329 static OSStatus
DNSCryptPadQuery( uint8_t *inMsgPtr
, size_t inMsgLen
, size_t inMaxLen
, size_t *outPaddedLen
)
6334 require_action_quiet( ( inMsgLen
+ kDNSCryptMinPadLength
) <= inMaxLen
, exit
, err
= kSizeErr
);
6336 paddedLen
= inMsgLen
+ kDNSCryptMinPadLength
+
6337 arc4random_uniform( (uint32_t)( inMaxLen
- ( inMsgLen
+ kDNSCryptMinPadLength
) + 1 ) );
6338 paddedLen
+= ( kDNSCryptBlockSize
- ( paddedLen
% kDNSCryptBlockSize
) );
6339 if( paddedLen
> inMaxLen
) paddedLen
= inMaxLen
;
6341 inMsgPtr
[ inMsgLen
] = 0x80;
6342 memset( &inMsgPtr
[ inMsgLen
+ 1 ], 0, paddedLen
- ( inMsgLen
+ 1 ) );
6344 if( outPaddedLen
) *outPaddedLen
= paddedLen
;
6351 //===========================================================================================================================
6352 // DNSCryptSendQuery
6353 //===========================================================================================================================
6355 static OSStatus
DNSCryptSendQuery( DNSCryptContext
*inContext
)
6358 SocketContext
* sockCtx
;
6359 SocketRef sock
= kInvalidSocketRef
;
6361 check( inContext
->msgLen
> 0 );
6362 check( !inContext
->readSource
);
6364 err
= UDPClientSocketOpen( AF_UNSPEC
, &inContext
->serverAddr
, 0, -1, NULL
, &sock
);
6365 require_noerr( err
, exit
);
6367 inContext
->sendTicks
= UpTicks();
6368 err
= _SocketWriteAll( sock
, inContext
->msgBuf
, inContext
->msgLen
, 5 );
6369 require_noerr( err
, exit
);
6371 err
= SocketContextCreate( sock
, inContext
, &sockCtx
);
6372 require_noerr( err
, exit
);
6373 sock
= kInvalidSocketRef
;
6375 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, DNSCryptReceiveResponseHandler
, SocketContextCancelHandler
, sockCtx
,
6376 &inContext
->readSource
);
6377 if( err
) ForgetSocketContext( &sockCtx
);
6378 require_noerr( err
, exit
);
6380 dispatch_resume( inContext
->readSource
);
6383 ForgetSocket( &sock
);
6387 //===========================================================================================================================
6388 // DNSCryptPrintCertificate
6389 //===========================================================================================================================
6391 #define kCertTimeStrBufLen 32
6393 static char * CertTimeStr( time_t inTime
, char inBuffer
[ kCertTimeStrBufLen
] );
6395 static void DNSCryptPrintCertificate( const DNSCryptCert
*inCert
, size_t inLen
)
6397 time_t startTime
, endTime
;
6399 char timeBuf
[ kCertTimeStrBufLen
];
6401 check( inLen
>= kDNSCryptCertMinimumLength
);
6403 startTime
= (time_t) ReadBig32( inCert
->startTime
);
6404 endTime
= (time_t) ReadBig32( inCert
->endTime
);
6406 FPrintF( stdout
, "DNSCrypt certificate (%zu bytes):\n", inLen
);
6407 FPrintF( stdout
, "Cert Magic: %#H\n", inCert
->certMagic
, kDNSCryptCertMagicLength
, INT_MAX
);
6408 FPrintF( stdout
, "ES Version: %u\n", ReadBig16( inCert
->esVersion
) );
6409 FPrintF( stdout
, "Minor Version: %u\n", ReadBig16( inCert
->minorVersion
) );
6410 FPrintF( stdout
, "Signature: %H\n", inCert
->signature
, crypto_sign_BYTES
/ 2, INT_MAX
);
6411 FPrintF( stdout
, " %H\n", &inCert
->signature
[ crypto_sign_BYTES
/ 2 ], crypto_sign_BYTES
/ 2, INT_MAX
);
6412 FPrintF( stdout
, "Public Key: %H\n", inCert
->publicKey
, sizeof( inCert
->publicKey
), INT_MAX
);
6413 FPrintF( stdout
, "Client Magic: %H\n", inCert
->clientMagic
, kDNSCryptClientMagicLength
, INT_MAX
);
6414 FPrintF( stdout
, "Serial: %u\n", ReadBig32( inCert
->serial
) );
6415 FPrintF( stdout
, "Start Time: %u (%s)\n", (uint32_t) startTime
, CertTimeStr( startTime
, timeBuf
) );
6416 FPrintF( stdout
, "End Time: %u (%s)\n", (uint32_t) endTime
, CertTimeStr( endTime
, timeBuf
) );
6418 if( inLen
> kDNSCryptCertMinimumLength
)
6420 extLen
= (int)( inLen
- kDNSCryptCertMinimumLength
);
6421 FPrintF( stdout
, "Extensions: %.1H\n", inCert
->extensions
, extLen
, extLen
);
6423 FPrintF( stdout
, "\n" );
6426 static char * CertTimeStr( time_t inTime
, char inBuffer
[ kCertTimeStrBufLen
] )
6430 tm
= localtime( &inTime
);
6433 dlogassert( "localtime() returned a NULL pointer.\n" );
6438 strftime( inBuffer
, kCertTimeStrBufLen
, "%a %b %d %H:%M:%S %Z %Y", tm
);
6444 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
6446 //===========================================================================================================================
6448 //===========================================================================================================================
6452 const char * qnameStr
; // Name (QNAME) of the record being queried as a C string.
6453 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
6454 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
6455 int localPort
; // The port number to which the sockets are bound.
6456 int receiveSecs
; // After send, the amount of time to spend receiving.
6457 uint32_t ifIndex
; // Index of the interface over which to send the query.
6458 uint16_t qtype
; // The type (QTYPE) of the record being queried.
6459 Boolean isQU
; // True if the query is QU, i.e., requests unicast responses.
6460 Boolean allResponses
; // True if all mDNS messages received should be printed.
6461 Boolean printRawRData
; // True if RDATA should be printed as hexdumps.
6462 Boolean useIPv4
; // True if the query should be sent via IPv4 multicast.
6463 Boolean useIPv6
; // True if the query should be sent via IPv6 multicast.
6464 char ifName
[ IF_NAMESIZE
+ 1 ]; // Name of the interface over which to send the query.
6465 uint8_t qname
[ kDomainNameLengthMax
]; // Buffer to hold the QNAME in DNS label format.
6466 uint8_t msgBuf
[ kMDNSMessageSizeMax
]; // mDNS message buffer.
6470 static void MDNSQueryPrintPrologue( const MDNSQueryContext
*inContext
);
6471 static void MDNSQueryReadHandler( void *inContext
);
6473 static void MDNSQueryCmd( void )
6476 MDNSQueryContext
* context
;
6477 SocketRef sockV4
= kInvalidSocketRef
;
6478 SocketRef sockV6
= kInvalidSocketRef
;
6480 const char * ifname
;
6482 unsigned int sendCount
;
6484 // Check command parameters.
6486 if( gMDNSQuery_ReceiveSecs
< -1 )
6488 FPrintF( stdout
, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs
);
6493 context
= (MDNSQueryContext
*) calloc( 1, sizeof( *context
) );
6494 require_action( context
, exit
, err
= kNoMemoryErr
);
6496 context
->qnameStr
= gMDNSQuery_Name
;
6497 context
->receiveSecs
= gMDNSQuery_ReceiveSecs
;
6498 context
->isQU
= gMDNSQuery_IsQU
? true : false;
6499 context
->allResponses
= gMDNSQuery_AllResponses
? true : false;
6500 context
->printRawRData
= gMDNSQuery_RawRData
? true : false;
6501 context
->useIPv4
= ( gMDNSQuery_UseIPv4
|| !gMDNSQuery_UseIPv6
) ? true : false;
6502 context
->useIPv6
= ( gMDNSQuery_UseIPv6
|| !gMDNSQuery_UseIPv4
) ? true : false;
6504 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
6505 require_noerr_quiet( err
, exit
);
6507 ifname
= if_indextoname( context
->ifIndex
, context
->ifName
);
6508 require_action( ifname
, exit
, err
= kNameErr
);
6510 err
= RecordTypeFromArgString( gMDNSQuery_Type
, &context
->qtype
);
6511 require_noerr( err
, exit
);
6513 // Set up IPv4 socket.
6515 if( context
->useIPv4
)
6517 err
= CreateMulticastSocket( GetMDNSMulticastAddrV4(),
6518 gMDNSQuery_SourcePort
? gMDNSQuery_SourcePort
: ( context
->isQU
? context
->localPort
: kMDNSPort
),
6519 ifname
, context
->ifIndex
, !context
->isQU
, &context
->localPort
, &sockV4
);
6520 require_noerr( err
, exit
);
6523 // Set up IPv6 socket.
6525 if( context
->useIPv6
)
6527 err
= CreateMulticastSocket( GetMDNSMulticastAddrV6(),
6528 gMDNSQuery_SourcePort
? gMDNSQuery_SourcePort
: ( context
->isQU
? context
->localPort
: kMDNSPort
),
6529 ifname
, context
->ifIndex
, !context
->isQU
, &context
->localPort
, &sockV6
);
6530 require_noerr( err
, exit
);
6533 // Craft mDNS query message.
6535 check_compile_time_code( sizeof( context
->msgBuf
) >= kDNSQueryMessageMaxLen
);
6536 err
= WriteDNSQueryMessage( context
->msgBuf
, kDefaultMDNSMessageID
, kDefaultMDNSQueryFlags
, context
->qnameStr
,
6537 context
->qtype
, context
->isQU
? ( kDNSServiceClass_IN
| kQClassUnicastResponseBit
) : kDNSServiceClass_IN
, &msgLen
);
6538 require_noerr( err
, exit
);
6542 MDNSQueryPrintPrologue( context
);
6544 // Send mDNS query message.
6547 if( IsValidSocket( sockV4
) )
6549 const struct sockaddr
* const mcastAddr4
= GetMDNSMulticastAddrV4();
6551 n
= sendto( sockV4
, context
->msgBuf
, msgLen
, 0, mcastAddr4
, SockAddrGetSize( mcastAddr4
) );
6552 err
= map_socket_value_errno( sockV4
, n
== (ssize_t
) msgLen
, n
);
6555 FPrintF( stderr
, "*** Failed to send query on IPv4 socket with error %#m\n", err
);
6556 ForgetSocket( &sockV4
);
6563 if( IsValidSocket( sockV6
) )
6565 const struct sockaddr
* const mcastAddr6
= GetMDNSMulticastAddrV6();
6567 n
= sendto( sockV6
, context
->msgBuf
, msgLen
, 0, mcastAddr6
, SockAddrGetSize( mcastAddr6
) );
6568 err
= map_socket_value_errno( sockV6
, n
== (ssize_t
) msgLen
, n
);
6571 FPrintF( stderr
, "*** Failed to send query on IPv6 socket with error %#m\n", err
);
6572 ForgetSocket( &sockV6
);
6579 require_action_quiet( sendCount
> 0, exit
, err
= kUnexpectedErr
);
6581 // If there's no wait period after the send, then exit.
6583 if( context
->receiveSecs
== 0 ) goto exit
;
6585 // Create dispatch read sources for socket(s).
6587 if( IsValidSocket( sockV4
) )
6589 SocketContext
* sockCtx
;
6591 err
= SocketContextCreate( sockV4
, context
, &sockCtx
);
6592 require_noerr( err
, exit
);
6593 sockV4
= kInvalidSocketRef
;
6595 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, MDNSQueryReadHandler
, SocketContextCancelHandler
, sockCtx
,
6596 &context
->readSourceV4
);
6597 if( err
) ForgetSocketContext( &sockCtx
);
6598 require_noerr( err
, exit
);
6600 dispatch_resume( context
->readSourceV4
);
6603 if( IsValidSocket( sockV6
) )
6605 SocketContext
* sockCtx
;
6607 err
= SocketContextCreate( sockV6
, context
, &sockCtx
);
6608 require_noerr( err
, exit
);
6609 sockV6
= kInvalidSocketRef
;
6611 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, MDNSQueryReadHandler
, SocketContextCancelHandler
, sockCtx
,
6612 &context
->readSourceV6
);
6613 if( err
) ForgetSocketContext( &sockCtx
);
6614 require_noerr( err
, exit
);
6616 dispatch_resume( context
->readSourceV6
);
6619 if( context
->receiveSecs
> 0 )
6621 dispatch_after_f( dispatch_time_seconds( context
->receiveSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
6627 ForgetSocket( &sockV4
);
6628 ForgetSocket( &sockV6
);
6629 if( err
) exit( 1 );
6632 //===========================================================================================================================
6634 //===========================================================================================================================
6636 static void _MDNSColliderCmdStopHandler( void *inContext
, OSStatus inError
);
6638 static void MDNSColliderCmd( void )
6641 MDNSColliderRef collider
= NULL
;
6642 uint8_t * rdataPtr
= NULL
;
6643 size_t rdataLen
= 0;
6644 const char * ifname
;
6646 MDNSColliderProtocols protocols
;
6648 char ifName
[ IF_NAMESIZE
+ 1 ];
6649 uint8_t name
[ kDomainNameLengthMax
];
6651 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
6652 require_noerr_quiet( err
, exit
);
6654 ifname
= if_indextoname( ifIndex
, ifName
);
6657 FPrintF( stderr
, "error: Invalid interface name or index: %s\n", gInterface
);
6662 err
= DomainNameFromString( name
, gMDNSCollider_Name
, NULL
);
6665 FPrintF( stderr
, "error: Invalid record name: %s\n", gMDNSCollider_Name
);
6669 err
= RecordTypeFromArgString( gMDNSCollider_Type
, &type
);
6670 require_noerr_quiet( err
, exit
);
6672 if( gMDNSCollider_RecordData
)
6674 err
= RecordDataFromArgString( gMDNSCollider_RecordData
, &rdataPtr
, &rdataLen
);
6675 require_noerr_quiet( err
, exit
);
6678 err
= MDNSColliderCreate( dispatch_get_main_queue(), &collider
);
6679 require_noerr( err
, exit
);
6681 err
= MDNSColliderSetProgram( collider
, gMDNSCollider_Program
);
6684 FPrintF( stderr
, "error: Failed to set program string: '%s'\n", gMDNSCollider_Program
);
6688 err
= MDNSColliderSetRecord( collider
, name
, type
, rdataPtr
, rdataLen
);
6689 require_noerr( err
, exit
);
6690 ForgetMem( &rdataPtr
);
6692 protocols
= kMDNSColliderProtocol_None
;
6693 if( gMDNSCollider_UseIPv4
|| !gMDNSCollider_UseIPv6
) protocols
|= kMDNSColliderProtocol_IPv4
;
6694 if( gMDNSCollider_UseIPv6
|| !gMDNSCollider_UseIPv4
) protocols
|= kMDNSColliderProtocol_IPv6
;
6695 MDNSColliderSetProtocols( collider
, protocols
);
6696 MDNSColliderSetInterfaceIndex( collider
, ifIndex
);
6697 MDNSColliderSetStopHandler( collider
, _MDNSColliderCmdStopHandler
, collider
);
6699 err
= MDNSColliderStart( collider
);
6700 require_noerr( err
, exit
);
6705 FreeNullSafe( rdataPtr
);
6706 CFReleaseNullSafe( collider
);
6707 if( err
) exit( 1 );
6710 static void _MDNSColliderCmdStopHandler( void *inContext
, OSStatus inError
)
6712 MDNSColliderRef
const collider
= (MDNSColliderRef
) inContext
;
6714 CFRelease( collider
);
6715 exit( inError
? 1 : 0 );
6718 //===========================================================================================================================
6719 // MDNSQueryPrintPrologue
6720 //===========================================================================================================================
6722 static void MDNSQueryPrintPrologue( const MDNSQueryContext
*inContext
)
6724 const int receiveSecs
= inContext
->receiveSecs
;
6726 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, inContext
->ifName
);
6727 FPrintF( stdout
, "Name: %s\n", inContext
->qnameStr
);
6728 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->qtype
), inContext
->qtype
);
6729 FPrintF( stdout
, "Class: IN (%s)\n", inContext
->isQU
? "QU" : "QM" );
6730 FPrintF( stdout
, "Local port: %d\n", inContext
->localPort
);
6731 FPrintF( stdout
, "IP protocols: %?s%?s%?s\n",
6732 inContext
->useIPv4
, "IPv4", ( inContext
->useIPv4
&& inContext
->useIPv6
), ", ", inContext
->useIPv6
, "IPv6" );
6733 FPrintF( stdout
, "Receive duration: " );
6734 if( receiveSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", receiveSecs
, receiveSecs
!= 1, 's' );
6735 else FPrintF( stdout
, "∞\n" );
6736 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
6739 //===========================================================================================================================
6740 // MDNSQueryReadHandler
6741 //===========================================================================================================================
6743 static void MDNSQueryReadHandler( void *inContext
)
6747 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6748 MDNSQueryContext
* const context
= (MDNSQueryContext
*) sockCtx
->userContext
;
6750 sockaddr_ip fromAddr
;
6751 Boolean foundAnswer
= false;
6753 gettimeofday( &now
, NULL
);
6755 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &msgLen
, &fromAddr
,
6756 sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6757 require_noerr( err
, exit
);
6759 if( !context
->allResponses
&& ( msgLen
>= kDNSHeaderLength
) )
6761 const uint8_t * ptr
;
6762 const DNSHeader
* const hdr
= (DNSHeader
*) context
->msgBuf
;
6763 unsigned int rrCount
, i
;
6764 uint16_t type
, class;
6765 uint8_t name
[ kDomainNameLengthMax
];
6767 err
= DNSMessageGetAnswerSection( context
->msgBuf
, msgLen
, &ptr
);
6768 require_noerr( err
, exit
);
6770 if( context
->qname
[ 0 ] == 0 )
6772 err
= DomainNameAppendString( context
->qname
, context
->qnameStr
, NULL
);
6773 require_noerr( err
, exit
);
6776 rrCount
= DNSHeaderGetAnswerCount( hdr
) + DNSHeaderGetAuthorityCount( hdr
) + DNSHeaderGetAdditionalCount( hdr
);
6777 for( i
= 0; i
< rrCount
; ++i
)
6779 err
= DNSMessageExtractRecord( context
->msgBuf
, msgLen
, ptr
, name
, &type
, &class, NULL
, NULL
, NULL
, &ptr
);
6780 require_noerr( err
, exit
);
6782 if( ( ( context
->qtype
== kDNSServiceType_ANY
) || ( type
== context
->qtype
) ) &&
6783 DomainNameEqual( name
, context
->qname
) )
6790 if( context
->allResponses
|| foundAnswer
)
6792 FPrintF( stdout
, "---\n" );
6793 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6794 FPrintF( stdout
, "Source: %##a\n", &fromAddr
);
6795 FPrintF( stdout
, "Message size: %zu\n\n%#.*{du:dnsmsg}",
6796 msgLen
, context
->printRawRData
? 1 : 0, context
->msgBuf
, msgLen
);
6800 if( err
) exit( 1 );
6803 //===========================================================================================================================
6805 //===========================================================================================================================
6807 static void PIDToUUIDCmd( void )
6811 struct proc_uniqidentifierinfo info
;
6813 n
= proc_pidinfo( gPIDToUUID_PID
, PROC_PIDUNIQIDENTIFIERINFO
, 1, &info
, sizeof( info
) );
6814 require_action_quiet( n
== (int) sizeof( info
), exit
, err
= kUnknownErr
);
6816 FPrintF( stdout
, "%#U\n", info
.p_uuid
);
6820 if( err
) exit( 1 );
6823 //===========================================================================================================================
6825 //===========================================================================================================================
6827 typedef struct DNSServerPrivate
* DNSServerRef
;
6831 DNSServerRef server
; // Reference to the DNS server.
6832 dispatch_source_t sigIntSource
; // Dispatch SIGINT source.
6833 dispatch_source_t sigTermSource
; // Dispatch SIGTERM source.
6834 const char * domainOverride
; // If non-NULL, the server is to use this domain instead of "d.test.".
6835 #if( TARGET_OS_DARWIN )
6836 dispatch_source_t processMonitor
; // Process monitor source for process being followed, if any.
6837 pid_t followPID
; // PID of process being followed, if any. (If it exits, we exit).
6838 Boolean addedResolver
; // True if system DNS settings contains a resolver entry for server.
6840 Boolean loopbackOnly
; // True if the server should be bound to the loopback interface.
6842 } DNSServerCmdContext
;
6846 kDNSServerEvent_Started
= 1,
6847 kDNSServerEvent_Stopped
= 2
6849 } DNSServerEventType
;
6851 typedef void ( *DNSServerEventHandler_f
)( DNSServerEventType inType
, uintptr_t inEventData
, void *inContext
);
6853 CFTypeID
DNSServerGetTypeID( void );
6856 dispatch_queue_t inQueue
,
6857 DNSServerEventHandler_f inEventHandler
,
6858 void * inEventContext
,
6859 unsigned int inResponseDelayMs
,
6860 uint32_t inDefaultTTL
,
6862 Boolean inLoopbackOnly
,
6863 const char * inDomain
,
6864 Boolean inBadUDPMode
,
6865 DNSServerRef
* outServer
);
6866 static void DNSServerStart( DNSServerRef inServer
);
6867 static void DNSServerStop( DNSServerRef inServer
);
6869 #define ForgetDNSServer( X ) ForgetCustomEx( X, DNSServerStop, CFRelease )
6871 static void DNSServerCmdContextFree( DNSServerCmdContext
*inContext
);
6872 static void DNSServerCmdEventHandler( DNSServerEventType inType
, uintptr_t inEventData
, void *inContext
);
6873 static void DNSServerCmdSigIntHandler( void *inContext
);
6874 static void DNSServerCmdSigTermHandler( void *inContext
);
6875 #if( TARGET_OS_DARWIN )
6876 static void DNSServerCmdFollowedProcessHandler( void *inContext
);
6879 ulog_define_ex( kDNSSDUtilIdentifier
, DNSServer
, kLogLevelInfo
, kLogFlags_None
, "DNSServer", NULL
);
6880 #define ds_ulog( LEVEL, ... ) ulog( &log_category_from_name( DNSServer ), (LEVEL), __VA_ARGS__ )
6882 static void DNSServerCmd( void )
6885 DNSServerCmdContext
* context
= NULL
;
6887 if( gDNSServer_Foreground
)
6889 LogControl( "DNSServer:output=file;stdout,DNSServer:flags=time;prefix" );
6892 err
= CheckIntegerArgument( gDNSServer_ResponseDelayMs
, "response delay (ms)", 0, INT_MAX
);
6893 require_noerr_quiet( err
, exit
);
6895 err
= CheckIntegerArgument( gDNSServer_DefaultTTL
, "default TTL", 0, INT32_MAX
);
6896 require_noerr_quiet( err
, exit
);
6898 err
= CheckIntegerArgument( gDNSServer_Port
, "port number", -UINT16_MAX
, UINT16_MAX
);
6899 require_noerr_quiet( err
, exit
);
6901 context
= (DNSServerCmdContext
*) calloc( 1, sizeof( *context
) );
6902 require_action( context
, exit
, err
= kNoMemoryErr
);
6904 context
->domainOverride
= gDNSServer_DomainOverride
;
6905 context
->loopbackOnly
= gDNSServer_LoopbackOnly
? true : false;
6907 #if( TARGET_OS_DARWIN )
6908 if( gDNSServer_FollowPID
)
6910 context
->followPID
= _StringToPID( gDNSServer_FollowPID
, &err
);
6911 if( err
|| ( context
->followPID
< 0 ) )
6913 FPrintF( stderr
, "error: Invalid follow PID: %s\n", gDNSServer_FollowPID
);
6918 err
= DispatchProcessMonitorCreate( context
->followPID
, DISPATCH_PROC_EXIT
, dispatch_get_main_queue(),
6919 DNSServerCmdFollowedProcessHandler
, NULL
, context
, &context
->processMonitor
);
6920 require_noerr( err
, exit
);
6921 dispatch_resume( context
->processMonitor
);
6925 context
->followPID
= -1;
6929 signal( SIGINT
, SIG_IGN
);
6930 err
= DispatchSignalSourceCreate( SIGINT
, DNSServerCmdSigIntHandler
, context
, &context
->sigIntSource
);
6931 require_noerr( err
, exit
);
6932 dispatch_resume( context
->sigIntSource
);
6934 signal( SIGTERM
, SIG_IGN
);
6935 err
= DispatchSignalSourceCreate( SIGTERM
, DNSServerCmdSigTermHandler
, context
, &context
->sigTermSource
);
6936 require_noerr( err
, exit
);
6937 dispatch_resume( context
->sigTermSource
);
6939 err
= DNSServerCreate( dispatch_get_main_queue(), DNSServerCmdEventHandler
, context
,
6940 (unsigned int) gDNSServer_ResponseDelayMs
, (uint32_t) gDNSServer_DefaultTTL
, gDNSServer_Port
, context
->loopbackOnly
,
6941 context
->domainOverride
, gDNSServer_BadUDPMode
? true : false, &context
->server
);
6942 require_noerr( err
, exit
);
6944 DNSServerStart( context
->server
);
6948 FPrintF( stderr
, "Failed to start DNS server: %#m\n", err
);
6949 if( context
) DNSServerCmdContextFree( context
);
6950 if( err
) exit( 1 );
6953 //===========================================================================================================================
6954 // DNSServerCmdContextFree
6955 //===========================================================================================================================
6957 static void DNSServerCmdContextFree( DNSServerCmdContext
*inContext
)
6959 ForgetCF( &inContext
->server
);
6960 dispatch_source_forget( &inContext
->sigIntSource
);
6961 dispatch_source_forget( &inContext
->sigTermSource
);
6962 #if( TARGET_OS_DARWIN )
6963 dispatch_source_forget( &inContext
->processMonitor
);
6968 //===========================================================================================================================
6969 // DNSServerCmdEventHandler
6970 //===========================================================================================================================
6972 #if( TARGET_OS_DARWIN )
6973 static OSStatus
_DNSServerCmdLoopbackResolverAdd( const char *inDomain
, int inPort
);
6974 static OSStatus
_DNSServerCmdLoopbackResolverRemove( void );
6977 static void DNSServerCmdEventHandler( DNSServerEventType inType
, uintptr_t inEventData
, void *inContext
)
6980 DNSServerCmdContext
* const context
= (DNSServerCmdContext
*) inContext
;
6982 if( inType
== kDNSServerEvent_Started
)
6984 #if( TARGET_OS_DARWIN )
6985 const int port
= (int) inEventData
;
6987 err
= _DNSServerCmdLoopbackResolverAdd( context
->domainOverride
? context
->domainOverride
: "d.test.", port
);
6990 ds_ulog( kLogLevelError
, "Failed to add loopback resolver to DNS configuration for \"d.test.\" domain: %#m\n",
6992 if( context
->loopbackOnly
) ForgetDNSServer( &context
->server
);
6996 context
->addedResolver
= true;
7000 else if( inType
== kDNSServerEvent_Stopped
)
7002 const OSStatus stopError
= (OSStatus
) inEventData
;
7004 if( stopError
) ds_ulog( kLogLevelError
, "The server stopped unexpectedly with error: %#m.\n", stopError
);
7007 #if( TARGET_OS_DARWIN )
7008 if( context
->addedResolver
)
7010 err
= _DNSServerCmdLoopbackResolverRemove();
7013 ds_ulog( kLogLevelError
, "Failed to remove loopback resolver from DNS configuration: %#m\n", err
);
7017 context
->addedResolver
= false;
7020 else if( context
->loopbackOnly
)
7025 DNSServerCmdContextFree( context
);
7026 exit( ( stopError
|| err
) ? 1 : 0 );
7030 #if( TARGET_OS_DARWIN )
7031 //===========================================================================================================================
7032 // _DNSServerCmdLoopbackResolverAdd
7033 //===========================================================================================================================
7035 static OSStatus
_DNSServerCmdLoopbackResolverAdd( const char *inDomain
, int inPort
)
7038 SCDynamicStoreRef store
;
7039 CFPropertyListRef plist
= NULL
;
7040 CFStringRef key
= NULL
;
7041 const uint32_t loopbackV4
= htonl( INADDR_LOOPBACK
);
7044 store
= SCDynamicStoreCreate( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
7045 err
= map_scerror( store
);
7046 require_noerr( err
, exit
);
7048 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
7063 kSCPropNetDNSSupplementalMatchDomains
, inDomain
,
7064 kSCPropNetDNSServerAddresses
, &loopbackV4
, in6addr_loopback
.s6_addr
,
7065 kSCPropNetDNSServerPort
, inPort
,
7066 kSCPropInterfaceName
, CFSTR( "lo0" ),
7067 kSCPropNetDNSConfirmedServiceID
, CFSTR( "com.apple.dnssdutil.server" ) );
7068 require_noerr( err
, exit
);
7070 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
,
7071 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS
);
7072 require_action( key
, exit
, err
= kUnknownErr
);
7074 success
= SCDynamicStoreSetValue( store
, key
, plist
);
7075 require_action( success
, exit
, err
= kUnknownErr
);
7078 CFReleaseNullSafe( store
);
7079 CFReleaseNullSafe( plist
);
7080 CFReleaseNullSafe( key
);
7084 //===========================================================================================================================
7085 // _DNSServerCmdLoopbackResolverRemove
7086 //===========================================================================================================================
7088 static OSStatus
_DNSServerCmdLoopbackResolverRemove( void )
7091 SCDynamicStoreRef store
;
7092 CFStringRef key
= NULL
;
7095 store
= SCDynamicStoreCreate( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
7096 err
= map_scerror( store
);
7097 require_noerr( err
, exit
);
7099 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
,
7100 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS
);
7101 require_action( key
, exit
, err
= kUnknownErr
);
7103 success
= SCDynamicStoreRemoveValue( store
, key
);
7104 require_action( success
, exit
, err
= kUnknownErr
);
7107 CFReleaseNullSafe( store
);
7108 CFReleaseNullSafe( key
);
7113 //===========================================================================================================================
7114 // DNSServerCmdSigIntHandler
7115 //===========================================================================================================================
7117 static void _DNSServerCmdShutdown( DNSServerCmdContext
*inContext
, int inSignal
);
7119 static void DNSServerCmdSigIntHandler( void *inContext
)
7121 _DNSServerCmdShutdown( (DNSServerCmdContext
*) inContext
, SIGINT
);
7124 //===========================================================================================================================
7125 // DNSServerCmdSigTermHandler
7126 //===========================================================================================================================
7128 static void DNSServerCmdSigTermHandler( void *inContext
)
7130 _DNSServerCmdShutdown( (DNSServerCmdContext
*) inContext
, SIGTERM
);
7133 #if( TARGET_OS_DARWIN )
7134 //===========================================================================================================================
7135 // DNSServerCmdFollowedProcessHandler
7136 //===========================================================================================================================
7138 static void DNSServerCmdFollowedProcessHandler( void *inContext
)
7140 DNSServerCmdContext
* const context
= (DNSServerCmdContext
*) inContext
;
7142 if( dispatch_source_get_data( context
->processMonitor
) & DISPATCH_PROC_EXIT
) _DNSServerCmdShutdown( context
, 0 );
7146 //===========================================================================================================================
7147 // _DNSServerCmdExternalExit
7148 //===========================================================================================================================
7150 #define SignalNumberToString( X ) ( \
7151 ( (X) == SIGINT ) ? "SIGINT" : \
7152 ( (X) == SIGTERM ) ? "SIGTERM" : \
7155 static void _DNSServerCmdShutdown( DNSServerCmdContext
*inContext
, int inSignal
)
7157 dispatch_source_forget( &inContext
->sigIntSource
);
7158 dispatch_source_forget( &inContext
->sigTermSource
);
7159 #if( TARGET_OS_DARWIN )
7160 dispatch_source_forget( &inContext
->processMonitor
);
7164 ds_ulog( kLogLevelNotice
, "Exiting: followed process (%lld) exited\n", (int64_t) inContext
->followPID
);
7169 ds_ulog( kLogLevelNotice
, "Exiting: received signal %d (%s)\n", inSignal
, SignalNumberToString( inSignal
) );
7172 ForgetDNSServer( &inContext
->server
);
7175 //===========================================================================================================================
7177 //===========================================================================================================================
7179 #define kDDotTestDomainName (const uint8_t *) "\x01" "d" "\x04" "test"
7181 typedef struct DNSDelayedResponse DNSDelayedResponse
;
7182 struct DNSDelayedResponse
7184 DNSDelayedResponse
* next
;
7185 sockaddr_ip destAddr
;
7186 uint64_t targetTicks
;
7191 struct DNSServerPrivate
7193 CFRuntimeBase base
; // CF object base.
7194 uint8_t * domain
; // Parent domain of server's resource records.
7195 dispatch_queue_t queue
; // Queue for DNS server's events.
7196 dispatch_source_t readSourceUDPv4
; // Read source for IPv4 UDP socket.
7197 dispatch_source_t readSourceUDPv6
; // Read source for IPv6 UDP socket.
7198 dispatch_source_t readSourceTCPv4
; // Read source for IPv4 TCP socket.
7199 dispatch_source_t readSourceTCPv6
; // Read source for IPv6 TCP socket.
7200 SocketRef sockUDPv4
;
7201 SocketRef sockUDPv6
;
7202 DNSServerEventHandler_f eventHandler
;
7203 void * eventContext
;
7204 DNSDelayedResponse
* responseList
;
7205 dispatch_source_t responseTimer
;
7206 unsigned int responseDelayMs
;
7207 uint32_t defaultTTL
;
7208 uint32_t serial
; // Serial number for SOA record.
7209 int port
; // Port to use for receiving and sending DNS messages.
7212 Boolean loopbackOnly
;
7213 Boolean badUDPMode
; // True if the server runs in Bad UDP mode.
7216 static void _DNSServerUDPReadHandler( void *inContext
);
7217 static void _DNSServerTCPReadHandler( void *inContext
);
7218 static void _DNSDelayedResponseFree( DNSDelayedResponse
*inResponse
);
7219 static void _DNSDelayedResponseFreeList( DNSDelayedResponse
*inList
);
7221 CF_CLASS_DEFINE( DNSServer
);
7225 dispatch_queue_t inQueue
,
7226 DNSServerEventHandler_f inEventHandler
,
7227 void * inEventContext
,
7228 unsigned int inResponseDelayMs
,
7229 uint32_t inDefaultTTL
,
7231 Boolean inLoopbackOnly
,
7232 const char * inDomain
,
7233 Boolean inBadUDPMode
,
7234 DNSServerRef
* outServer
)
7237 DNSServerRef obj
= NULL
;
7239 require_action_quiet( inDefaultTTL
<= INT32_MAX
, exit
, err
= kRangeErr
);
7241 CF_OBJECT_CREATE( DNSServer
, obj
, err
, exit
);
7243 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
7244 obj
->eventHandler
= inEventHandler
;
7245 obj
->eventContext
= inEventContext
;
7246 obj
->responseDelayMs
= inResponseDelayMs
;
7247 obj
->defaultTTL
= inDefaultTTL
;
7249 obj
->loopbackOnly
= inLoopbackOnly
;
7250 obj
->badUDPMode
= inBadUDPMode
;
7254 err
= StringToDomainName( inDomain
, &obj
->domain
, NULL
);
7255 require_noerr_quiet( err
, exit
);
7259 err
= DomainNameDup( kDDotTestDomainName
, &obj
->domain
, NULL
);
7260 require_noerr_quiet( err
, exit
);
7268 CFReleaseNullSafe( obj
);
7272 //===========================================================================================================================
7273 // _DNSServerFinalize
7274 //===========================================================================================================================
7276 static void _DNSServerFinalize( CFTypeRef inObj
)
7278 DNSServerRef
const me
= (DNSServerRef
) inObj
;
7280 check( !me
->readSourceUDPv4
);
7281 check( !me
->readSourceUDPv6
);
7282 check( !me
->readSourceTCPv4
);
7283 check( !me
->readSourceTCPv6
);
7284 check( !me
->responseTimer
);
7285 ForgetMem( &me
->domain
);
7286 dispatch_forget( &me
->queue
);
7289 //===========================================================================================================================
7291 //===========================================================================================================================
7293 static void _DNSServerStart( void *inContext
);
7294 static void _DNSServerStop( void *inContext
, OSStatus inError
);
7296 static void DNSServerStart( DNSServerRef me
)
7299 dispatch_async_f( me
->queue
, me
, _DNSServerStart
);
7302 static void _DNSServerStart( void *inContext
)
7306 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7307 SocketRef sock
= kInvalidSocketRef
;
7308 SocketContext
* sockCtx
= NULL
;
7309 const uint32_t loopbackV4
= htonl( INADDR_LOOPBACK
);
7310 int year
, month
, day
;
7312 // Create IPv4 UDP socket.
7313 // Initially, me->port is the port requested by the user. If it's 0, then the user wants any available ephemeral port.
7314 // If it's negative, then the user would like a port number equal to its absolute value, but will settle for any
7315 // available ephemeral port, if it's not available. The actual port number that was used will be stored in me->port and
7316 // used for the remaining sockets.
7318 err
= _ServerSocketOpenEx2( AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
, me
->loopbackOnly
? &loopbackV4
: NULL
,
7319 me
->port
, &me
->port
, kSocketBufferSize_DontSet
, me
->loopbackOnly
? true : false, &sock
);
7320 require_noerr( err
, exit
);
7321 check( me
->port
> 0 );
7323 // Create read source for IPv4 UDP socket.
7325 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7326 require_noerr( err
, exit
);
7327 sock
= kInvalidSocketRef
;
7329 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerUDPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7330 &me
->readSourceUDPv4
);
7331 require_noerr( err
, exit
);
7332 dispatch_resume( me
->readSourceUDPv4
);
7333 me
->sockUDPv4
= sockCtx
->sock
;
7336 // Create IPv6 UDP socket.
7338 err
= _ServerSocketOpenEx2( AF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
, me
->loopbackOnly
? &in6addr_loopback
: NULL
,
7339 me
->port
, NULL
, kSocketBufferSize_DontSet
, me
->loopbackOnly
? true : false, &sock
);
7340 require_noerr( err
, exit
);
7342 // Create read source for IPv6 UDP socket.
7344 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7345 require_noerr( err
, exit
);
7346 sock
= kInvalidSocketRef
;
7348 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerUDPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7349 &me
->readSourceUDPv6
);
7350 require_noerr( err
, exit
);
7351 dispatch_resume( me
->readSourceUDPv6
);
7352 me
->sockUDPv6
= sockCtx
->sock
;
7355 // Create IPv4 TCP socket.
7357 err
= _ServerSocketOpenEx2( AF_INET
, SOCK_STREAM
, IPPROTO_TCP
, me
->loopbackOnly
? &loopbackV4
: NULL
,
7358 me
->port
, NULL
, kSocketBufferSize_DontSet
, false, &sock
);
7359 require_noerr( err
, exit
);
7361 // Create read source for IPv4 TCP socket.
7363 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7364 require_noerr( err
, exit
);
7365 sock
= kInvalidSocketRef
;
7367 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerTCPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7368 &me
->readSourceTCPv4
);
7369 require_noerr( err
, exit
);
7370 dispatch_resume( me
->readSourceTCPv4
);
7373 // Create IPv6 TCP socket.
7375 err
= _ServerSocketOpenEx2( AF_INET6
, SOCK_STREAM
, IPPROTO_TCP
, me
->loopbackOnly
? &in6addr_loopback
: NULL
,
7376 me
->port
, NULL
, kSocketBufferSize_DontSet
, false, &sock
);
7377 require_noerr( err
, exit
);
7379 // Create read source for IPv6 TCP socket.
7381 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7382 require_noerr( err
, exit
);
7383 sock
= kInvalidSocketRef
;
7385 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerTCPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7386 &me
->readSourceTCPv6
);
7387 require_noerr( err
, exit
);
7388 dispatch_resume( me
->readSourceTCPv6
);
7391 ds_ulog( kLogLevelInfo
, "Server is using port %d.\n", me
->port
);
7392 if( me
->eventHandler
) me
->eventHandler( kDNSServerEvent_Started
, (uintptr_t) me
->port
, me
->eventContext
);
7394 // Create the serial number for the server's SOA record in the YYYMMDDnn convention recommended by
7395 // <https://tools.ietf.org/html/rfc1912#section-2.2> using the current time.
7397 gettimeofday( &now
, NULL
);
7398 SecondsToYMD_HMS( ( INT64_C_safe( kDaysToUnixEpoch
) * kSecondsPerDay
) + now
.tv_sec
, &year
, &month
, &day
,
7400 me
->serial
= (uint32_t)( ( year
* 1000000 ) + ( month
* 10000 ) + ( day
* 100 ) + 1 );
7403 ForgetSocket( &sock
);
7404 if( sockCtx
) SocketContextRelease( sockCtx
);
7405 if( err
) _DNSServerStop( me
, err
);
7408 //===========================================================================================================================
7410 //===========================================================================================================================
7412 static void _DNSServerUserStop( void *inContext
);
7413 static void _DNSServerStop2( void *inContext
);
7415 static void DNSServerStop( DNSServerRef me
)
7418 dispatch_async_f( me
->queue
, me
, _DNSServerUserStop
);
7421 static void _DNSServerUserStop( void *inContext
)
7423 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7425 _DNSServerStop( me
, kNoErr
);
7429 static void _DNSServerStop( void *inContext
, OSStatus inError
)
7431 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7433 me
->stopError
= inError
;
7434 dispatch_source_forget( &me
->readSourceUDPv4
);
7435 dispatch_source_forget( &me
->readSourceUDPv6
);
7436 dispatch_source_forget( &me
->readSourceTCPv4
);
7437 dispatch_source_forget( &me
->readSourceTCPv6
);
7438 dispatch_source_forget( &me
->responseTimer
);
7439 me
->sockUDPv4
= kInvalidSocketRef
;
7440 me
->sockUDPv6
= kInvalidSocketRef
;
7442 if( me
->responseList
)
7444 _DNSDelayedResponseFreeList( me
->responseList
);
7445 me
->responseList
= NULL
;
7447 dispatch_async_f( me
->queue
, me
, _DNSServerStop2
);
7450 static void _DNSServerStop2( void *inContext
)
7452 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7457 if( me
->eventHandler
) me
->eventHandler( kDNSServerEvent_Stopped
, (uintptr_t) me
->stopError
, me
->eventContext
);
7463 //===========================================================================================================================
7464 // _DNSDelayedResponseFree
7465 //===========================================================================================================================
7467 static void _DNSDelayedResponseFree( DNSDelayedResponse
*inResponse
)
7469 ForgetMem( &inResponse
->msgPtr
);
7473 //===========================================================================================================================
7474 // _DNSDelayedResponseFreeList
7475 //===========================================================================================================================
7477 static void _DNSDelayedResponseFreeList( DNSDelayedResponse
*inList
)
7479 DNSDelayedResponse
* response
;
7481 while( ( response
= inList
) != NULL
)
7483 inList
= response
->next
;
7484 _DNSDelayedResponseFree( response
);
7488 //===========================================================================================================================
7489 // _DNSServerUDPReadHandler
7490 //===========================================================================================================================
7493 _DNSServerAnswerQuery(
7494 DNSServerRef inServer
,
7495 const uint8_t * inQueryPtr
,
7498 uint8_t ** outResponsePtr
,
7499 size_t * outResponseLen
);
7501 #define _DNSServerAnswerQueryForUDP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7502 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, false, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7504 #define _DNSServerAnswerQueryForTCP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7505 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, true, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7508 _DNSServerScheduleDelayedResponse(
7509 DNSServerRef inServer
,
7510 const struct sockaddr
* inDestAddr
,
7513 static void _DNSServerUDPDelayedSend( void *inContext
);
7515 static void _DNSServerUDPReadHandler( void *inContext
)
7518 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
7519 DNSServerRef
const me
= (DNSServerRef
) sockCtx
->userContext
;
7522 sockaddr_ip clientAddr
;
7523 socklen_t clientAddrLen
;
7524 uint8_t * responsePtr
= NULL
; // malloc'd
7528 gettimeofday( &now
, NULL
);
7532 clientAddrLen
= (socklen_t
) sizeof( clientAddr
);
7533 n
= recvfrom( sockCtx
->sock
, (char *) msg
, sizeof( msg
), 0, &clientAddr
.sa
, &clientAddrLen
);
7534 err
= map_socket_value_errno( sockCtx
->sock
, n
>= 0, n
);
7535 require_noerr( err
, exit
);
7537 ds_ulog( kLogLevelInfo
, "UDP server received %zd bytes from %##a at %{du:time}.\n", n
, &clientAddr
, &now
);
7539 if( n
< kDNSHeaderLength
)
7541 ds_ulog( kLogLevelInfo
, "UDP DNS message is too small (%zd < %d).\n", n
, kDNSHeaderLength
);
7545 ds_ulog( kLogLevelInfo
, "UDP received message:\n\n%1{du:dnsmsg}", msg
, (size_t) n
);
7549 err
= _DNSServerAnswerQueryForUDP( me
, msg
, (size_t) n
, &responsePtr
, &responseLen
);
7550 require_noerr_quiet( err
, exit
);
7552 // Schedule response.
7554 if( me
->responseDelayMs
> 0 )
7556 err
= _DNSServerScheduleDelayedResponse( me
, &clientAddr
.sa
, responsePtr
, responseLen
);
7557 require_noerr( err
, exit
);
7562 ds_ulog( kLogLevelInfo
, "UDP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen
, responsePtr
, responseLen
);
7564 n
= sendto( sockCtx
->sock
, (char *) responsePtr
, responseLen
, 0, &clientAddr
.sa
, clientAddrLen
);
7565 err
= map_socket_value_errno( sockCtx
->sock
, n
== (ssize_t
) responseLen
, n
);
7566 require_noerr( err
, exit
);
7570 FreeNullSafe( responsePtr
);
7575 _DNSServerScheduleDelayedResponse(
7577 const struct sockaddr
* inDestAddr
,
7582 DNSDelayedResponse
* response
;
7583 DNSDelayedResponse
** responsePtr
;
7584 DNSDelayedResponse
* newResponse
;
7585 uint64_t targetTicks
;
7587 targetTicks
= UpTicks() + MillisecondsToUpTicks( me
->responseDelayMs
);
7589 newResponse
= (DNSDelayedResponse
*) calloc( 1, sizeof( *newResponse
) );
7590 require_action( newResponse
, exit
, err
= kNoMemoryErr
);
7592 if( !me
->responseList
|| ( targetTicks
< me
->responseList
->targetTicks
) )
7594 dispatch_source_forget( &me
->responseTimer
);
7596 err
= DispatchTimerCreate( dispatch_time_milliseconds( me
->responseDelayMs
), DISPATCH_TIME_FOREVER
,
7597 ( (uint64_t) me
->responseDelayMs
) * kNanosecondsPerMillisecond
/ 10, me
->queue
, _DNSServerUDPDelayedSend
,
7598 NULL
, me
, &me
->responseTimer
);
7599 require_noerr( err
, exit
);
7600 dispatch_resume( me
->responseTimer
);
7603 SockAddrCopy( inDestAddr
, &newResponse
->destAddr
);
7604 newResponse
->targetTicks
= targetTicks
;
7605 newResponse
->msgPtr
= inMsgPtr
;
7606 newResponse
->msgLen
= inMsgLen
;
7608 for( responsePtr
= &me
->responseList
; ( response
= *responsePtr
) != NULL
; responsePtr
= &response
->next
)
7610 if( newResponse
->targetTicks
< response
->targetTicks
) break;
7612 newResponse
->next
= response
;
7613 *responsePtr
= newResponse
;
7618 if( newResponse
) _DNSDelayedResponseFree( newResponse
);
7622 static void _DNSServerUDPDelayedSend( void *inContext
)
7625 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7626 DNSDelayedResponse
* response
;
7630 uint64_t remainingNs
;
7631 DNSDelayedResponse
* freeList
= NULL
;
7633 dispatch_source_forget( &me
->responseTimer
);
7635 nowTicks
= UpTicks();
7636 while( ( ( response
= me
->responseList
) != NULL
) && ( response
->targetTicks
<= nowTicks
) )
7638 me
->responseList
= response
->next
;
7640 ds_ulog( kLogLevelInfo
, "UDP sending %zu byte response (delayed):\n\n%1{du:dnsmsg}",
7641 response
->msgLen
, response
->msgPtr
, response
->msgLen
);
7643 sock
= ( response
->destAddr
.sa
.sa_family
== AF_INET
) ? me
->sockUDPv4
: me
->sockUDPv6
;
7644 n
= sendto( sock
, (char *) response
->msgPtr
, response
->msgLen
, 0, &response
->destAddr
.sa
,
7645 SockAddrGetSize( &response
->destAddr
) );
7646 err
= map_socket_value_errno( sock
, n
== (ssize_t
) response
->msgLen
, n
);
7649 response
->next
= freeList
;
7650 freeList
= response
;
7651 nowTicks
= UpTicks();
7656 check( response
->targetTicks
> nowTicks
);
7657 remainingNs
= UpTicksToNanoseconds( response
->targetTicks
- nowTicks
);
7658 if( remainingNs
> INT64_MAX
) remainingNs
= INT64_MAX
;
7660 err
= DispatchTimerCreate( dispatch_time( DISPATCH_TIME_NOW
, (int64_t) remainingNs
), DISPATCH_TIME_FOREVER
, 0,
7661 me
->queue
, _DNSServerUDPDelayedSend
, NULL
, me
, &me
->responseTimer
);
7662 require_noerr( err
, exit
);
7663 dispatch_resume( me
->responseTimer
);
7667 if( freeList
) _DNSDelayedResponseFreeList( freeList
);
7670 //===========================================================================================================================
7671 // _DNSServerAnswerQuery
7672 //===========================================================================================================================
7674 #define kLabelPrefix_Alias "alias"
7675 #define kLabelPrefix_AliasTTL "alias-ttl"
7676 #define kLabelPrefix_Count "count-"
7677 #define kLabelPrefix_Tag "tag-"
7678 #define kLabelPrefix_TTL "ttl-"
7679 #define kLabel_IPv4 "ipv4"
7680 #define kLabel_IPv6 "ipv6"
7681 #define kLabelPrefix_SRV "srv-"
7683 #define kMaxAliasTTLCount ( ( kDomainLabelLengthMax - sizeof_string( kLabelPrefix_AliasTTL ) ) / 2 )
7684 #define kMaxParsedSRVCount ( kDomainNameLengthMax / ( 1 + sizeof_string( kLabelPrefix_SRV ) + 5 ) )
7688 uint16_t priority
; // Priority from SRV label.
7689 uint16_t weight
; // Weight from SRV label.
7690 uint16_t port
; // Port number from SRV label.
7691 uint16_t targetLen
; // Total length of the target hostname labels that follow an SRV label.
7692 const uint8_t * targetPtr
; // Pointer to the target hostname embedded in a domain name.
7697 _DNSServerInitializeResponseMessage(
7700 unsigned int inFlags
,
7701 const uint8_t * inQName
,
7702 unsigned int inQType
,
7703 unsigned int inQClass
);
7705 _DNSServerAnswerQueryDynamically(
7706 DNSServerRef inServer
,
7707 const uint8_t * inQName
,
7708 unsigned int inQType
,
7709 unsigned int inQClass
,
7711 DataBuffer
* inDB
);
7713 _DNSServerNameIsSRVName(
7714 DNSServerRef inServer
,
7715 const uint8_t * inName
,
7716 const uint8_t ** outDomainPtr
,
7717 size_t * outDomainLen
,
7718 ParsedSRV inSRVArray
[ kMaxParsedSRVCount
],
7719 size_t * outSRVCount
);
7721 _DNSServerNameIsHostname(
7722 DNSServerRef inServer
,
7723 const uint8_t * inName
,
7724 uint32_t * outAliasCount
,
7725 uint32_t inAliasTTLs
[ kMaxAliasTTLCount
],
7726 size_t * outAliasTTLCount
,
7727 unsigned int * outCount
,
7728 unsigned int * outRandCount
,
7731 Boolean
* outHasAAAA
,
7732 Boolean
* outHasSOA
);
7735 _DNSServerAnswerQuery(
7737 const uint8_t * const inQueryPtr
,
7738 const size_t inQueryLen
,
7740 uint8_t ** outResponsePtr
,
7741 size_t * outResponseLen
)
7745 const uint8_t * ptr
;
7746 const uint8_t * const queryEnd
= &inQueryPtr
[ inQueryLen
];
7747 const DNSHeader
* qhdr
;
7748 const dns_fixed_fields_question
* fields
;
7749 unsigned int msgID
, qflags
, qtype
, qclass
, rflags
;
7750 uint8_t qname
[ kDomainNameLengthMax
];
7752 DataBuffer_Init( &dataBuf
, NULL
, 0, kDNSMaxTCPMessageSize
);
7754 require_action_quiet( inQueryLen
>= kDNSHeaderLength
, exit
, err
= kUnderrunErr
);
7756 qhdr
= (const DNSHeader
*) inQueryPtr
;
7757 msgID
= DNSHeaderGetID( qhdr
);
7758 qflags
= DNSHeaderGetFlags( qhdr
);
7760 // Minimal checking of the query message's header.
7762 if( ( qflags
& kDNSHeaderFlag_Response
) || // The message must be a query, not a response.
7763 ( DNSFlagsGetOpCode( qflags
) != kDNSOpCode_Query
) || // OPCODE must be QUERY (standard query).
7764 ( DNSHeaderGetQuestionCount( qhdr
) != 1 ) ) // There should be a single question.
7772 ptr
= (const uint8_t *) &qhdr
[ 1 ];
7773 err
= DNSMessageExtractDomainName( inQueryPtr
, inQueryLen
, ptr
, qname
, &ptr
);
7774 require_noerr( err
, exit
);
7776 // Get QTYPE and QCLASS.
7778 require_action_quiet( ( (size_t)( queryEnd
- ptr
) ) >= sizeof( *fields
), exit
, err
= kUnderrunErr
);
7779 fields
= (const dns_fixed_fields_question
*) ptr
;
7780 qtype
= dns_fixed_fields_question_get_type( fields
);
7781 qclass
= dns_fixed_fields_question_get_class( fields
);
7783 // Create a tentative response message.
7785 rflags
= kDNSHeaderFlag_Response
;
7786 if( qflags
& kDNSHeaderFlag_RecursionDesired
) rflags
|= kDNSHeaderFlag_RecursionDesired
;
7787 DNSFlagsSetOpCode( rflags
, kDNSOpCode_Query
);
7789 if( me
->badUDPMode
&& !inForTCP
) msgID
= (uint16_t)( msgID
+ 1 );
7790 err
= _DNSServerInitializeResponseMessage( &dataBuf
, msgID
, rflags
, qname
, qtype
, qclass
);
7791 require_noerr( err
, exit
);
7793 err
= _DNSServerAnswerQueryDynamically( me
, qname
, qtype
, qclass
, inForTCP
, &dataBuf
);
7796 DNSFlagsSetRCode( rflags
, kDNSRCode_ServerFailure
);
7797 err
= _DNSServerInitializeResponseMessage( &dataBuf
, msgID
, rflags
, qname
, qtype
, qclass
);
7798 require_noerr( err
, exit
);
7801 err
= DataBuffer_Detach( &dataBuf
, outResponsePtr
, outResponseLen
);
7802 require_noerr( err
, exit
);
7805 DataBuffer_Free( &dataBuf
);
7810 _DNSServerInitializeResponseMessage(
7813 unsigned int inFlags
,
7814 const uint8_t * inQName
,
7815 unsigned int inQType
,
7816 unsigned int inQClass
)
7821 DataBuffer_Reset( inDB
);
7823 memset( &header
, 0, sizeof( header
) );
7824 DNSHeaderSetID( &header
, inID
);
7825 DNSHeaderSetFlags( &header
, inFlags
);
7826 DNSHeaderSetQuestionCount( &header
, 1 );
7828 err
= DataBuffer_Append( inDB
, &header
, sizeof( header
) );
7829 require_noerr( err
, exit
);
7831 err
= _DataBuffer_AppendDNSQuestion( inDB
, inQName
, DomainNameLength( inQName
), (uint16_t) inQType
,
7832 (uint16_t) inQClass
);
7833 require_noerr( err
, exit
);
7840 _DNSServerAnswerQueryDynamically(
7842 const uint8_t * const inQName
,
7843 const unsigned int inQType
,
7844 const unsigned int inQClass
,
7845 const Boolean inForTCP
,
7846 DataBuffer
* const inDB
)
7850 unsigned int flags
, rcode
;
7851 uint32_t aliasCount
, i
;
7852 uint32_t aliasTTLs
[ kMaxAliasTTLCount
];
7853 size_t aliasTTLCount
;
7854 unsigned int addrCount
, randCount
;
7856 ParsedSRV srvArray
[ kMaxParsedSRVCount
];
7858 const uint8_t * srvDomainPtr
;
7859 size_t srvDomainLen
;
7860 unsigned int answerCount
;
7861 Boolean notImplemented
, truncated
;
7862 Boolean useAliasTTLs
, nameExists
, nameHasA
, nameHasAAAA
, nameHasSRV
, nameHasSOA
;
7863 uint8_t namePtr
[ 2 ];
7864 dns_fixed_fields_record fields
;
7869 require_action_quiet( inQClass
== kDNSServiceClass_IN
, done
, notImplemented
= true );
7871 notImplemented
= false;
7874 nameHasAAAA
= false;
7876 useAliasTTLs
= false;
7881 if( _DNSServerNameIsHostname( me
, inQName
, &aliasCount
, aliasTTLs
, &aliasTTLCount
, &addrCount
, &randCount
, &ttl
,
7882 &nameHasA
, &nameHasAAAA
, &nameHasSOA
) )
7884 check( !( ( aliasCount
> 0 ) && ( aliasTTLCount
> 0 ) ) );
7885 check( ( addrCount
>= 1 ) && ( addrCount
<= 255 ) );
7886 check( ( randCount
== 0 ) || ( ( randCount
>= addrCount
) && ( randCount
<= 255 ) ) );
7887 check( nameHasA
|| nameHasAAAA
);
7889 if( aliasTTLCount
> 0 )
7891 aliasCount
= (uint32_t) aliasTTLCount
;
7892 useAliasTTLs
= true;
7896 else if( _DNSServerNameIsSRVName( me
, inQName
, &srvDomainPtr
, &srvDomainLen
, srvArray
, &srvCount
) )
7901 require_quiet( nameExists
, done
);
7903 if( aliasCount
> 0 )
7906 uint8_t rdataLabel
[ 1 + kDomainLabelLengthMax
+ 1 ];
7908 // If aliasCount is non-zero, then the first label of QNAME is either "alias" or "alias-<N>". superPtr is a name
7909 // compression pointer to the second label of QNAME, i.e., the immediate superdomain name of QNAME. It's used for
7910 // the RDATA of CNAME records whose canonical name ends with the superdomain name. It may also be used to construct
7911 // CNAME record names, when the offset to the previous CNAME's RDATA doesn't fit in a compression pointer.
7913 const uint8_t superPtr
[ 2 ] = { 0xC0, (uint8_t)( kDNSHeaderLength
+ 1 + inQName
[ 0 ] ) };
7915 // The name of the first CNAME record is equal to QNAME, so nameOffset is set to offset of QNAME.
7917 nameOffset
= kDNSHeaderLength
;
7919 for( i
= aliasCount
; i
>= 1; --i
)
7925 uint8_t nameLabel
[ 1 + kDomainLabelLengthMax
];
7927 if( nameOffset
<= kDNSCompressionOffsetMax
)
7929 DNSMessageWriteLabelPointer( namePtr
, nameOffset
);
7930 nameLen
= sizeof( namePtr
);
7934 memcpy( nameLabel
, rdataLabel
, 1 + rdataLabel
[ 0 ] );
7935 nameLen
= 1 + nameLabel
[ 0 ] + sizeof( superPtr
);
7940 char * dst
= (char *) &rdataLabel
[ 1 ];
7941 char * const lim
= (char *) &rdataLabel
[ countof( rdataLabel
) ];
7945 err
= SNPrintF_Add( &dst
, lim
, kLabelPrefix_AliasTTL
);
7946 require_noerr( err
, exit
);
7948 for( j
= aliasCount
- ( i
- 1 ); j
< aliasCount
; ++j
)
7950 err
= SNPrintF_Add( &dst
, lim
, "-%u", aliasTTLs
[ j
] );
7951 require_noerr( err
, exit
);
7956 err
= SNPrintF_Add( &dst
, lim
, kLabelPrefix_Alias
"%?{end}-%u", i
== 2, i
- 1 );
7957 require_noerr( err
, exit
);
7959 rdataLabel
[ 0 ] = (uint8_t)( dst
- (char *) &rdataLabel
[ 1 ] );
7960 rdataLen
= 1 + rdataLabel
[ 0 ] + sizeof( superPtr
);
7964 rdataLen
= sizeof( superPtr
);
7969 size_t recordLen
= nameLen
+ sizeof( fields
) + rdataLen
;
7971 if( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
)
7979 // Set CNAME record's NAME.
7981 if( nameOffset
<= kDNSCompressionOffsetMax
)
7983 err
= DataBuffer_Append( inDB
, namePtr
, sizeof( namePtr
) );
7984 require_noerr( err
, exit
);
7988 err
= DataBuffer_Append( inDB
, nameLabel
, 1 + nameLabel
[ 0 ] );
7989 require_noerr( err
, exit
);
7991 err
= DataBuffer_Append( inDB
, superPtr
, sizeof( superPtr
) );
7992 require_noerr( err
, exit
);
7995 // Set CNAME record's TYPE, CLASS, TTL, and RDLENGTH.
7997 aliasTTL
= useAliasTTLs
? aliasTTLs
[ aliasCount
- i
] : me
->defaultTTL
;
7998 dns_fixed_fields_record_init( &fields
, kDNSServiceType_CNAME
, kDNSServiceClass_IN
, aliasTTL
,
7999 (uint16_t) rdataLen
);
8000 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
8001 require_noerr( err
, exit
);
8003 // Save offset of CNAME record's RDATA, which may be used for the name of the next CNAME record.
8005 nameOffset
= DataBuffer_GetLen( inDB
);
8007 // Set CNAME record's RDATA.
8011 err
= DataBuffer_Append( inDB
, rdataLabel
, 1 + rdataLabel
[ 0 ] );
8012 require_noerr( err
, exit
);
8014 err
= DataBuffer_Append( inDB
, superPtr
, sizeof( superPtr
) );
8015 require_noerr( err
, exit
);
8018 namePtr
[ 0 ] = superPtr
[ 0 ];
8019 namePtr
[ 1 ] = superPtr
[ 1 ];
8023 // There are no aliases, so initialize the name compression pointer to point to QNAME.
8025 DNSMessageWriteLabelPointer( namePtr
, kDNSHeaderLength
);
8028 if( ( inQType
== kDNSServiceType_A
) || ( inQType
== kDNSServiceType_AAAA
) )
8030 uint8_t * lsb
; // Pointer to the least significant byte of record data.
8031 size_t recordLen
; // Length of the entire record.
8032 size_t rdataLen
; // Length of record's RDATA.
8033 uint8_t rdata
[ 16 ]; // A buffer that's big enough for either A or AAAA RDATA.
8034 uint8_t randIntegers
[ 255 ]; // Array for random integers in [1, 255].
8035 const int useBadAddrs
= ( me
->badUDPMode
&& !inForTCP
) ? true : false;
8037 if( inQType
== kDNSServiceType_A
)
8039 const uint32_t baseAddrV4
= useBadAddrs
? kDNSServerBadBaseAddrV4
: kDNSServerBaseAddrV4
;
8041 require_quiet( nameHasA
, done
);
8044 WriteBig32( rdata
, baseAddrV4
);
8049 const uint8_t * const baseAddrV6
= useBadAddrs
? kDNSServerBadBaseAddrV6
: kDNSServerBaseAddrV6
;
8051 require_quiet( nameHasAAAA
, done
);
8054 memcpy( rdata
, baseAddrV6
, 16 );
8060 // Populate the array with all integers between 1 and <randCount>, inclusive.
8062 for( i
= 0; i
< randCount
; ++i
) randIntegers
[ i
] = (uint8_t)( i
+ 1 );
8064 // Prevent dubious static analyzer warning.
8065 // Note: _DNSServerNameIsHostname() already enforces randCount >= addrCount. Also, this require_fatal() check
8066 // needs to be placed right before the next for-loop. Any earlier, and the static analyzer warning will persist
8069 require_fatal( addrCount
<= randCount
, "Invalid Count label values: addrCount %u > randCount %u",
8070 addrCount
, randCount
);
8072 // Create a contiguous subarray starting at index 0 that contains <addrCount> randomly chosen integers between
8073 // 1 and <randCount>, inclusive.
8074 // Loop invariant 1: Array elements with indexes in [0, i - 1] have been randomly chosen.
8075 // Loop invariant 2: Array elements with indexes in [i, randCount - 1] are candidates for being chosen.
8077 for( i
= 0; i
< addrCount
; ++i
)
8082 j
= RandomRange( i
, randCount
- 1 );
8085 tmp
= randIntegers
[ i
];
8086 randIntegers
[ i
] = randIntegers
[ j
];
8087 randIntegers
[ j
] = tmp
;
8092 recordLen
= sizeof( namePtr
) + sizeof( fields
) + rdataLen
;
8093 for( i
= 0; i
< addrCount
; ++i
)
8095 if( !inForTCP
&& ( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
) )
8103 err
= DataBuffer_Append( inDB
, namePtr
, sizeof( namePtr
) );
8104 require_noerr( err
, exit
);
8106 // Set record TYPE, CLASS, TTL, and RDLENGTH.
8108 dns_fixed_fields_record_init( &fields
, (uint16_t) inQType
, kDNSServiceClass_IN
, ttl
, (uint16_t) rdataLen
);
8109 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
8110 require_noerr( err
, exit
);
8112 // Set record RDATA.
8114 *lsb
= ( randCount
> 0 ) ? randIntegers
[ i
] : ( *lsb
+ 1 );
8116 err
= DataBuffer_Append( inDB
, rdata
, rdataLen
);
8117 require_noerr( err
, exit
);
8122 else if( inQType
== kDNSServiceType_SRV
)
8124 require_quiet( nameHasSRV
, done
);
8126 dns_fixed_fields_record_init( &fields
, kDNSServiceType_SRV
, kDNSServiceClass_IN
, me
->defaultTTL
, 0 );
8128 for( i
= 0; i
< srvCount
; ++i
)
8130 dns_fixed_fields_srv fieldsSRV
;
8133 const ParsedSRV
* const srv
= &srvArray
[ i
];
8135 rdataLen
= sizeof( fieldsSRV
) + srvDomainLen
+ srv
->targetLen
+ 1;
8136 recordLen
= sizeof( namePtr
) + sizeof( fields
) + rdataLen
;
8138 if( !inForTCP
&& ( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
) )
8144 // Append record NAME.
8146 err
= DataBuffer_Append( inDB
, namePtr
, sizeof( namePtr
) );
8147 require_noerr( err
, exit
);
8149 // Append record TYPE, CLASS, TTL, and RDLENGTH.
8151 WriteBig16( fields
.rdlength
, rdataLen
);
8152 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
8153 require_noerr( err
, exit
);
8155 // Append SRV RDATA.
8157 dns_fixed_fields_srv_init( &fieldsSRV
, srv
->priority
, srv
->weight
, srv
->port
);
8159 err
= DataBuffer_Append( inDB
, &fieldsSRV
, sizeof( fieldsSRV
) );
8160 require_noerr( err
, exit
);
8162 if( srv
->targetLen
> 0 )
8164 err
= DataBuffer_Append( inDB
, srv
->targetPtr
, srv
->targetLen
);
8165 require_noerr( err
, exit
);
8168 if( srvDomainLen
> 0 )
8170 err
= DataBuffer_Append( inDB
, srvDomainPtr
, srvDomainLen
);
8171 require_noerr( err
, exit
);
8174 err
= DataBuffer_Append( inDB
, "", 1 ); // Append root label.
8175 require_noerr( err
, exit
);
8180 else if( inQType
== kDNSServiceType_SOA
)
8182 size_t nameLen
, recordLen
;
8184 require_quiet( nameHasSOA
, done
);
8186 nameLen
= DomainNameLength( me
->domain
);
8189 err
= AppendSOARecord( NULL
, me
->domain
, nameLen
, 0, 0, 0, kRootLabel
, kRootLabel
, 0, 0, 0, 0, 0, &recordLen
);
8190 require_noerr( err
, exit
);
8192 if( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
)
8199 err
= AppendSOARecord( inDB
, me
->domain
, nameLen
, kDNSServiceType_SOA
, kDNSServiceClass_IN
, me
->defaultTTL
,
8200 kRootLabel
, kRootLabel
, me
->serial
, 1 * kSecondsPerDay
, 2 * kSecondsPerHour
, 1000 * kSecondsPerHour
,
8201 me
->defaultTTL
, NULL
);
8202 require_noerr( err
, exit
);
8208 hdr
= (DNSHeader
*) DataBuffer_GetPtr( inDB
);
8209 flags
= DNSHeaderGetFlags( hdr
);
8210 if( notImplemented
)
8212 rcode
= kDNSRCode_NotImplemented
;
8216 flags
|= kDNSHeaderFlag_AuthAnswer
;
8217 if( truncated
) flags
|= kDNSHeaderFlag_Truncation
;
8218 rcode
= nameExists
? kDNSRCode_NoError
: kDNSRCode_NXDomain
;
8220 DNSFlagsSetRCode( flags
, rcode
);
8221 DNSHeaderSetFlags( hdr
, flags
);
8222 DNSHeaderSetAnswerCount( hdr
, answerCount
);
8230 _DNSServerNameIsHostname(
8232 const uint8_t * inName
,
8233 uint32_t * outAliasCount
,
8234 uint32_t inAliasTTLs
[ kMaxAliasTTLCount
],
8235 size_t * outAliasTTLCount
,
8236 unsigned int * outCount
,
8237 unsigned int * outRandCount
,
8240 Boolean
* outHasAAAA
,
8241 Boolean
* outHasSOA
)
8244 const uint8_t * label
;
8245 const uint8_t * nextLabel
;
8246 uint32_t aliasCount
= 0; // Arg from Alias label. Valid values are in [2, 2^31 - 1].
8247 unsigned int count
= 0; // First arg from Count label. Valid values are in [1, 255].
8248 unsigned int randCount
= 0; // Second arg from Count label. Valid values are in [count, 255].
8249 int32_t ttl
= -1; // Arg from TTL label. Valid values are in [0, 2^31 - 1].
8250 size_t aliasTTLCount
= 0; // Count of TTL args from Alias-TTL label.
8251 int hasTagLabel
= false;
8252 int hasIPv4Label
= false;
8253 int hasIPv6Label
= false;
8254 int isNameValid
= false;
8256 for( label
= inName
; label
[ 0 ]; label
= nextLabel
)
8260 nextLabel
= &label
[ 1 + label
[ 0 ] ];
8262 // Check if the first label is a valid alias TTL sequence label.
8264 if( ( label
== inName
) && ( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_AliasTTL
) == 0 ) )
8266 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_AliasTTL
) ];
8267 const char * const end
= (const char *) nextLabel
;
8270 check( label
[ 0 ] <= kDomainLabelLengthMax
);
8274 if( *ptr
!= '-' ) break;
8276 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &next
);
8277 if( err
|| ( arg
> INT32_MAX
) ) break; // TTL must be in [0, 2^31 - 1].
8278 inAliasTTLs
[ aliasTTLCount
++ ] = arg
;
8281 if( ( aliasTTLCount
== 0 ) || ( ptr
!= end
) ) break;
8284 // Check if the first label is a valid alias label.
8286 else if( ( label
== inName
) && ( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_Alias
) == 0 ) )
8288 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_Alias
) ];
8289 const char * const end
= (const char *) nextLabel
;
8293 if( *ptr
++ != '-' ) break;
8294 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8295 if( err
|| ( arg
< 2 ) || ( arg
> INT32_MAX
) ) break; // Alias count must be in [2, 2^31 - 1].
8297 if( ptr
!= end
) break;
8305 // Check if this label is a valid count label.
8307 else if( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_Count
) == 0 )
8309 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_Count
) ];
8310 const char * const end
= (const char *) nextLabel
;
8312 if( count
> 0 ) break; // Count cannot be specified more than once.
8314 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8315 if( err
|| ( arg
< 1 ) || ( arg
> 255 ) ) break; // Count must be in [1, 255].
8316 count
= (unsigned int) arg
;
8320 if( *ptr
++ != '-' ) break;
8321 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8322 if( err
|| ( arg
< (uint32_t) count
) || ( arg
> 255 ) ) break; // Rand count must be in [count, 255].
8323 randCount
= (unsigned int) arg
;
8324 if( ptr
!= end
) break;
8328 // Check if this label is a valid TTL label.
8330 else if( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_TTL
) == 0 )
8332 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_TTL
) ];
8333 const char * const end
= (const char *) nextLabel
;
8335 if( ttl
>= 0 ) break; // TTL cannot be specified more than once.
8337 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8338 if( err
|| ( arg
> INT32_MAX
) ) break; // TTL must be in [0, 2^31 - 1].
8339 ttl
= (int32_t) arg
;
8340 if( ptr
!= end
) break;
8343 // Check if this label is a valid IPv4 label.
8345 else if( strnicmpx( &label
[ 1 ], label
[ 0 ], kLabel_IPv4
) == 0 )
8347 if( hasIPv4Label
|| hasIPv6Label
) break; // Valid names have at most one IPv4 or IPv6 label.
8348 hasIPv4Label
= true;
8351 // Check if this label is a valid IPv6 label.
8353 else if( strnicmpx( &label
[ 1 ], label
[ 0 ], kLabel_IPv6
) == 0 )
8355 if( hasIPv4Label
|| hasIPv6Label
) break; // Valid names have at most one IPv4 or IPv6 label.
8356 hasIPv6Label
= true;
8359 // Check if this label is a valid tag label.
8361 else if( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_Tag
) == 0 )
8366 // If this and the remaining labels are equal to "d.test.", then the name exists. Otherwise, this label is invalid.
8367 // In both cases, there are no more labels to check.
8371 if( DomainNameEqual( label
, me
->domain
) ) isNameValid
= true;
8375 require_quiet( isNameValid
, exit
);
8377 if( outAliasCount
) *outAliasCount
= aliasCount
;
8378 if( outAliasTTLCount
) *outAliasTTLCount
= aliasTTLCount
;
8379 if( outCount
) *outCount
= ( count
> 0 ) ? count
: 1;
8380 if( outRandCount
) *outRandCount
= randCount
;
8381 if( outTTL
) *outTTL
= ( ttl
>= 0 ) ? ( (uint32_t) ttl
) : me
->defaultTTL
;
8382 if( outHasA
) *outHasA
= ( hasIPv4Label
|| !hasIPv6Label
) ? true : false;
8383 if( outHasAAAA
) *outHasAAAA
= ( hasIPv6Label
|| !hasIPv4Label
) ? true : false;
8386 *outHasSOA
= ( !count
&& ( ttl
< 0 ) && !hasIPv4Label
&& !hasIPv6Label
&& !hasTagLabel
) ? true : false;
8390 return( isNameValid
? true : false );
8394 _DNSServerNameIsSRVName(
8396 const uint8_t * inName
,
8397 const uint8_t ** outDomainPtr
,
8398 size_t * outDomainLen
,
8399 ParsedSRV inSRVArray
[ kMaxParsedSRVCount
],
8400 size_t * outSRVCount
)
8403 const uint8_t * label
;
8404 const uint8_t * domainPtr
;
8408 int isNameValid
= false;
8412 // Ensure that first label, i.e, the service label, begins with a '_' character.
8414 require_quiet( ( label
[ 0 ] > 0 ) && ( label
[ 1 ] == '_' ), exit
);
8415 label
= DomainNameGetNextLabel( label
);
8417 // Ensure that the second label, i.e., the proto label, begins with a '_' character (usually _tcp or _udp).
8419 require_quiet( ( label
[ 0 ] > 0 ) && ( label
[ 1 ] == '_' ), exit
);
8420 label
= DomainNameGetNextLabel( label
);
8422 // Parse the domain name, if any.
8427 if( DomainNameEqual( label
, me
->domain
) ||
8428 ( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_SRV
) == 0 ) ) break;
8429 label
= DomainNameGetNextLabel( label
);
8431 require_quiet( *label
, exit
);
8433 domainLen
= (size_t)( label
- domainPtr
);
8435 // Parse SRV labels, if any.
8438 while( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_SRV
) == 0 )
8440 const uint8_t * const nextLabel
= DomainNameGetNextLabel( label
);
8441 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_SRV
) ];
8442 const char * const end
= (const char *) nextLabel
;
8443 const uint8_t * target
;
8444 unsigned int priority
, weight
, port
;
8446 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8447 require_quiet( !err
&& ( arg
<= UINT16_MAX
), exit
);
8448 priority
= (unsigned int) arg
;
8450 require_quiet( ( ptr
< end
) && ( *ptr
== '-' ), exit
);
8453 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8454 require_quiet( !err
&& ( arg
<= UINT16_MAX
), exit
);
8455 weight
= (unsigned int) arg
;
8457 require_quiet( ( ptr
< end
) && ( *ptr
== '-' ), exit
);
8460 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8461 require_quiet( !err
&& ( arg
<= UINT16_MAX
), exit
);
8462 port
= (unsigned int) arg
;
8464 require_quiet( ptr
== end
, exit
);
8467 for( label
= nextLabel
; *label
; label
= DomainNameGetNextLabel( label
) )
8469 if( DomainNameEqual( label
, me
->domain
) ||
8470 ( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_SRV
) == 0 ) ) break;
8472 require_quiet( *label
, exit
);
8476 inSRVArray
[ srvCount
].priority
= (uint16_t) priority
;
8477 inSRVArray
[ srvCount
].weight
= (uint16_t) weight
;
8478 inSRVArray
[ srvCount
].port
= (uint16_t) port
;
8479 inSRVArray
[ srvCount
].targetPtr
= target
;
8480 inSRVArray
[ srvCount
].targetLen
= (uint16_t)( label
- target
);
8484 require_quiet( DomainNameEqual( label
, me
->domain
), exit
);
8487 if( outDomainPtr
) *outDomainPtr
= domainPtr
;
8488 if( outDomainLen
) *outDomainLen
= domainLen
;
8489 if( outSRVCount
) *outSRVCount
= srvCount
;
8492 return( isNameValid
? true : false );
8495 //===========================================================================================================================
8496 // _DNSServerTCPReadHandler
8497 //===========================================================================================================================
8501 DNSServerRef server
; // Reference to DNS server object.
8502 sockaddr_ip clientAddr
; // Client's address.
8503 dispatch_source_t readSource
; // Dispatch read source for client socket.
8504 dispatch_source_t writeSource
; // Dispatch write source for client socket.
8505 size_t offset
; // Offset into receive buffer.
8506 void * msgPtr
; // Pointer to dynamically allocated message buffer.
8507 size_t msgLen
; // Length of message buffer.
8508 Boolean readSuspended
; // True if the read source is currently suspended.
8509 Boolean writeSuspended
; // True if the write source is currently suspended.
8510 Boolean receivedLength
; // True if receiving DNS message as opposed to the message length.
8511 uint8_t lenBuf
[ 2 ]; // Buffer for two-octet message length field.
8512 iovec_t iov
[ 2 ]; // IO vector for writing response message.
8513 iovec_t
* iovPtr
; // Vector pointer for SocketWriteData().
8514 int iovCount
; // Vector count for SocketWriteData().
8516 } TCPConnectionContext
;
8518 static void TCPConnectionStop( TCPConnectionContext
*inContext
);
8519 static void TCPConnectionContextFree( TCPConnectionContext
*inContext
);
8520 static void TCPConnectionReadHandler( void *inContext
);
8521 static void TCPConnectionWriteHandler( void *inContext
);
8523 #define TCPConnectionForget( X ) ForgetCustomEx( X, TCPConnectionStop, TCPConnectionContextFree )
8525 static void _DNSServerTCPReadHandler( void *inContext
)
8528 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
8529 DNSServerRef
const me
= (DNSServerRef
) sockCtx
->userContext
;
8530 TCPConnectionContext
* connection
;
8531 socklen_t clientAddrLen
;
8532 SocketRef newSock
= kInvalidSocketRef
;
8533 SocketContext
* newSockCtx
= NULL
;
8535 connection
= (TCPConnectionContext
*) calloc( 1, sizeof( *connection
) );
8536 require_action( connection
, exit
, err
= kNoMemoryErr
);
8539 connection
->server
= me
;
8541 clientAddrLen
= (socklen_t
) sizeof( connection
->clientAddr
);
8542 newSock
= accept( sockCtx
->sock
, &connection
->clientAddr
.sa
, &clientAddrLen
);
8543 err
= map_socket_creation_errno( newSock
);
8544 require_noerr( err
, exit
);
8546 err
= SocketContextCreate( newSock
, connection
, &newSockCtx
);
8547 require_noerr( err
, exit
);
8548 newSock
= kInvalidSocketRef
;
8550 err
= DispatchReadSourceCreate( newSockCtx
->sock
, me
->queue
, TCPConnectionReadHandler
, SocketContextCancelHandler
,
8551 newSockCtx
, &connection
->readSource
);
8552 require_noerr( err
, exit
);
8553 SocketContextRetain( newSockCtx
);
8554 dispatch_resume( connection
->readSource
);
8556 err
= DispatchWriteSourceCreate( newSockCtx
->sock
, me
->queue
, TCPConnectionWriteHandler
, SocketContextCancelHandler
,
8557 newSockCtx
, &connection
->writeSource
);
8558 require_noerr( err
, exit
);
8559 SocketContextRetain( newSockCtx
);
8560 connection
->writeSuspended
= true;
8564 ForgetSocket( &newSock
);
8565 SocketContextRelease( newSockCtx
);
8566 TCPConnectionForget( &connection
);
8569 //===========================================================================================================================
8570 // TCPConnectionStop
8571 //===========================================================================================================================
8573 static void TCPConnectionStop( TCPConnectionContext
*inContext
)
8575 dispatch_source_forget_ex( &inContext
->readSource
, &inContext
->readSuspended
);
8576 dispatch_source_forget_ex( &inContext
->writeSource
, &inContext
->writeSuspended
);
8579 //===========================================================================================================================
8580 // TCPConnectionContextFree
8581 //===========================================================================================================================
8583 static void TCPConnectionContextFree( TCPConnectionContext
*inContext
)
8585 check( !inContext
->readSource
);
8586 check( !inContext
->writeSource
);
8587 ForgetCF( &inContext
->server
);
8588 ForgetMem( &inContext
->msgPtr
);
8592 //===========================================================================================================================
8593 // TCPConnectionReadHandler
8594 //===========================================================================================================================
8596 static void TCPConnectionReadHandler( void *inContext
)
8599 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
8600 TCPConnectionContext
* connection
= (TCPConnectionContext
*) sockCtx
->userContext
;
8602 uint8_t * responsePtr
= NULL
; // malloc'd
8605 // Receive message length.
8607 if( !connection
->receivedLength
)
8609 err
= SocketReadData( sockCtx
->sock
, connection
->lenBuf
, sizeof( connection
->lenBuf
), &connection
->offset
);
8610 if( err
== EWOULDBLOCK
) goto exit
;
8611 require_noerr( err
, exit
);
8613 connection
->offset
= 0;
8614 connection
->msgLen
= ReadBig16( connection
->lenBuf
);
8615 connection
->msgPtr
= malloc( connection
->msgLen
);
8616 require_action( connection
->msgPtr
, exit
, err
= kNoMemoryErr
);
8617 connection
->receivedLength
= true;
8622 err
= SocketReadData( sockCtx
->sock
, connection
->msgPtr
, connection
->msgLen
, &connection
->offset
);
8623 if( err
== EWOULDBLOCK
) goto exit
;
8624 require_noerr( err
, exit
);
8626 gettimeofday( &now
, NULL
);
8627 dispatch_suspend( connection
->readSource
);
8628 connection
->readSuspended
= true;
8630 ds_ulog( kLogLevelInfo
, "TCP server received %zu bytes from %##a at %{du:time}.\n",
8631 connection
->msgLen
, &connection
->clientAddr
, &now
);
8633 if( connection
->msgLen
< kDNSHeaderLength
)
8635 ds_ulog( kLogLevelInfo
, "TCP DNS message is too small (%zu < %d).\n", connection
->msgLen
, kDNSHeaderLength
);
8639 ds_ulog( kLogLevelInfo
, "TCP received message:\n\n%1{du:dnsmsg}", connection
->msgPtr
, connection
->msgLen
);
8643 err
= _DNSServerAnswerQueryForTCP( connection
->server
, connection
->msgPtr
, connection
->msgLen
, &responsePtr
,
8645 require_noerr_quiet( err
, exit
);
8649 ds_ulog( kLogLevelInfo
, "TCP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen
, responsePtr
, responseLen
);
8651 free( connection
->msgPtr
);
8652 connection
->msgPtr
= responsePtr
;
8653 connection
->msgLen
= responseLen
;
8656 check( connection
->msgLen
<= UINT16_MAX
);
8657 WriteBig16( connection
->lenBuf
, connection
->msgLen
);
8658 connection
->iov
[ 0 ].iov_base
= connection
->lenBuf
;
8659 connection
->iov
[ 0 ].iov_len
= sizeof( connection
->lenBuf
);
8660 connection
->iov
[ 1 ].iov_base
= connection
->msgPtr
;
8661 connection
->iov
[ 1 ].iov_len
= connection
->msgLen
;
8663 connection
->iovPtr
= connection
->iov
;
8664 connection
->iovCount
= 2;
8666 check( connection
->writeSuspended
);
8667 dispatch_resume( connection
->writeSource
);
8668 connection
->writeSuspended
= false;
8671 FreeNullSafe( responsePtr
);
8672 if( err
&& ( err
!= EWOULDBLOCK
) ) TCPConnectionForget( &connection
);
8675 //===========================================================================================================================
8676 // TCPConnectionWriteHandler
8677 //===========================================================================================================================
8679 static void TCPConnectionWriteHandler( void *inContext
)
8682 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
8683 TCPConnectionContext
* connection
= (TCPConnectionContext
*) sockCtx
->userContext
;
8685 err
= SocketWriteData( sockCtx
->sock
, &connection
->iovPtr
, &connection
->iovCount
);
8686 if( err
== EWOULDBLOCK
) goto exit
;
8689 TCPConnectionForget( &connection
);
8695 //===========================================================================================================================
8697 //===========================================================================================================================
8701 uint8_t * hostname
; // Used as the base name for hostnames and service names.
8702 uint8_t * serviceLabel
; // Label containing the base service name.
8703 unsigned int maxInstanceCount
; // Maximum number of service instances and hostnames.
8704 uint64_t * bitmaps
; // Array of 64-bit bitmaps for keeping track of needed responses.
8705 size_t bitmapCount
; // Number of 64-bit bitmaps.
8706 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
8707 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
8708 uint32_t ifIndex
; // Index of the interface to run on.
8709 unsigned int recordCountA
; // Number of A records per hostname.
8710 unsigned int recordCountAAAA
; // Number of AAAA records per hostname.
8711 unsigned int maxDropCount
; // If > 0, the drop rates apply to only the first <maxDropCount> responses.
8712 double ucastDropRate
; // Probability of dropping a unicast response.
8713 double mcastDropRate
; // Probability of dropping a multicast query or response.
8714 uint8_t * dropCounters
; // If maxDropCount > 0, array of <maxInstanceCount> response drop counters.
8715 Boolean noAdditionals
; // True if responses are to not include additional records.
8716 Boolean useIPv4
; // True if the replier is to use IPv4.
8717 Boolean useIPv6
; // True if the replier is to use IPv6.
8718 uint8_t msgBuf
[ kMDNSMessageSizeMax
]; // Buffer for received mDNS message.
8719 #if( TARGET_OS_DARWIN )
8720 dispatch_source_t processMonitor
; // Process monitor source for process being followed, if any.
8721 pid_t followPID
; // PID of process being followed, if any. (If it exits, we exit).
8724 } MDNSReplierContext
;
8726 typedef struct MRResourceRecord MRResourceRecord
;
8727 struct MRResourceRecord
8729 MRResourceRecord
* next
; // Next item in list.
8730 uint8_t * name
; // Resource record name.
8731 uint16_t type
; // Resource record type.
8732 uint16_t class; // Resource record class.
8733 uint32_t ttl
; // Resource record TTL.
8734 uint16_t rdlength
; // Resource record data length.
8735 uint8_t * rdata
; // Resource record data.
8736 const uint8_t * target
; // For SRV records, pointer to target in RDATA.
8739 typedef struct MRNameOffsetItem MRNameOffsetItem
;
8740 struct MRNameOffsetItem
8742 MRNameOffsetItem
* next
; // Next item in list.
8743 uint16_t offset
; // Offset of domain name in response message.
8744 uint8_t name
[ 1 ]; // Variable-length array for domain name.
8747 #if( TARGET_OS_DARWIN )
8748 static void _MDNSReplierFollowedProcessHandler( void *inContext
);
8750 static void _MDNSReplierReadHandler( void *inContext
);
8752 _MDNSReplierAnswerQuery(
8753 MDNSReplierContext
* inContext
,
8754 const uint8_t * inQueryPtr
,
8756 sockaddr_ip
* inSender
,
8758 unsigned int inIndex
);
8760 _MDNSReplierAnswerListAdd(
8761 MDNSReplierContext
* inContext
,
8762 MRResourceRecord
** inAnswerList
,
8763 unsigned int inIndex
,
8764 const uint8_t * inName
,
8765 unsigned int inType
,
8766 unsigned int inClass
);
8768 _MDNSReplierAnswerListRemovePTR(
8769 MRResourceRecord
** inAnswerListPtr
,
8770 const uint8_t * inName
,
8771 const uint8_t * inRData
);
8773 _MDNSReplierSendOrDropResponse(
8774 MDNSReplierContext
* inContext
,
8775 MRResourceRecord
* inAnswerList
,
8776 sockaddr_ip
* inQuerier
,
8778 unsigned int inIndex
,
8779 Boolean inUnicast
);
8781 _MDNSReplierCreateResponse(
8782 MDNSReplierContext
* inContext
,
8783 MRResourceRecord
* inAnswerList
,
8784 unsigned int inIndex
,
8785 uint8_t ** outResponsePtr
,
8786 size_t * outResponseLen
);
8788 _MDNSReplierAppendNameToResponse(
8789 DataBuffer
* inResponse
,
8790 const uint8_t * inName
,
8791 MRNameOffsetItem
** inNameOffsetListPtr
);
8793 _MDNSReplierServiceTypeMatch(
8794 const MDNSReplierContext
* inContext
,
8795 const uint8_t * inName
,
8796 unsigned int * outTXTSize
,
8797 unsigned int * outCount
);
8799 _MDNSReplierServiceInstanceNameMatch(
8800 const MDNSReplierContext
* inContext
,
8801 const uint8_t * inName
,
8802 unsigned int * outIndex
,
8803 unsigned int * outTXTSize
,
8804 unsigned int * outCount
);
8805 static Boolean
_MDNSReplierAboutRecordNameMatch( const MDNSReplierContext
*inContext
, const uint8_t *inName
);
8807 _MDNSReplierHostnameMatch(
8808 const MDNSReplierContext
* inContext
,
8809 const uint8_t * inName
,
8810 unsigned int * outIndex
);
8811 static OSStatus
_MDNSReplierCreateTXTRecord( const uint8_t *inRecordName
, size_t inSize
, uint8_t **outTXT
);
8813 _MRResourceRecordCreate(
8818 uint16_t inRDLength
,
8820 MRResourceRecord
** outRecord
);
8821 static void _MRResourceRecordFree( MRResourceRecord
*inRecord
);
8822 static void _MRResourceRecordFreeList( MRResourceRecord
*inList
);
8823 static OSStatus
_MRNameOffsetItemCreate( const uint8_t *inName
, uint16_t inOffset
, MRNameOffsetItem
**outItem
);
8824 static void _MRNameOffsetItemFree( MRNameOffsetItem
*inItem
);
8825 static void _MRNameOffsetItemFreeList( MRNameOffsetItem
*inList
);
8827 ulog_define_ex( kDNSSDUtilIdentifier
, MDNSReplier
, kLogLevelInfo
, kLogFlags_None
, "MDNSReplier", NULL
);
8828 #define mr_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSReplier ), (LEVEL), __VA_ARGS__ )
8830 static void MDNSReplierCmd( void )
8833 MDNSReplierContext
* context
;
8834 SocketRef sockV4
= kInvalidSocketRef
;
8835 SocketRef sockV6
= kInvalidSocketRef
;
8836 const char * ifname
;
8838 uint8_t name
[ 1 + kDomainLabelLengthMax
+ 1 ];
8839 char ifnameBuf
[ IF_NAMESIZE
+ 1 ];
8841 err
= CheckIntegerArgument( gMDNSReplier_MaxInstanceCount
, "max instance count", 1, UINT16_MAX
);
8842 require_noerr_quiet( err
, exit
);
8844 err
= CheckIntegerArgument( gMDNSReplier_RecordCountA
, "A record count", 0, 255 );
8845 require_noerr_quiet( err
, exit
);
8847 err
= CheckIntegerArgument( gMDNSReplier_RecordCountAAAA
, "AAAA record count", 0, 255 );
8848 require_noerr_quiet( err
, exit
);
8850 err
= CheckDoubleArgument( gMDNSReplier_UnicastDropRate
, "unicast drop rate", 0.0, 1.0 );
8851 require_noerr_quiet( err
, exit
);
8853 err
= CheckDoubleArgument( gMDNSReplier_MulticastDropRate
, "multicast drop rate", 0.0, 1.0 );
8854 require_noerr_quiet( err
, exit
);
8856 err
= CheckIntegerArgument( gMDNSReplier_MaxDropCount
, "drop count", 0, 255 );
8857 require_noerr_quiet( err
, exit
);
8859 if( gMDNSReplier_Foreground
)
8861 LogControl( "MDNSReplier:output=file;stdout,MDNSReplier:flags=time;prefix" );
8864 context
= (MDNSReplierContext
*) calloc( 1, sizeof( *context
) );
8865 require_action( context
, exit
, err
= kNoMemoryErr
);
8867 context
->maxInstanceCount
= (unsigned int) gMDNSReplier_MaxInstanceCount
;
8868 context
->recordCountA
= (unsigned int) gMDNSReplier_RecordCountA
;
8869 context
->recordCountAAAA
= (unsigned int) gMDNSReplier_RecordCountAAAA
;
8870 context
->maxDropCount
= (unsigned int) gMDNSReplier_MaxDropCount
;
8871 context
->ucastDropRate
= gMDNSReplier_UnicastDropRate
;
8872 context
->mcastDropRate
= gMDNSReplier_MulticastDropRate
;
8873 context
->noAdditionals
= gMDNSReplier_NoAdditionals
? true : false;
8874 context
->useIPv4
= ( gMDNSReplier_UseIPv4
|| !gMDNSReplier_UseIPv6
) ? true : false;
8875 context
->useIPv6
= ( gMDNSReplier_UseIPv6
|| !gMDNSReplier_UseIPv4
) ? true : false;
8876 context
->bitmapCount
= ( context
->maxInstanceCount
+ 63 ) / 64;
8878 #if( TARGET_OS_DARWIN )
8879 if( gMDNSReplier_FollowPID
)
8881 context
->followPID
= _StringToPID( gMDNSReplier_FollowPID
, &err
);
8882 if( err
|| ( context
->followPID
< 0 ) )
8884 FPrintF( stderr
, "error: Invalid follow PID: %s\n", gMDNSReplier_FollowPID
);
8888 err
= DispatchProcessMonitorCreate( context
->followPID
, DISPATCH_PROC_EXIT
, dispatch_get_main_queue(),
8889 _MDNSReplierFollowedProcessHandler
, NULL
, context
, &context
->processMonitor
);
8890 require_noerr( err
, exit
);
8891 dispatch_resume( context
->processMonitor
);
8895 context
->followPID
= -1;
8899 if( context
->maxDropCount
> 0 )
8901 context
->dropCounters
= (uint8_t *) calloc( context
->maxInstanceCount
, sizeof( *context
->dropCounters
) );
8902 require_action( context
->dropCounters
, exit
, err
= kNoMemoryErr
);
8905 context
->bitmaps
= (uint64_t *) calloc( context
->bitmapCount
, sizeof( *context
->bitmaps
) );
8906 require_action( context
->bitmaps
, exit
, err
= kNoMemoryErr
);
8908 // Create the base hostname label.
8910 len
= strlen( gMDNSReplier_Hostname
);
8911 if( context
->maxInstanceCount
> 1 )
8913 unsigned int maxInstanceCount
, digitCount
;
8915 // When there's more than one instance, extra bytes are needed to append " (<instance index>)" or
8916 // "-<instance index>" to the base hostname.
8918 maxInstanceCount
= context
->maxInstanceCount
;
8919 for( digitCount
= 0; maxInstanceCount
> 0; ++digitCount
) maxInstanceCount
/= 10;
8920 len
+= ( 3 + digitCount
);
8923 if( len
<= kDomainLabelLengthMax
)
8925 uint8_t * dst
= &name
[ 1 ];
8926 uint8_t * lim
= &name
[ countof( name
) ];
8928 SNPrintF_Add( (char **) &dst
, (char *) lim
, "%s", gMDNSReplier_Hostname
);
8929 name
[ 0 ] = (uint8_t)( dst
- &name
[ 1 ] );
8931 err
= DomainNameDupLower( name
, &context
->hostname
, NULL
);
8932 require_noerr( err
, exit
);
8936 FPrintF( stderr
, "error: Base name \"%s\" is too long for max instance count of %u.\n",
8937 gMDNSReplier_Hostname
, context
->maxInstanceCount
);
8941 // Create the service label.
8943 len
= strlen( gMDNSReplier_ServiceTypeTag
) + 3; // We need three extra bytes for the service type prefix "_t-".
8944 if( len
<= kDomainLabelLengthMax
)
8946 uint8_t * dst
= &name
[ 1 ];
8947 uint8_t * lim
= &name
[ countof( name
) ];
8949 SNPrintF_Add( (char **) &dst
, (char *) lim
, "_t-%s", gMDNSReplier_ServiceTypeTag
);
8950 name
[ 0 ] = (uint8_t)( dst
- &name
[ 1 ] );
8952 err
= DomainNameDupLower( name
, &context
->serviceLabel
, NULL
);
8953 require_noerr( err
, exit
);
8957 FPrintF( stderr
, "error: Service type tag is too long.\n" );
8961 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
8962 require_noerr_quiet( err
, exit
);
8964 ifname
= if_indextoname( context
->ifIndex
, ifnameBuf
);
8965 require_action( ifname
, exit
, err
= kNameErr
);
8967 // Set up IPv4 socket.
8969 if( context
->useIPv4
)
8971 err
= CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort
, ifname
, context
->ifIndex
, true, NULL
, &sockV4
);
8972 require_noerr( err
, exit
);
8975 // Set up IPv6 socket.
8977 if( context
->useIPv6
)
8979 err
= CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort
, ifname
, context
->ifIndex
, true, NULL
, &sockV6
);
8980 require_noerr( err
, exit
);
8983 // Create dispatch read sources for socket(s).
8985 if( IsValidSocket( sockV4
) )
8987 SocketContext
* sockCtx
;
8989 err
= SocketContextCreate( sockV4
, context
, &sockCtx
);
8990 require_noerr( err
, exit
);
8991 sockV4
= kInvalidSocketRef
;
8993 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, _MDNSReplierReadHandler
, SocketContextCancelHandler
, sockCtx
,
8994 &context
->readSourceV4
);
8995 if( err
) ForgetSocketContext( &sockCtx
);
8996 require_noerr( err
, exit
);
8998 dispatch_resume( context
->readSourceV4
);
9001 if( IsValidSocket( sockV6
) )
9003 SocketContext
* sockCtx
;
9005 err
= SocketContextCreate( sockV6
, context
, &sockCtx
);
9006 require_noerr( err
, exit
);
9007 sockV6
= kInvalidSocketRef
;
9009 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, _MDNSReplierReadHandler
, SocketContextCancelHandler
, sockCtx
,
9010 &context
->readSourceV6
);
9011 if( err
) ForgetSocketContext( &sockCtx
);
9012 require_noerr( err
, exit
);
9014 dispatch_resume( context
->readSourceV6
);
9020 ForgetSocket( &sockV4
);
9021 ForgetSocket( &sockV6
);
9025 #if( TARGET_OS_DARWIN )
9026 //===========================================================================================================================
9027 // _MDNSReplierFollowedProcessHandler
9028 //===========================================================================================================================
9030 static void _MDNSReplierFollowedProcessHandler( void *inContext
)
9032 MDNSReplierContext
* const context
= (MDNSReplierContext
*) inContext
;
9034 if( dispatch_source_get_data( context
->processMonitor
) & DISPATCH_PROC_EXIT
)
9036 mr_ulog( kLogLevelNotice
, "Exiting: followed process (%lld) exited.\n", (int64_t) context
->followPID
);
9042 //===========================================================================================================================
9043 // _MDNSReplierReadHandler
9044 //===========================================================================================================================
9046 #define ShouldDrop( P ) ( ( (P) > 0.0 ) && ( ( (P) >= 1.0 ) || RandomlyTrue( P ) ) )
9048 static void _MDNSReplierReadHandler( void *inContext
)
9051 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
9052 MDNSReplierContext
* const context
= (MDNSReplierContext
*) sockCtx
->userContext
;
9055 const DNSHeader
* hdr
;
9056 unsigned int flags
, questionCount
, i
, j
;
9057 const uint8_t * ptr
;
9058 int drop
, isMetaQuery
;
9060 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &msgLen
, &sender
, sizeof( sender
),
9061 NULL
, NULL
, NULL
, NULL
);
9062 require_noerr( err
, exit
);
9064 if( msgLen
< kDNSHeaderLength
)
9066 mr_ulog( kLogLevelInfo
, "Message is too small (%zu < %d).\n", msgLen
, kDNSHeaderLength
);
9070 // Perform header field checks.
9071 // The message ID and most flag bits are silently ignored (see <https://tools.ietf.org/html/rfc6762#section-18>).
9073 hdr
= (DNSHeader
*) context
->msgBuf
;
9074 flags
= DNSHeaderGetFlags( hdr
);
9075 require_quiet( ( flags
& kDNSHeaderFlag_Response
) == 0, exit
); // Reject responses.
9076 require_quiet( DNSFlagsGetOpCode( flags
) == kDNSOpCode_Query
, exit
); // Reject opcodes other than standard query.
9077 require_quiet( DNSFlagsGetRCode( flags
) == kDNSRCode_NoError
, exit
); // Reject non-zero rcodes.
9079 drop
= ( !context
->maxDropCount
&& ShouldDrop( context
->mcastDropRate
) ) ? true : false;
9081 mr_ulog( kLogLevelInfo
, "Received %zu byte message from %##a%?s:\n\n%#1{du:dnsmsg}",
9082 msgLen
, &sender
, drop
, " (dropping)", context
->msgBuf
, msgLen
);
9084 // Based on the QNAMEs in the query message, determine from which sets of records we may possibly need answers.
9086 questionCount
= DNSHeaderGetQuestionCount( hdr
);
9087 require_quiet( questionCount
> 0, exit
);
9089 memset( context
->bitmaps
, 0, context
->bitmapCount
* sizeof_element( context
->bitmaps
) );
9091 isMetaQuery
= false;
9092 ptr
= (const uint8_t *) &hdr
[ 1 ];
9093 for( i
= 0; i
< questionCount
; ++i
)
9095 unsigned int count
, index
;
9096 uint16_t qtype
, qclass
;
9097 uint8_t qname
[ kDomainNameLengthMax
];
9099 err
= DNSMessageExtractQuestion( context
->msgBuf
, msgLen
, ptr
, qname
, &qtype
, &qclass
, &ptr
);
9100 require_noerr_quiet( err
, exit
);
9102 if( ( qclass
& ~kQClassUnicastResponseBit
) != kDNSServiceClass_IN
) continue;
9104 if( _MDNSReplierHostnameMatch( context
, qname
, &index
) ||
9105 _MDNSReplierServiceInstanceNameMatch( context
, qname
, &index
, NULL
, NULL
) )
9107 if( ( index
>= 1 ) && ( index
<= context
->maxInstanceCount
) )
9109 context
->bitmaps
[ ( index
- 1 ) / 64 ] |= ( UINT64_C( 1 ) << ( ( index
- 1 ) % 64 ) );
9112 else if( _MDNSReplierServiceTypeMatch( context
, qname
, NULL
, &count
) )
9114 if( ( count
>= 1 ) && ( count
<= context
->maxInstanceCount
) )
9116 for( j
= 0; j
< (unsigned int) context
->bitmapCount
; ++j
)
9120 context
->bitmaps
[ j
] |= ( ( UINT64_C( 1 ) << count
) - 1 );
9125 context
->bitmaps
[ j
] = ~UINT64_C( 0 );
9131 else if( _MDNSReplierAboutRecordNameMatch( context
, qname
) )
9137 // Attempt to answer the query message using selected record sets.
9141 err
= _MDNSReplierAnswerQuery( context
, context
->msgBuf
, msgLen
, &sender
, sockCtx
->sock
, 0 );
9144 if( drop
) goto exit
;
9146 for( i
= 0; i
< context
->bitmapCount
; ++i
)
9148 for( j
= 0; ( context
->bitmaps
[ i
] != 0 ) && ( j
< 64 ); ++j
)
9150 const uint64_t bitmask
= UINT64_C( 1 ) << j
;
9152 if( context
->bitmaps
[ i
] & bitmask
)
9154 context
->bitmaps
[ i
] &= ~bitmask
;
9156 err
= _MDNSReplierAnswerQuery( context
, context
->msgBuf
, msgLen
, &sender
, sockCtx
->sock
,
9157 ( i
* 64 ) + j
+ 1 );
9167 //===========================================================================================================================
9168 // _MDNSReplierAnswerQuery
9169 //===========================================================================================================================
9172 _MDNSReplierAnswerQuery(
9173 MDNSReplierContext
* inContext
,
9174 const uint8_t * inQueryPtr
,
9176 sockaddr_ip
* inSender
,
9178 unsigned int inIndex
)
9181 const DNSHeader
* hdr
;
9182 const uint8_t * ptr
;
9183 unsigned int questionCount
, answerCount
, i
;
9184 MRResourceRecord
* ucastAnswerList
= NULL
;
9185 MRResourceRecord
* mcastAnswerList
= NULL
;
9187 require_action( inIndex
<= inContext
->maxInstanceCount
, exit
, err
= kRangeErr
);
9189 // Get answers for questions.
9191 check( inQueryLen
>= kDNSHeaderLength
);
9192 hdr
= (const DNSHeader
*) inQueryPtr
;
9193 questionCount
= DNSHeaderGetQuestionCount( hdr
);
9195 ptr
= (const uint8_t *) &hdr
[ 1 ];
9196 for( i
= 0; i
< questionCount
; ++i
)
9198 MRResourceRecord
** answerListPtr
;
9199 uint16_t qtype
, qclass
;
9200 uint8_t qname
[ kDomainNameLengthMax
];
9202 err
= DNSMessageExtractQuestion( inQueryPtr
, inQueryLen
, ptr
, qname
, &qtype
, &qclass
, &ptr
);
9203 require_noerr_quiet( err
, exit
);
9205 if( qclass
& kQClassUnicastResponseBit
)
9207 qclass
&= ~kQClassUnicastResponseBit
;
9208 answerListPtr
= &ucastAnswerList
;
9212 answerListPtr
= &mcastAnswerList
;
9215 err
= _MDNSReplierAnswerListAdd( inContext
, answerListPtr
, inIndex
, qname
, qtype
, qclass
);
9216 require_noerr( err
, exit
);
9218 require_action_quiet( mcastAnswerList
|| ucastAnswerList
, exit
, err
= kNoErr
);
9220 // Suppress known answers.
9221 // Records in the Answer section of the query message are known answers, so remove them from the answer lists.
9222 // See <https://tools.ietf.org/html/rfc6762#section-7.1>.
9224 answerCount
= DNSHeaderGetAnswerCount( hdr
);
9225 for( i
= 0; i
< answerCount
; ++i
)
9227 const uint8_t * rdataPtr
;
9228 const uint8_t * recordPtr
;
9229 uint16_t type
, class;
9230 uint8_t name
[ kDomainNameLengthMax
];
9231 uint8_t instance
[ kDomainNameLengthMax
];
9234 err
= DNSMessageExtractRecord( inQueryPtr
, inQueryLen
, ptr
, NULL
, &type
, &class, NULL
, NULL
, NULL
, &ptr
);
9235 require_noerr_quiet( err
, exit
);
9237 if( ( type
!= kDNSServiceType_PTR
) || ( class != kDNSServiceClass_IN
) ) continue;
9239 err
= DNSMessageExtractRecord( inQueryPtr
, inQueryLen
, recordPtr
, name
, NULL
, NULL
, NULL
, &rdataPtr
, NULL
, NULL
);
9240 require_noerr( err
, exit
);
9242 err
= DNSMessageExtractDomainName( inQueryPtr
, inQueryLen
, rdataPtr
, instance
, NULL
);
9243 require_noerr_quiet( err
, exit
);
9245 if( ucastAnswerList
) _MDNSReplierAnswerListRemovePTR( &ucastAnswerList
, name
, instance
);
9246 if( mcastAnswerList
) _MDNSReplierAnswerListRemovePTR( &mcastAnswerList
, name
, instance
);
9248 require_action_quiet( mcastAnswerList
|| ucastAnswerList
, exit
, err
= kNoErr
);
9250 // Send or drop responses.
9252 if( ucastAnswerList
)
9254 err
= _MDNSReplierSendOrDropResponse( inContext
, ucastAnswerList
, inSender
, inSock
, inIndex
, true );
9255 require_noerr( err
, exit
);
9258 if( mcastAnswerList
)
9260 err
= _MDNSReplierSendOrDropResponse( inContext
, mcastAnswerList
, inSender
, inSock
, inIndex
, false );
9261 require_noerr( err
, exit
);
9266 _MRResourceRecordFreeList( ucastAnswerList
);
9267 _MRResourceRecordFreeList( mcastAnswerList
);
9271 //===========================================================================================================================
9272 // _MDNSReplierAnswerListAdd
9273 //===========================================================================================================================
9276 _MDNSReplierAnswerListAdd(
9277 MDNSReplierContext
* inContext
,
9278 MRResourceRecord
** inAnswerList
,
9279 unsigned int inIndex
,
9280 const uint8_t * inName
,
9281 unsigned int inType
,
9282 unsigned int inClass
)
9285 uint8_t * recordName
= NULL
;
9286 uint8_t * rdataPtr
= NULL
;
9288 MRResourceRecord
* answer
;
9289 MRResourceRecord
** answerPtr
;
9290 const uint8_t * const hostname
= inContext
->hostname
;
9293 unsigned int count
, txtSize
;
9295 require_action( inIndex
<= inContext
->maxInstanceCount
, exit
, err
= kRangeErr
);
9296 require_action_quiet( inClass
== kDNSServiceClass_IN
, exit
, err
= kNoErr
);
9298 for( answerPtr
= inAnswerList
; ( answer
= *answerPtr
) != NULL
; answerPtr
= &answer
->next
)
9300 if( ( answer
->type
== inType
) && DomainNameEqual( answer
->name
, inName
) )
9307 // Index 0 is reserved for answering queries about the mdnsreplier, while all other index values up to the maximum
9308 // instance count are for answering queries about service instances.
9312 if( _MDNSReplierAboutRecordNameMatch( inContext
, inName
) )
9314 int listHasTXT
= false;
9316 if( inType
== kDNSServiceType_ANY
)
9318 for( answer
= *inAnswerList
; answer
; answer
= answer
->next
)
9320 if( ( answer
->type
== kDNSServiceType_TXT
) && DomainNameEqual( answer
->name
, inName
) )
9328 if( ( inType
== kDNSServiceType_TXT
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasTXT
) )
9330 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9331 require_noerr( err
, exit
);
9333 err
= CreateTXTRecordDataFromString( "ready=yes", ',', &rdataPtr
, &rdataLen
);
9334 require_noerr( err
, exit
);
9336 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
, kMDNSRecordTTL_Other
,
9337 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9338 require_noerr( err
, exit
);
9342 *answerPtr
= answer
;
9344 else if( inType
== kDNSServiceType_NSEC
)
9346 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9347 require_noerr( err
, exit
);
9349 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 1, kDNSServiceType_TXT
);
9350 require_noerr( err
, exit
);
9352 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_NSEC
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9353 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9354 require_noerr( err
, exit
);
9358 *answerPtr
= answer
;
9362 else if( _MDNSReplierHostnameMatch( inContext
, inName
, &index
) && ( index
== inIndex
) )
9364 int listHasA
= false;
9365 int listHasAAAA
= false;
9367 if( inType
== kDNSServiceType_ANY
)
9369 for( answer
= *inAnswerList
; answer
; answer
= answer
->next
)
9371 if( answer
->type
== kDNSServiceType_A
)
9373 if( !listHasA
&& DomainNameEqual( answer
->name
, inName
) ) listHasA
= true;
9375 else if( answer
->type
== kDNSServiceType_AAAA
)
9377 if( !listHasAAAA
&& DomainNameEqual( answer
->name
, inName
) ) listHasAAAA
= true;
9379 if( listHasA
&& listHasAAAA
) break;
9383 if( ( inType
== kDNSServiceType_A
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasA
) )
9385 for( i
= 1; i
<= inContext
->recordCountA
; ++i
)
9387 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9388 require_noerr( err
, exit
);
9391 rdataPtr
= (uint8_t *) malloc( rdataLen
);
9392 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9395 WriteBig16( &rdataPtr
[ 1 ], inIndex
);
9396 rdataPtr
[ 3 ] = (uint8_t) i
;
9398 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_A
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9399 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9400 require_noerr( err
, exit
);
9404 *answerPtr
= answer
;
9405 answerPtr
= &answer
->next
;
9409 if( ( inType
== kDNSServiceType_AAAA
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasAAAA
) )
9411 for( i
= 1; i
<= inContext
->recordCountAAAA
; ++i
)
9413 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9414 require_noerr( err
, exit
);
9417 rdataPtr
= (uint8_t *) _memdup( kMDNSReplierBaseAddrV6
, rdataLen
);
9418 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9420 WriteBig16( &rdataPtr
[ 12 ], inIndex
);
9421 rdataPtr
[ 15 ] = (uint8_t) i
;
9423 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_AAAA
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9424 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9425 require_noerr( err
, exit
);
9429 *answerPtr
= answer
;
9430 answerPtr
= &answer
->next
;
9433 else if( inType
== kDNSServiceType_NSEC
)
9435 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9436 require_noerr( err
, exit
);
9438 if( ( inContext
->recordCountA
> 0 ) && ( inContext
->recordCountAAAA
> 0 ) )
9440 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 2, kDNSServiceType_A
, kDNSServiceType_AAAA
);
9441 require_noerr( err
, exit
);
9443 else if( inContext
->recordCountA
> 0 )
9445 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 1, kDNSServiceType_A
);
9446 require_noerr( err
, exit
);
9448 else if( inContext
->recordCountAAAA
> 0 )
9450 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 1, kDNSServiceType_AAAA
);
9451 require_noerr( err
, exit
);
9455 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 0 );
9456 require_noerr( err
, exit
);
9459 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_NSEC
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9460 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9461 require_noerr( err
, exit
);
9465 *answerPtr
= answer
;
9468 else if( _MDNSReplierServiceTypeMatch( inContext
, inName
, NULL
, &count
) && ( count
>= inIndex
) )
9470 int listHasPTR
= false;
9472 if( inType
== kDNSServiceType_ANY
)
9474 for( answer
= *inAnswerList
; answer
; answer
= answer
->next
)
9476 if( ( answer
->type
== kDNSServiceType_PTR
) && DomainNameEqual( answer
->name
, inName
) )
9484 if( ( inType
== kDNSServiceType_PTR
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasPTR
) )
9486 size_t recordNameLen
;
9490 err
= DomainNameDupLower( inName
, &recordName
, &recordNameLen
);
9491 require_noerr( err
, exit
);
9493 rdataLen
= 1 + hostname
[ 0 ] + 10 + recordNameLen
;
9494 rdataPtr
= (uint8_t *) malloc( rdataLen
);
9495 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9497 lim
= &rdataPtr
[ rdataLen
];
9499 ptr
= &rdataPtr
[ 1 ];
9500 memcpy( ptr
, &hostname
[ 1 ], hostname
[ 0 ] );
9501 ptr
+= hostname
[ 0 ];
9502 if( inIndex
!= 1 ) SNPrintF_Add( (char **) &ptr
, (char *) lim
, " (%u)", inIndex
);
9503 rdataPtr
[ 0 ] = (uint8_t)( ptr
- &rdataPtr
[ 1 ] );
9505 check( (size_t)( lim
- ptr
) >= recordNameLen
);
9506 memcpy( ptr
, recordName
, recordNameLen
);
9507 ptr
+= recordNameLen
;
9509 rdataLen
= (size_t)( ptr
- rdataPtr
);
9511 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_PTR
, kDNSServiceClass_IN
, kMDNSRecordTTL_Other
,
9512 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9513 require_noerr( err
, exit
);
9517 *answerPtr
= answer
;
9520 else if( _MDNSReplierServiceInstanceNameMatch( inContext
, inName
, &index
, &txtSize
, &count
) &&
9521 ( index
== inIndex
) && ( count
>= inIndex
) )
9523 int listHasSRV
= false;
9524 int listHasTXT
= false;
9526 if( inType
== kDNSServiceType_ANY
)
9528 for( answer
= *inAnswerList
; answer
; answer
= answer
->next
)
9530 if( answer
->type
== kDNSServiceType_SRV
)
9532 if( !listHasSRV
&& DomainNameEqual( answer
->name
, inName
) ) listHasSRV
= true;
9534 else if( answer
->type
== kDNSServiceType_TXT
)
9536 if( !listHasTXT
&& DomainNameEqual( answer
->name
, inName
) ) listHasTXT
= true;
9538 if( listHasSRV
&& listHasTXT
) break;
9542 if( ( inType
== kDNSServiceType_SRV
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasSRV
) )
9544 dns_fixed_fields_srv
* fields
;
9547 uint8_t * targetPtr
;
9549 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9550 require_noerr( err
, exit
);
9552 rdataLen
= sizeof( dns_fixed_fields_srv
) + 1 + hostname
[ 0 ] + 10 + kLocalNameLen
;
9553 rdataPtr
= (uint8_t *) malloc( rdataLen
);
9554 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9556 lim
= &rdataPtr
[ rdataLen
];
9558 fields
= (dns_fixed_fields_srv
*) rdataPtr
;
9559 dns_fixed_fields_srv_init( fields
, 0, 0, (uint16_t)( kMDNSReplierPortBase
+ txtSize
) );
9561 targetPtr
= (uint8_t *) &fields
[ 1 ];
9563 ptr
= &targetPtr
[ 1 ];
9564 memcpy( ptr
, &hostname
[ 1 ], hostname
[ 0 ] );
9565 ptr
+= hostname
[ 0 ];
9566 if( inIndex
!= 1 ) SNPrintF_Add( (char **) &ptr
, (char *) lim
, "-%u", inIndex
);
9567 targetPtr
[ 0 ] = (uint8_t)( ptr
- &targetPtr
[ 1 ] );
9569 check( (size_t)( lim
- ptr
) >= kLocalNameLen
);
9570 memcpy( ptr
, kLocalName
, kLocalNameLen
);
9571 ptr
+= kLocalNameLen
;
9573 rdataLen
= (size_t)( ptr
- rdataPtr
);
9575 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_SRV
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9576 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9577 require_noerr( err
, exit
);
9581 *answerPtr
= answer
;
9582 answerPtr
= &answer
->next
;
9585 if( ( inType
== kDNSServiceType_TXT
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasTXT
) )
9587 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9588 require_noerr( err
, exit
);
9591 err
= _MDNSReplierCreateTXTRecord( inName
, rdataLen
, &rdataPtr
);
9592 require_noerr( err
, exit
);
9594 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
, kMDNSRecordTTL_Other
,
9595 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9596 require_noerr( err
, exit
);
9600 *answerPtr
= answer
;
9602 else if( inType
== kDNSServiceType_NSEC
)
9604 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9605 require_noerr( err
, exit
);
9607 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 2, kDNSServiceType_TXT
, kDNSServiceType_SRV
);
9608 require_noerr( err
, exit
);
9610 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_NSEC
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9611 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9612 require_noerr( err
, exit
);
9616 *answerPtr
= answer
;
9622 FreeNullSafe( recordName
);
9623 FreeNullSafe( rdataPtr
);
9627 //===========================================================================================================================
9628 // _MDNSReplierAnswerListRemovePTR
9629 //===========================================================================================================================
9632 _MDNSReplierAnswerListRemovePTR(
9633 MRResourceRecord
** inAnswerListPtr
,
9634 const uint8_t * inName
,
9635 const uint8_t * inRData
)
9637 MRResourceRecord
* answer
;
9638 MRResourceRecord
** answerPtr
;
9640 for( answerPtr
= inAnswerListPtr
; ( answer
= *answerPtr
) != NULL
; answerPtr
= &answer
->next
)
9642 if( ( answer
->type
== kDNSServiceType_PTR
) && ( answer
->class == kDNSServiceClass_IN
) &&
9643 DomainNameEqual( answer
->name
, inName
) && DomainNameEqual( answer
->rdata
, inRData
) ) break;
9647 *answerPtr
= answer
->next
;
9648 _MRResourceRecordFree( answer
);
9652 //===========================================================================================================================
9653 // _MDNSReplierSendOrDropResponse
9654 //===========================================================================================================================
9657 _MDNSReplierSendOrDropResponse(
9658 MDNSReplierContext
* inContext
,
9659 MRResourceRecord
* inAnswerList
,
9660 sockaddr_ip
* inQuerier
,
9662 unsigned int inIndex
,
9666 uint8_t * responsePtr
= NULL
;
9668 const struct sockaddr
* destAddr
;
9670 const double dropRate
= inUnicast
? inContext
->ucastDropRate
: inContext
->mcastDropRate
;
9673 check( inIndex
<= inContext
->maxInstanceCount
);
9675 // If maxDropCount > 0, then the drop rates apply only to the first maxDropCount responses. Otherwise, all messages are
9676 // subject to their respective drop rate. Also, responses to queries about mDNS replier itself (indicated by index 0),
9677 // as opposed to those for service instance records, are never dropped.
9682 if( inContext
->maxDropCount
> 0 )
9684 uint8_t * const dropCount
= &inContext
->dropCounters
[ inIndex
- 1 ];
9686 if( *dropCount
< inContext
->maxDropCount
)
9688 if( ShouldDrop( dropRate
) ) drop
= true;
9692 else if( ShouldDrop( dropRate
) )
9698 err
= _MDNSReplierCreateResponse( inContext
, inAnswerList
, inIndex
, &responsePtr
, &responseLen
);
9699 require_noerr( err
, exit
);
9703 destAddr
= &inQuerier
->sa
;
9707 destAddr
= ( inQuerier
->sa
.sa_family
== AF_INET
) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
9710 mr_ulog( kLogLevelInfo
, "%s %zu byte response to %##a:\n\n%#1{du:dnsmsg}",
9711 drop
? "Dropping" : "Sending", responseLen
, destAddr
, responsePtr
, responseLen
);
9715 n
= sendto( inSock
, (char *) responsePtr
, responseLen
, 0, destAddr
, SockAddrGetSize( destAddr
) );
9716 err
= map_socket_value_errno( inSock
, n
== (ssize_t
) responseLen
, n
);
9717 require_noerr( err
, exit
);
9721 FreeNullSafe( responsePtr
);
9725 //===========================================================================================================================
9726 // _MDNSReplierCreateResponse
9727 //===========================================================================================================================
9730 _MDNSReplierCreateResponse(
9731 MDNSReplierContext
* inContext
,
9732 MRResourceRecord
* inAnswerList
,
9733 unsigned int inIndex
,
9734 uint8_t ** outResponsePtr
,
9735 size_t * outResponseLen
)
9738 DataBuffer responseDB
;
9740 MRResourceRecord
* answer
;
9741 uint8_t * responsePtr
;
9742 size_t responseLen
, len
;
9743 unsigned int answerCount
, recordCount
;
9744 MRNameOffsetItem
* nameOffsetList
= NULL
;
9746 DataBuffer_Init( &responseDB
, NULL
, 0, SIZE_MAX
);
9748 // The current answers in the answer list will make up the response's Answer Record Section.
9751 for( answer
= inAnswerList
; answer
; answer
= answer
->next
) { ++answerCount
; }
9753 // Unless configured not to, add any additional answers to the answer list for the Additional Record Section.
9755 if( !inContext
->noAdditionals
)
9757 for( answer
= inAnswerList
; answer
; answer
= answer
->next
)
9759 switch( answer
->type
)
9761 case kDNSServiceType_PTR
:
9762 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->rdata
, kDNSServiceType_SRV
,
9764 require_noerr( err
, exit
);
9766 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->rdata
, kDNSServiceType_TXT
,
9768 require_noerr( err
, exit
);
9771 case kDNSServiceType_SRV
:
9772 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->target
, kDNSServiceType_A
,
9774 require_noerr( err
, exit
);
9776 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->target
, kDNSServiceType_AAAA
,
9778 require_noerr( err
, exit
);
9780 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_NSEC
,
9782 require_noerr( err
, exit
);
9785 case kDNSServiceType_TXT
:
9786 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_NSEC
,
9788 require_noerr( err
, exit
);
9791 case kDNSServiceType_A
:
9792 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_AAAA
,
9794 require_noerr( err
, exit
);
9796 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_NSEC
,
9798 require_noerr( err
, exit
);
9801 case kDNSServiceType_AAAA
:
9802 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_A
,
9804 require_noerr( err
, exit
);
9806 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_NSEC
,
9808 require_noerr( err
, exit
);
9817 // Append a provisional header to the response message.
9819 memset( &hdr
, 0, sizeof( hdr
) );
9820 DNSHeaderSetFlags( &hdr
, kDNSHeaderFlag_Response
| kDNSHeaderFlag_AuthAnswer
);
9822 err
= DataBuffer_Append( &responseDB
, &hdr
, sizeof( hdr
) );
9823 require_noerr( err
, exit
);
9825 // Append answers to response message.
9827 responseLen
= DataBuffer_GetLen( &responseDB
);
9829 for( answer
= inAnswerList
; answer
; answer
= answer
->next
)
9831 dns_fixed_fields_record fields
;
9834 // Append record NAME.
9836 err
= _MDNSReplierAppendNameToResponse( &responseDB
, answer
->name
, &nameOffsetList
);
9837 require_noerr( err
, exit
);
9839 // Append record TYPE, CLASS, TTL, and provisional RDLENGTH.
9841 class = answer
->class;
9842 if( ( answer
->type
== kDNSServiceType_SRV
) || ( answer
->type
== kDNSServiceType_TXT
) ||
9843 ( answer
->type
== kDNSServiceType_A
) || ( answer
->type
== kDNSServiceType_AAAA
) ||
9844 ( answer
->type
== kDNSServiceType_NSEC
) )
9846 class |= kRRClassCacheFlushBit
;
9849 dns_fixed_fields_record_init( &fields
, answer
->type
, (uint16_t) class, answer
->ttl
, (uint16_t) answer
->rdlength
);
9850 err
= DataBuffer_Append( &responseDB
, &fields
, sizeof( fields
) );
9851 require_noerr( err
, exit
);
9853 // Append record RDATA.
9854 // The RDATA of PTR, SRV, and NSEC records contain domain names, which are subject to name compression.
9856 if( ( answer
->type
== kDNSServiceType_PTR
) || ( answer
->type
== kDNSServiceType_SRV
) ||
9857 ( answer
->type
== kDNSServiceType_NSEC
) )
9860 uint8_t * rdLengthPtr
;
9861 const size_t rdLengthOffset
= DataBuffer_GetLen( &responseDB
) - 2;
9862 const size_t rdataOffset
= DataBuffer_GetLen( &responseDB
);
9864 if( answer
->type
== kDNSServiceType_PTR
)
9866 err
= _MDNSReplierAppendNameToResponse( &responseDB
, answer
->rdata
, &nameOffsetList
);
9867 require_noerr( err
, exit
);
9869 else if( answer
->type
== kDNSServiceType_SRV
)
9871 require_fatal( answer
->target
== &answer
->rdata
[ 6 ], "Bad SRV record target pointer." );
9873 err
= DataBuffer_Append( &responseDB
, answer
->rdata
, (size_t)( answer
->target
- answer
->rdata
) );
9874 require_noerr( err
, exit
);
9876 err
= _MDNSReplierAppendNameToResponse( &responseDB
, answer
->target
, &nameOffsetList
);
9877 require_noerr( err
, exit
);
9881 const size_t nameLen
= DomainNameLength( answer
->rdata
);
9883 err
= _MDNSReplierAppendNameToResponse( &responseDB
, answer
->rdata
, &nameOffsetList
);
9884 require_noerr( err
, exit
);
9886 require_fatal( answer
->rdlength
> nameLen
, "Bad NSEC record data length." );
9888 err
= DataBuffer_Append( &responseDB
, &answer
->rdata
[ nameLen
], answer
->rdlength
- nameLen
);
9889 require_noerr( err
, exit
);
9892 // Set the actual RDLENGTH, which may be less than the original due to name compression.
9894 rdlength
= DataBuffer_GetLen( &responseDB
) - rdataOffset
;
9895 check( rdlength
<= UINT16_MAX
);
9897 rdLengthPtr
= DataBuffer_GetPtr( &responseDB
) + rdLengthOffset
;
9898 WriteBig16( rdLengthPtr
, rdlength
);
9902 err
= DataBuffer_Append( &responseDB
, answer
->rdata
, answer
->rdlength
);
9903 require_noerr( err
, exit
);
9906 if( DataBuffer_GetLen( &responseDB
) > kMDNSMessageSizeMax
) break;
9907 responseLen
= DataBuffer_GetLen( &responseDB
);
9911 // Set the response header's Answer and Additional record counts.
9912 // Note: recordCount may be less than answerCount if including all answerCount records would cause the size of the
9913 // response message to exceed the maximum mDNS message size.
9915 if( recordCount
<= answerCount
)
9917 DNSHeaderSetAnswerCount( (DNSHeader
*) DataBuffer_GetPtr( &responseDB
), recordCount
);
9921 DNSHeaderSetAnswerCount( (DNSHeader
*) DataBuffer_GetPtr( &responseDB
), answerCount
);
9922 DNSHeaderSetAdditionalCount( (DNSHeader
*) DataBuffer_GetPtr( &responseDB
), recordCount
- answerCount
);
9925 err
= DataBuffer_Detach( &responseDB
, &responsePtr
, &len
);
9926 require_noerr( err
, exit
);
9928 if( outResponsePtr
) *outResponsePtr
= responsePtr
;
9929 if( outResponseLen
) *outResponseLen
= responseLen
;
9932 _MRNameOffsetItemFreeList( nameOffsetList
);
9933 DataBuffer_Free( &responseDB
);
9937 //===========================================================================================================================
9938 // _MDNSReplierAppendNameToResponse
9939 //===========================================================================================================================
9942 _MDNSReplierAppendNameToResponse(
9943 DataBuffer
* inResponse
,
9944 const uint8_t * inName
,
9945 MRNameOffsetItem
** inNameOffsetListPtr
)
9948 const uint8_t * subname
;
9949 const uint8_t * limit
;
9951 MRNameOffsetItem
* item
;
9952 uint8_t compressionPtr
[ 2 ];
9954 nameOffset
= DataBuffer_GetLen( inResponse
);
9956 // Find the name's longest subname (more accurately, its longest sub-FQDN) in the name compression list.
9958 for( subname
= inName
; subname
[ 0 ] != 0; subname
+= ( 1 + subname
[ 0 ] ) )
9960 for( item
= *inNameOffsetListPtr
; item
; item
= item
->next
)
9962 if( DomainNameEqual( item
->name
, subname
) ) break;
9965 // If an item was found for this subname, then append a name compression pointer and we're done. Otherwise, append
9966 // the subname's first label.
9970 DNSMessageWriteLabelPointer( compressionPtr
, item
->offset
);
9972 err
= DataBuffer_Append( inResponse
, compressionPtr
, sizeof( compressionPtr
) );
9973 require_noerr( err
, exit
);
9978 err
= DataBuffer_Append( inResponse
, subname
, 1 + subname
[ 0 ] );
9979 require_noerr( err
, exit
);
9983 // 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
9984 // label were appended to the response message, so a root label is needed to terminate the complete name.
9986 if( subname
[ 0 ] == 0 )
9988 err
= DataBuffer_Append( inResponse
, "", 1 );
9989 require_noerr( err
, exit
);
9992 // Add subnames that weren't able to be compressed and their offsets to the name compression list.
9995 for( subname
= inName
; subname
< limit
; subname
+= ( 1 + subname
[ 0 ] ) )
9997 const size_t subnameOffset
= nameOffset
+ (size_t)( subname
- inName
);
9999 if( subnameOffset
> kDNSCompressionOffsetMax
) break;
10001 err
= _MRNameOffsetItemCreate( subname
, (uint16_t) subnameOffset
, &item
);
10002 require_noerr( err
, exit
);
10004 item
->next
= *inNameOffsetListPtr
;
10005 *inNameOffsetListPtr
= item
;
10013 //===========================================================================================================================
10014 // _MDNSReplierServiceTypeMatch
10015 //===========================================================================================================================
10018 _MDNSReplierServiceTypeMatch(
10019 const MDNSReplierContext
* inContext
,
10020 const uint8_t * inName
,
10021 unsigned int * outTXTSize
,
10022 unsigned int * outCount
)
10027 uint32_t txtSize
, count
;
10028 const uint8_t * const serviceLabel
= inContext
->serviceLabel
;
10029 int nameMatches
= false;
10031 require_quiet( inName
[ 0 ] >= serviceLabel
[ 0 ], exit
);
10032 if( _memicmp( &inName
[ 1 ], &serviceLabel
[ 1 ], serviceLabel
[ 0 ] ) != 0 ) goto exit
;
10034 ptr
= (const char *) &inName
[ 1 + serviceLabel
[ 0 ] ];
10035 end
= (const char *) &inName
[ 1 + inName
[ 0 ] ];
10037 require_quiet( ( ptr
< end
) && ( *ptr
== '-' ), exit
);
10040 err
= DecimalTextToUInt32( ptr
, end
, &txtSize
, &ptr
);
10041 require_noerr_quiet( err
, exit
);
10042 require_quiet( txtSize
<= UINT16_MAX
, exit
);
10044 require_quiet( ( ptr
< end
) && ( *ptr
== '-' ), exit
);
10047 err
= DecimalTextToUInt32( ptr
, end
, &count
, &ptr
);
10048 require_noerr_quiet( err
, exit
);
10049 require_quiet( count
<= UINT16_MAX
, exit
);
10050 require_quiet( ptr
== end
, exit
);
10052 if( !DomainNameEqual( (const uint8_t *) ptr
, (const uint8_t *) "\x04" "_tcp" "\x05" "local" ) ) goto exit
;
10053 nameMatches
= true;
10055 if( outTXTSize
) *outTXTSize
= txtSize
;
10056 if( outCount
) *outCount
= count
;
10059 return( nameMatches
? true : false );
10062 //===========================================================================================================================
10063 // _MDNSReplierServiceInstanceNameMatch
10064 //===========================================================================================================================
10067 _MDNSReplierServiceInstanceNameMatch(
10068 const MDNSReplierContext
* inContext
,
10069 const uint8_t * inName
,
10070 unsigned int * outIndex
,
10071 unsigned int * outTXTSize
,
10072 unsigned int * outCount
)
10075 const uint8_t * ptr
;
10076 const uint8_t * end
;
10078 unsigned int txtSize
, count
;
10079 const uint8_t * const hostname
= inContext
->hostname
;
10080 int nameMatches
= false;
10082 require_quiet( inName
[ 0 ] >= hostname
[ 0 ], exit
);
10083 if( _memicmp( &inName
[ 1 ], &hostname
[ 1 ], hostname
[ 0 ] ) != 0 ) goto exit
;
10085 ptr
= &inName
[ 1 + hostname
[ 0 ] ];
10086 end
= &inName
[ 1 + inName
[ 0 ] ];
10089 require_quiet( ( end
- ptr
) >= 2, exit
);
10090 require_quiet( ( ptr
[ 0 ] == ' ' ) && ( ptr
[ 1 ] == '(' ), exit
);
10093 err
= DecimalTextToUInt32( (const char *) ptr
, (const char *) end
, &index
, (const char **) &ptr
);
10094 require_noerr_quiet( err
, exit
);
10095 require_quiet( ( index
>= 2 ) && ( index
<= UINT16_MAX
), exit
);
10097 require_quiet( ( ( end
- ptr
) == 1 ) && ( *ptr
== ')' ), exit
);
10105 if( !_MDNSReplierServiceTypeMatch( inContext
, ptr
, &txtSize
, &count
) ) goto exit
;
10106 nameMatches
= true;
10108 if( outIndex
) *outIndex
= index
;
10109 if( outTXTSize
) *outTXTSize
= txtSize
;
10110 if( outCount
) *outCount
= count
;
10113 return( nameMatches
? true : false );
10116 //===========================================================================================================================
10117 // _MDNSReplierAboutRecordNameMatch
10118 //===========================================================================================================================
10120 #define _MemIEqual( PTR1, LEN1, PTR2, LEN2 ) \
10121 ( ( ( LEN1 ) == ( LEN2 ) ) && ( _memicmp( ( PTR1 ), ( PTR2 ), ( LEN1 ) ) == 0 ) )
10123 static Boolean
_MDNSReplierAboutRecordNameMatch( const MDNSReplierContext
*inContext
, const uint8_t *inName
)
10125 const uint8_t * subname
;
10126 const uint8_t * const hostname
= inContext
->hostname
;
10127 int nameMatches
= false;
10129 if( strnicmpx( &inName
[ 1 ], inName
[ 0 ], "about" ) != 0 ) goto exit
;
10130 subname
= DomainNameGetNextLabel( inName
);
10132 if( !_MemIEqual( &subname
[ 1 ], subname
[ 0 ], &hostname
[ 1 ], hostname
[ 0 ] ) ) goto exit
;
10133 subname
= DomainNameGetNextLabel( subname
);
10135 if( !DomainNameEqual( subname
, kLocalName
) ) goto exit
;
10136 nameMatches
= true;
10139 return( nameMatches
? true : false );
10142 //===========================================================================================================================
10143 // _MDNSReplierHostnameMatch
10144 //===========================================================================================================================
10147 _MDNSReplierHostnameMatch(
10148 const MDNSReplierContext
* inContext
,
10149 const uint8_t * inName
,
10150 unsigned int * outIndex
)
10153 const uint8_t * ptr
;
10154 const uint8_t * end
;
10156 const uint8_t * const hostname
= inContext
->hostname
;
10157 int nameMatches
= false;
10159 require_quiet( inName
[ 0 ] >= hostname
[ 0 ], exit
);
10160 if( _memicmp( &inName
[ 1 ], &hostname
[ 1 ], hostname
[ 0 ] ) != 0 ) goto exit
;
10162 ptr
= &inName
[ 1 + hostname
[ 0 ] ];
10163 end
= &inName
[ 1 + inName
[ 0 ] ];
10166 require_quiet( *ptr
== '-', exit
);
10169 err
= DecimalTextToUInt32( (const char *) ptr
, (const char *) end
, &index
, (const char **) &ptr
);
10170 require_noerr_quiet( err
, exit
);
10171 require_quiet( ( index
>= 2 ) && ( index
<= UINT16_MAX
), exit
);
10172 require_quiet( ptr
== end
, exit
);
10179 if( !DomainNameEqual( ptr
, kLocalName
) ) goto exit
;
10180 nameMatches
= true;
10182 if( outIndex
) *outIndex
= index
;
10185 return( nameMatches
? true : false );
10188 //===========================================================================================================================
10189 // _MDNSReplierCreateTXTRecord
10190 //===========================================================================================================================
10192 static OSStatus
_MDNSReplierCreateTXTRecord( const uint8_t *inRecordName
, size_t inSize
, uint8_t **outTXT
)
10197 size_t i
, wholeCount
, remCount
;
10200 uint8_t txtStr
[ 16 ];
10202 require_action_quiet( inSize
> 0, exit
, err
= kSizeErr
);
10204 txt
= (uint8_t *) malloc( inSize
);
10205 require_action( txt
, exit
, err
= kNoMemoryErr
);
10207 hash
= _FNV1( inRecordName
, DomainNameLength( inRecordName
) );
10210 n
= MemPrintF( &txtStr
[ 1 ], 15, "hash=0x%08X", hash
);
10214 wholeCount
= inSize
/ 16;
10215 for( i
= 0; i
< wholeCount
; ++i
)
10217 memcpy( ptr
, txtStr
, 16 );
10221 remCount
= inSize
% 16;
10224 txtStr
[ 0 ] = (uint8_t)( remCount
- 1 );
10225 memcpy( ptr
, txtStr
, remCount
);
10228 check( ptr
== &txt
[ inSize
] );
10237 //===========================================================================================================================
10238 // _MRResourceRecordCreate
10239 //===========================================================================================================================
10242 _MRResourceRecordCreate(
10247 uint16_t inRDLength
,
10249 MRResourceRecord
** outRecord
)
10252 MRResourceRecord
* obj
;
10254 obj
= (MRResourceRecord
*) calloc( 1, sizeof( *obj
) );
10255 require_action( obj
, exit
, err
= kNoMemoryErr
);
10257 obj
->name
= inName
;
10258 obj
->type
= inType
;
10259 obj
->class = inClass
;
10261 obj
->rdlength
= inRDLength
;
10262 obj
->rdata
= inRData
;
10264 if( inType
== kDNSServiceType_SRV
)
10266 require_action_quiet( obj
->rdlength
> sizeof( dns_fixed_fields_srv
), exit
, err
= kMalformedErr
);
10267 obj
->target
= obj
->rdata
+ sizeof( dns_fixed_fields_srv
);
10275 FreeNullSafe( obj
);
10279 //===========================================================================================================================
10280 // _MRResourceRecordFree
10281 //===========================================================================================================================
10283 static void _MRResourceRecordFree( MRResourceRecord
*inRecord
)
10285 ForgetMem( &inRecord
->name
);
10286 ForgetMem( &inRecord
->rdata
);
10290 //===========================================================================================================================
10291 // _MRResourceRecordFreeList
10292 //===========================================================================================================================
10294 static void _MRResourceRecordFreeList( MRResourceRecord
*inList
)
10296 MRResourceRecord
* record
;
10298 while( ( record
= inList
) != NULL
)
10300 inList
= record
->next
;
10301 _MRResourceRecordFree( record
);
10305 //===========================================================================================================================
10306 // _MRNameOffsetItemCreate
10307 //===========================================================================================================================
10309 static OSStatus
_MRNameOffsetItemCreate( const uint8_t *inName
, uint16_t inOffset
, MRNameOffsetItem
**outItem
)
10312 MRNameOffsetItem
* obj
;
10315 require_action_quiet( inOffset
<= kDNSCompressionOffsetMax
, exit
, err
= kSizeErr
);
10317 nameLen
= DomainNameLength( inName
);
10318 obj
= (MRNameOffsetItem
*) calloc( 1, offsetof( MRNameOffsetItem
, name
) + nameLen
);
10319 require_action( obj
, exit
, err
= kNoMemoryErr
);
10321 obj
->offset
= inOffset
;
10322 memcpy( obj
->name
, inName
, nameLen
);
10331 //===========================================================================================================================
10332 // _MRNameOffsetItemFree
10333 //===========================================================================================================================
10335 static void _MRNameOffsetItemFree( MRNameOffsetItem
*inItem
)
10340 //===========================================================================================================================
10341 // _MRNameOffsetItemFreeList
10342 //===========================================================================================================================
10344 static void _MRNameOffsetItemFreeList( MRNameOffsetItem
*inList
)
10346 MRNameOffsetItem
* item
;
10348 while( ( item
= inList
) != NULL
)
10350 inList
= item
->next
;
10351 _MRNameOffsetItemFree( item
);
10355 //===========================================================================================================================
10357 //===========================================================================================================================
10359 #define kGAIPerfStandardTTL ( 1 * kSecondsPerHour )
10361 typedef struct GAITesterPrivate
* GAITesterRef
;
10362 typedef struct GAITestCase GAITestCase
;
10366 const char * name
; // Domain name that was resolved.
10367 uint64_t connectionTimeUs
; // Time in microseconds that it took to create a DNS-SD connection.
10368 uint64_t firstTimeUs
; // Time in microseconds that it took to get the first address result.
10369 uint64_t timeUs
; // Time in microseconds that it took to get all expected address results.
10372 } GAITestItemResult
;
10374 typedef void ( *GAITesterStopHandler_f
)( void *inContext
, OSStatus inError
);
10376 ( *GAITesterResultsHandler_f
)(
10377 const char * inCaseTitle
,
10378 NanoTime64 inCaseStartTime
,
10379 NanoTime64 inCaseEndTime
,
10380 const GAITestItemResult
* inResultArray
,
10381 size_t inResultCount
,
10382 void * inContext
);
10384 typedef unsigned int GAITestAddrType
;
10385 #define kGAITestAddrType_None 0
10386 #define kGAITestAddrType_IPv4 ( 1U << 0 )
10387 #define kGAITestAddrType_IPv6 ( 1U << 1 )
10388 #define kGAITestAddrType_Both ( kGAITestAddrType_IPv4 | kGAITestAddrType_IPv6 )
10390 #define GAITestAddrTypeIsValid( X ) \
10391 ( ( (X) & kGAITestAddrType_Both ) && ( ( (X) & ~kGAITestAddrType_Both ) == 0 ) )
10395 GAITesterRef tester
; // GAI tester object.
10396 CFMutableArrayRef testCaseResults
; // Array of test case results.
10397 unsigned int iterTimeLimitMs
; // Amount of time to allow each iteration to complete.
10398 unsigned int callDelayMs
; // Amount of time to wait before calling DNSServiceGetAddrInfo().
10399 unsigned int serverDelayMs
; // Amount of additional time to have server delay its responses.
10400 unsigned int defaultIterCount
; // Default test case iteration count.
10401 dispatch_source_t sigIntSource
; // Dispatch source for SIGINT.
10402 dispatch_source_t sigTermSource
; // Dispatch source for SIGTERM.
10403 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout.
10404 OutputFormatType outputFormat
; // Format of test results output.
10405 Boolean skipPathEval
; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
10406 Boolean badUDPMode
; // True if the test DNS server is to run in Bad UDP mode.
10407 Boolean testFailed
; // True if at least one test case iteration failed.
10411 static void GAIPerfContextFree( GAIPerfContext
*inContext
);
10412 static OSStatus
GAIPerfAddAdvancedTestCases( GAIPerfContext
*inContext
);
10413 static OSStatus
GAIPerfAddBasicTestCases( GAIPerfContext
*inContext
);
10414 static void GAIPerfTesterStopHandler( void *inContext
, OSStatus inError
);
10416 GAIPerfResultsHandler(
10417 const char * inCaseTitle
,
10418 NanoTime64 inCaseStartTime
,
10419 NanoTime64 inCaseEndTime
,
10420 const GAITestItemResult
* inResultArray
,
10421 size_t inResultCount
,
10422 void * inContext
);
10423 static void GAIPerfSignalHandler( void *inContext
);
10425 CFTypeID
GAITesterGetTypeID( void );
10428 dispatch_queue_t inQueue
,
10429 unsigned int inCallDelayMs
,
10430 int inServerDelayMs
,
10431 int inServerDefaultTTL
,
10432 Boolean inSkipPathEvaluation
,
10433 Boolean inBadUDPMode
,
10434 GAITesterRef
* outTester
);
10435 static void GAITesterStart( GAITesterRef inTester
);
10436 static void GAITesterStop( GAITesterRef inTester
);
10437 static OSStatus
GAITesterAddTestCase( GAITesterRef inTester
, GAITestCase
*inCase
);
10439 GAITesterSetStopHandler(
10440 GAITesterRef inTester
,
10441 GAITesterStopHandler_f inEventHandler
,
10442 void * inEventContext
);
10444 GAITesterSetResultsHandler(
10445 GAITesterRef inTester
,
10446 GAITesterResultsHandler_f inResultsHandler
,
10447 void * inResultsContext
);
10449 static OSStatus
GAITestCaseCreate( const char *inTitle
, GAITestCase
**outCase
);
10450 static void GAITestCaseFree( GAITestCase
*inCase
);
10452 GAITestCaseAddItem(
10453 GAITestCase
* inCase
,
10454 unsigned int inAliasCount
,
10455 unsigned int inAddressCount
,
10457 GAITestAddrType inHasAddrs
,
10458 GAITestAddrType inWantAddrs
,
10459 unsigned int inTimeLimitMs
,
10460 unsigned int inItemCount
);
10462 GAITestCaseAddLocalHostItem(
10463 GAITestCase
* inCase
,
10464 GAITestAddrType inWantAddrs
,
10465 unsigned int inTimeLimitMs
,
10466 unsigned int inItemCount
);
10468 static void GAIPerfCmd( void )
10471 GAIPerfContext
* context
= NULL
;
10473 err
= CheckRootUser();
10474 require_noerr_quiet( err
, exit
);
10476 err
= CheckIntegerArgument( gGAIPerf_CallDelayMs
, "call delay (ms)", 0, INT_MAX
);
10477 require_noerr_quiet( err
, exit
);
10479 err
= CheckIntegerArgument( gGAIPerf_ServerDelayMs
, "server delay (ms)", 0, INT_MAX
);
10480 require_noerr_quiet( err
, exit
);
10482 err
= CheckIntegerArgument( gGAIPerf_IterationCount
, "iteration count", 1, INT_MAX
);
10483 require_noerr_quiet( err
, exit
);
10485 err
= CheckIntegerArgument( gGAIPerf_IterationTimeLimitMs
, "iteration time limit (ms)", 0, INT_MAX
);
10486 require_noerr_quiet( err
, exit
);
10488 context
= (GAIPerfContext
*) calloc( 1, sizeof( *context
) );
10489 require_action( context
, exit
, err
= kNoMemoryErr
);
10491 context
->testCaseResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
10492 require_action( context
->testCaseResults
, exit
, err
= kNoMemoryErr
);
10494 context
->iterTimeLimitMs
= (unsigned int) gGAIPerf_IterationTimeLimitMs
;
10495 context
->callDelayMs
= (unsigned int) gGAIPerf_CallDelayMs
;
10496 context
->serverDelayMs
= (unsigned int) gGAIPerf_ServerDelayMs
;
10497 context
->defaultIterCount
= (unsigned int) gGAIPerf_IterationCount
;
10498 context
->skipPathEval
= gGAIPerf_SkipPathEvalulation
? true : false;
10499 context
->badUDPMode
= gGAIPerf_BadUDPMode
? true : false;
10501 if( gGAIPerf_OutputFilePath
)
10503 context
->outputFilePath
= strdup( gGAIPerf_OutputFilePath
);
10504 require_action( context
->outputFilePath
, exit
, err
= kNoMemoryErr
);
10507 err
= OutputFormatFromArgString( gGAIPerf_OutputFormat
, &context
->outputFormat
);
10508 require_noerr_quiet( err
, exit
);
10510 err
= GAITesterCreate( dispatch_get_main_queue(), context
->callDelayMs
, (int) context
->serverDelayMs
,
10511 kGAIPerfStandardTTL
, context
->skipPathEval
, context
->badUDPMode
, &context
->tester
);
10512 require_noerr( err
, exit
);
10514 check( gGAIPerf_TestSuite
);
10515 if( strcasecmp( gGAIPerf_TestSuite
, kGAIPerfTestSuiteName_Basic
) == 0 )
10517 err
= GAIPerfAddBasicTestCases( context
);
10518 require_noerr( err
, exit
);
10520 else if( strcasecmp( gGAIPerf_TestSuite
, kGAIPerfTestSuiteName_Advanced
) == 0 )
10522 err
= GAIPerfAddAdvancedTestCases( context
);
10523 require_noerr( err
, exit
);
10527 FPrintF( stderr
, "error: Invalid test suite name: %s.\n", gGAIPerf_TestSuite
);
10531 GAITesterSetStopHandler( context
->tester
, GAIPerfTesterStopHandler
, context
);
10532 GAITesterSetResultsHandler( context
->tester
, GAIPerfResultsHandler
, context
);
10534 signal( SIGINT
, SIG_IGN
);
10535 err
= DispatchSignalSourceCreate( SIGINT
, GAIPerfSignalHandler
, context
, &context
->sigIntSource
);
10536 require_noerr( err
, exit
);
10537 dispatch_resume( context
->sigIntSource
);
10539 signal( SIGTERM
, SIG_IGN
);
10540 err
= DispatchSignalSourceCreate( SIGTERM
, GAIPerfSignalHandler
, context
, &context
->sigTermSource
);
10541 require_noerr( err
, exit
);
10542 dispatch_resume( context
->sigTermSource
);
10544 GAITesterStart( context
->tester
);
10548 if( context
) GAIPerfContextFree( context
);
10552 //===========================================================================================================================
10553 // GAIPerfContextFree
10554 //===========================================================================================================================
10556 static void GAIPerfContextFree( GAIPerfContext
*inContext
)
10558 ForgetCF( &inContext
->tester
);
10559 ForgetCF( &inContext
->testCaseResults
);
10560 ForgetMem( &inContext
->outputFilePath
);
10561 dispatch_source_forget( &inContext
->sigIntSource
);
10562 dispatch_source_forget( &inContext
->sigTermSource
);
10566 //===========================================================================================================================
10567 // GAIPerfAddAdvancedTestCases
10568 //===========================================================================================================================
10570 #define kTestCaseTitleBufferSize 128
10573 _GAIPerfWriteTestCaseTitle(
10574 char inBuffer
[ kTestCaseTitleBufferSize
],
10575 unsigned int inCNAMERecordCount
,
10576 unsigned int inARecordCount
,
10577 unsigned int inAAAARecordCount
,
10578 GAITestAddrType inRequested
,
10579 unsigned int inIterationCount
,
10580 Boolean inIterationsAreUnique
);
10582 _GAIPerfWriteLocalHostTestCaseTitle(
10583 char inBuffer
[ kTestCaseTitleBufferSize
],
10584 GAITestAddrType inRequested
,
10585 unsigned int inIterationCount
);
10587 #define kGAIPerfAdvancedTestSuite_MaxAliasCount 4
10588 #define kGAIPerfAdvancedTestSuite_MaxAddrCount 8
10590 static OSStatus
GAIPerfAddAdvancedTestCases( GAIPerfContext
*inContext
)
10593 unsigned int aliasCount
, addressCount
, i
;
10594 GAITestCase
* testCase
= NULL
;
10595 char title
[ kTestCaseTitleBufferSize
];
10598 while( aliasCount
<= kGAIPerfAdvancedTestSuite_MaxAliasCount
)
10600 for( addressCount
= 1; addressCount
<= kGAIPerfAdvancedTestSuite_MaxAddrCount
; addressCount
*= 2 )
10602 // Add a test case to resolve a domain name with
10604 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10606 // to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which
10607 // requires server queries.
10609 _GAIPerfWriteTestCaseTitle( title
, aliasCount
, addressCount
, addressCount
, kGAITestAddrType_Both
,
10610 inContext
->defaultIterCount
, true );
10612 err
= GAITestCaseCreate( title
, &testCase
);
10613 require_noerr( err
, exit
);
10615 for( i
= 0; i
< inContext
->defaultIterCount
; ++i
)
10617 err
= GAITestCaseAddItem( testCase
, aliasCount
, addressCount
, kGAIPerfStandardTTL
,
10618 kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
, 1 );
10619 require_noerr( err
, exit
);
10622 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10623 require_noerr( err
, exit
);
10626 // Add a test case to resolve a domain name with
10628 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10630 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique domain name, which requires a server
10631 // query. The subsequent iterations resolve the same domain name as the preliminary iteration, which should
10632 // ideally require no server queries, i.e., the results should come from the cache.
10634 _GAIPerfWriteTestCaseTitle( title
, aliasCount
, addressCount
, addressCount
, kGAITestAddrType_Both
,
10635 inContext
->defaultIterCount
, false );
10637 err
= GAITestCaseCreate( title
, &testCase
);
10638 require_noerr( err
, exit
);
10640 err
= GAITestCaseAddItem( testCase
, aliasCount
, addressCount
, kGAIPerfStandardTTL
,
10641 kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
, inContext
->defaultIterCount
+ 1 );
10642 require_noerr( err
, exit
);
10644 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10645 require_noerr( err
, exit
);
10649 aliasCount
= ( aliasCount
== 0 ) ? 1 : ( 2 * aliasCount
);
10652 // Finally, add a test case to resolve localhost to its IPv4 and IPv6 addresses.
10654 _GAIPerfWriteLocalHostTestCaseTitle( title
, kGAITestAddrType_Both
, inContext
->defaultIterCount
);
10656 err
= GAITestCaseCreate( title
, &testCase
);
10657 require_noerr( err
, exit
);
10659 err
= GAITestCaseAddLocalHostItem( testCase
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
,
10660 inContext
->defaultIterCount
);
10661 require_noerr( err
, exit
);
10663 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10664 require_noerr( err
, exit
);
10668 if( testCase
) GAITestCaseFree( testCase
);
10672 //===========================================================================================================================
10673 // _GAIPerfWriteTestCaseTitle
10674 //===========================================================================================================================
10676 #define GAITestAddrTypeToRequestKeyValue( X ) ( \
10677 ( (X) == kGAITestAddrType_Both ) ? "ipv4\\,ipv6" : \
10678 ( (X) == kGAITestAddrType_IPv4 ) ? "ipv4" : \
10679 ( (X) == kGAITestAddrType_IPv6 ) ? "ipv6" : \
10683 _GAIPerfWriteTestCaseTitle(
10684 char inBuffer
[ kTestCaseTitleBufferSize
],
10685 unsigned int inCNAMERecordCount
,
10686 unsigned int inARecordCount
,
10687 unsigned int inAAAARecordCount
,
10688 GAITestAddrType inRequested
,
10689 unsigned int inIterationCount
,
10690 Boolean inIterationsAreUnique
)
10692 SNPrintF( inBuffer
, kTestCaseTitleBufferSize
, "name=dynamic,cname=%u,a=%u,aaaa=%u,req=%s,iterations=%u%?s",
10693 inCNAMERecordCount
, inARecordCount
, inAAAARecordCount
, GAITestAddrTypeToRequestKeyValue( inRequested
),
10694 inIterationCount
, inIterationsAreUnique
, ",unique" );
10697 //===========================================================================================================================
10698 // _GAIPerfWriteLocalHostTestCaseTitle
10699 //===========================================================================================================================
10702 _GAIPerfWriteLocalHostTestCaseTitle(
10703 char inBuffer
[ kTestCaseTitleBufferSize
],
10704 GAITestAddrType inRequested
,
10705 unsigned int inIterationCount
)
10707 SNPrintF( inBuffer
, kTestCaseTitleBufferSize
, "name=localhost,req=%s,iterations=%u",
10708 GAITestAddrTypeToRequestKeyValue( inRequested
), inIterationCount
);
10711 //===========================================================================================================================
10712 // GAIPerfAddBasicTestCases
10713 //===========================================================================================================================
10715 #define kGAIPerfBasicTestSuite_AliasCount 2
10716 #define kGAIPerfBasicTestSuite_AddrCount 4
10718 static OSStatus
GAIPerfAddBasicTestCases( GAIPerfContext
*inContext
)
10721 GAITestCase
* testCase
= NULL
;
10722 char title
[ kTestCaseTitleBufferSize
];
10726 // Resolve a domain name with
10728 // 2 CNAME records, 4 A records, and 4 AAAA records
10730 // to its IPv4 and IPv6 addresses. Each of the iterations resolves a unique domain name, which requires server
10733 _GAIPerfWriteTestCaseTitle( title
, kGAIPerfBasicTestSuite_AliasCount
,
10734 kGAIPerfBasicTestSuite_AddrCount
, kGAIPerfBasicTestSuite_AddrCount
, kGAITestAddrType_Both
,
10735 inContext
->defaultIterCount
, true );
10737 err
= GAITestCaseCreate( title
, &testCase
);
10738 require_noerr( err
, exit
);
10740 for( i
= 0; i
< inContext
->defaultIterCount
; ++i
)
10742 err
= GAITestCaseAddItem( testCase
, kGAIPerfBasicTestSuite_AliasCount
, kGAIPerfBasicTestSuite_AddrCount
,
10743 kGAIPerfStandardTTL
, kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
, 1 );
10744 require_noerr( err
, exit
);
10747 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10748 require_noerr( err
, exit
);
10752 // Resolve a domain name with
10754 // 2 CNAME records, 4 A records, and 4 AAAA records
10756 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which
10757 // requires server queries. Each of the subsequent iterations resolves the same domain name as the preliminary
10758 // iteration, which should ideally require no additional server queries, i.e., the results should come from the cache.
10760 _GAIPerfWriteTestCaseTitle( title
, kGAIPerfBasicTestSuite_AliasCount
,
10761 kGAIPerfBasicTestSuite_AddrCount
, kGAIPerfBasicTestSuite_AddrCount
, kGAITestAddrType_Both
,
10762 inContext
->defaultIterCount
, false );
10764 err
= GAITestCaseCreate( title
, &testCase
);
10765 require_noerr( err
, exit
);
10767 err
= GAITestCaseAddItem( testCase
, kGAIPerfBasicTestSuite_AliasCount
, kGAIPerfBasicTestSuite_AddrCount
,
10768 kGAIPerfStandardTTL
, kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
,
10769 inContext
->defaultIterCount
+ 1 );
10770 require_noerr( err
, exit
);
10772 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10773 require_noerr( err
, exit
);
10777 // Each iteration resolves localhost to its IPv4 and IPv6 addresses.
10779 _GAIPerfWriteLocalHostTestCaseTitle( title
, kGAITestAddrType_Both
, inContext
->defaultIterCount
);
10781 err
= GAITestCaseCreate( title
, &testCase
);
10782 require_noerr( err
, exit
);
10784 err
= GAITestCaseAddLocalHostItem( testCase
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
,
10785 inContext
->defaultIterCount
);
10786 require_noerr( err
, exit
);
10788 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10789 require_noerr( err
, exit
);
10793 if( testCase
) GAITestCaseFree( testCase
);
10797 //===========================================================================================================================
10798 // GAIPerfTesterStopHandler
10799 //===========================================================================================================================
10801 #define kGAIPerfResultsKey_Info CFSTR( "info" )
10802 #define kGAIPerfResultsKey_TestCases CFSTR( "testCases" )
10803 #define kGAIPerfResultsKey_Success CFSTR( "success" )
10805 #define kGAIPerfInfoKey_CallDelay CFSTR( "callDelayMs" )
10806 #define kGAIPerfInfoKey_ServerDelay CFSTR( "serverDelayMs" )
10807 #define kGAIPerfInfoKey_SkippedPathEval CFSTR( "skippedPathEval" )
10808 #define kGAIPerfInfoKey_UsedBadUDPMode CFSTR( "usedBadUPDMode" )
10810 static void GAIPerfTesterStopHandler( void *inContext
, OSStatus inError
)
10813 GAIPerfContext
* const context
= (GAIPerfContext
*) inContext
;
10814 CFPropertyListRef plist
;
10818 require_noerr_quiet( err
, exit
);
10820 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
10824 "%kO=%lli" // callDelayMs
10825 "%kO=%lli" // serverDelayMs
10826 "%kO=%b" // skippedPathEval
10827 "%kO=%b" // usedBadUPDMode
10829 "%kO=%O" // testCases
10830 "%kO=%b" // success
10832 kGAIPerfResultsKey_Info
,
10833 kGAIPerfInfoKey_CallDelay
, (int64_t) context
->callDelayMs
,
10834 kGAIPerfInfoKey_ServerDelay
, (int64_t) context
->serverDelayMs
,
10835 kGAIPerfInfoKey_SkippedPathEval
, context
->skipPathEval
,
10836 kGAIPerfInfoKey_UsedBadUDPMode
, context
->badUDPMode
,
10837 kGAIPerfResultsKey_TestCases
, context
->testCaseResults
,
10838 kGAIPerfResultsKey_Success
, !context
->testFailed
);
10839 require_noerr( err
, exit
);
10841 err
= OutputPropertyList( plist
, context
->outputFormat
, context
->outputFilePath
);
10842 CFRelease( plist
);
10843 require_noerr( err
, exit
);
10846 exitCode
= err
? 1 : ( context
->testFailed
? 2 : 0 );
10847 GAIPerfContextFree( context
);
10851 //===========================================================================================================================
10852 // GAIPerfResultsHandler
10853 //===========================================================================================================================
10855 // Keys for test case dictionary
10857 #define kGAIPerfTestCaseKey_Title CFSTR( "title" )
10858 #define kGAIPerfTestCaseKey_StartTime CFSTR( "startTime" )
10859 #define kGAIPerfTestCaseKey_EndTime CFSTR( "endTime" )
10860 #define kGAIPerfTestCaseKey_Results CFSTR( "results" )
10861 #define kGAIPerfTestCaseKey_FirstStats CFSTR( "firstStats" )
10862 #define kGAIPerfTestCaseKey_ConnectionStats CFSTR( "connectionStats" )
10863 #define kGAIPerfTestCaseKey_Stats CFSTR( "stats" )
10865 // Keys for test case results array entry dictionaries
10867 #define kGAIPerfTestCaseResultKey_Name CFSTR( "name" )
10868 #define kGAIPerfTestCaseResultKey_ConnectionTime CFSTR( "connectionTimeUs" )
10869 #define kGAIPerfTestCaseResultKey_FirstTime CFSTR( "firstTimeUs" )
10870 #define kGAIPerfTestCaseResultKey_Time CFSTR( "timeUs" )
10872 // Keys for test case stats dictionaries
10874 #define kGAIPerfTestCaseStatsKey_Count CFSTR( "count" )
10875 #define kGAIPerfTestCaseStatsKey_Min CFSTR( "min" )
10876 #define kGAIPerfTestCaseStatsKey_Max CFSTR( "max" )
10877 #define kGAIPerfTestCaseStatsKey_Mean CFSTR( "mean" )
10878 #define kGAIPerfTestCaseStatsKey_StdDev CFSTR( "sd" )
10889 #define GAIPerfStatsInit( X ) \
10890 do { (X)->min = DBL_MAX; (X)->max = DBL_MIN; (X)->mean = 0.0; (X)->stdDev = 0.0; } while( 0 )
10893 GAIPerfResultsHandler(
10894 const char * inCaseTitle
,
10895 NanoTime64 inCaseStartTime
,
10896 NanoTime64 inCaseEndTime
,
10897 const GAITestItemResult
* inResultArray
,
10898 size_t inResultCount
,
10902 GAIPerfContext
* const context
= (GAIPerfContext
*) inContext
;
10903 int namesAreDynamic
, namesAreUnique
;
10905 size_t count
, startIndex
;
10906 CFMutableArrayRef results
= NULL
;
10907 GAIPerfStats stats
, firstStats
, connStats
;
10908 double sum
, firstSum
, connSum
;
10909 size_t keyValueLen
, i
;
10910 char keyValue
[ 16 ]; // Size must be at least strlen( "name=dynamic" ) + 1 bytes.
10911 char startTime
[ 32 ];
10912 char endTime
[ 32 ];
10913 const GAITestItemResult
* result
;
10915 // If this test case resolves the same "d.test." name in each iteration (title contains the "name=dynamic" key-value
10916 // pair, but not the "unique" key), then don't count the first iteration, whose purpose is to populate the cache with
10917 // the domain name's CNAME, A, and AAAA records.
10919 namesAreDynamic
= false;
10920 namesAreUnique
= false;
10922 while( _ParseQuotedEscapedString( ptr
, NULL
, ",", keyValue
, sizeof( keyValue
), &keyValueLen
, NULL
, &ptr
) )
10924 if( strnicmpx( keyValue
, keyValueLen
, "name=dynamic" ) == 0 )
10926 namesAreDynamic
= true;
10928 else if( strnicmpx( keyValue
, keyValueLen
, "unique" ) == 0 )
10930 namesAreUnique
= true;
10932 if( namesAreDynamic
&& namesAreUnique
) break;
10935 startIndex
= ( ( inResultCount
> 0 ) && namesAreDynamic
&& !namesAreUnique
) ? 1 : 0;
10936 results
= CFArrayCreateMutable( NULL
, (CFIndex
)( inResultCount
- startIndex
), &kCFTypeArrayCallBacks
);
10937 require_action( results
, exit
, err
= kNoMemoryErr
);
10939 GAIPerfStatsInit( &stats
);
10940 GAIPerfStatsInit( &firstStats
);
10941 GAIPerfStatsInit( &connStats
);
10947 for( i
= startIndex
; i
< inResultCount
; ++i
)
10951 result
= &inResultArray
[ i
];
10953 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, results
,
10956 "%kO=%lli" // connectionTimeUs
10957 "%kO=%lli" // firstTimeUs
10958 "%kO=%lli" // timeUs
10959 "%kO=%lli" // error
10961 kGAIPerfTestCaseResultKey_Name
, result
->name
,
10962 kGAIPerfTestCaseResultKey_ConnectionTime
, (int64_t) result
->connectionTimeUs
,
10963 kGAIPerfTestCaseResultKey_FirstTime
, (int64_t) result
->firstTimeUs
,
10964 kGAIPerfTestCaseResultKey_Time
, (int64_t) result
->timeUs
,
10965 CFSTR( "error" ), (int64_t) result
->error
);
10966 require_noerr( err
, exit
);
10968 if( !result
->error
)
10970 value
= (double) result
->timeUs
;
10971 if( value
< stats
.min
) stats
.min
= value
;
10972 if( value
> stats
.max
) stats
.max
= value
;
10975 value
= (double) result
->firstTimeUs
;
10976 if( value
< firstStats
.min
) firstStats
.min
= value
;
10977 if( value
> firstStats
.max
) firstStats
.max
= value
;
10980 value
= (double) result
->connectionTimeUs
;
10981 if( value
< connStats
.min
) connStats
.min
= value
;
10982 if( value
> connStats
.max
) connStats
.max
= value
;
10989 context
->testFailed
= true;
10995 stats
.mean
= sum
/ count
;
10996 firstStats
.mean
= firstSum
/ count
;
10997 connStats
.mean
= connSum
/ count
;
11002 for( i
= startIndex
; i
< inResultCount
; ++i
)
11006 result
= &inResultArray
[ i
];
11007 if( result
->error
) continue;
11009 diff
= stats
.mean
- (double) result
->timeUs
;
11010 sum
+= ( diff
* diff
);
11012 diff
= firstStats
.mean
- (double) result
->firstTimeUs
;
11013 firstSum
+= ( diff
* diff
);
11015 diff
= connStats
.mean
- (double) result
->connectionTimeUs
;
11016 connSum
+= ( diff
* diff
);
11018 stats
.stdDev
= sqrt( sum
/ count
);
11019 firstStats
.stdDev
= sqrt( firstSum
/ count
);
11020 connStats
.stdDev
= sqrt( connSum
/ count
);
11023 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->testCaseResults
,
11054 kGAIPerfTestCaseKey_Title
, inCaseTitle
,
11055 kGAIPerfTestCaseKey_StartTime
, _NanoTime64ToTimestamp( inCaseStartTime
, startTime
, sizeof( startTime
) ),
11056 kGAIPerfTestCaseKey_EndTime
, _NanoTime64ToTimestamp( inCaseEndTime
, endTime
, sizeof( endTime
) ),
11057 kGAIPerfTestCaseKey_Results
, results
,
11058 kGAIPerfTestCaseKey_Stats
,
11059 kGAIPerfTestCaseStatsKey_Count
, (int64_t) count
,
11060 kGAIPerfTestCaseStatsKey_Min
, stats
.min
,
11061 kGAIPerfTestCaseStatsKey_Max
, stats
.max
,
11062 kGAIPerfTestCaseStatsKey_Mean
, stats
.mean
,
11063 kGAIPerfTestCaseStatsKey_StdDev
, stats
.stdDev
,
11064 kGAIPerfTestCaseKey_FirstStats
,
11065 kGAIPerfTestCaseStatsKey_Count
, (int64_t) count
,
11066 kGAIPerfTestCaseStatsKey_Min
, firstStats
.min
,
11067 kGAIPerfTestCaseStatsKey_Max
, firstStats
.max
,
11068 kGAIPerfTestCaseStatsKey_Mean
, firstStats
.mean
,
11069 kGAIPerfTestCaseStatsKey_StdDev
, firstStats
.stdDev
,
11070 kGAIPerfTestCaseKey_ConnectionStats
,
11071 kGAIPerfTestCaseStatsKey_Count
, (int64_t) count
,
11072 kGAIPerfTestCaseStatsKey_Min
, connStats
.min
,
11073 kGAIPerfTestCaseStatsKey_Max
, connStats
.max
,
11074 kGAIPerfTestCaseStatsKey_Mean
, connStats
.mean
,
11075 kGAIPerfTestCaseStatsKey_StdDev
, connStats
.stdDev
);
11076 require_noerr( err
, exit
);
11079 CFReleaseNullSafe( results
);
11080 if( err
) exit( 1 );
11083 //===========================================================================================================================
11084 // GAIPerfSignalHandler
11085 //===========================================================================================================================
11087 static void GAIPerfSignalHandler( void *inContext
)
11089 GAIPerfContext
* const context
= (GAIPerfContext
*) inContext
;
11091 if( !context
->tester
) exit( 1 );
11092 GAITesterStop( context
->tester
);
11093 context
->tester
= NULL
;
11096 //===========================================================================================================================
11098 //===========================================================================================================================
11100 // A character set of lower-case alphabet characters and digits and a string length of six allows for 36^6 = 2,176,782,336
11101 // possible strings to use in the Tag label.
11103 #define kGAITesterTagStringLen 6
11105 typedef struct GAITestItem GAITestItem
;
11108 GAITestItem
* next
; // Next test item in list.
11109 char * name
; // Domain name to resolve.
11110 uint64_t connectionTimeUs
; // Time in microseconds that it took to create a DNS-SD connection.
11111 uint64_t firstTimeUs
; // Time in microseconds that it took to get the first address result.
11112 uint64_t timeUs
; // Time in microseconds that it took to get all expected address results.
11113 unsigned int addressCount
; // Address count of the domain name, i.e., the Count label argument.
11114 OSStatus error
; // Current status/error.
11115 unsigned int timeLimitMs
; // Time limit in milliseconds for the test item's completion.
11116 Boolean hasV4
; // True if the domain name has one or more IPv4 addresses.
11117 Boolean hasV6
; // True if the domain name has one or more IPv6 addresses.
11118 Boolean wantV4
; // True if DNSServiceGetAddrInfo() should be called to get IPv4 addresses.
11119 Boolean wantV6
; // True if DNSServiceGetAddrInfo() should be called to get IPv6 addresses.
11124 GAITestCase
* next
; // Next test case in list.
11125 GAITestItem
* itemList
; // List of test items.
11126 char * title
; // Title of the test case.
11129 struct GAITesterPrivate
11131 CFRuntimeBase base
; // CF object base.
11132 dispatch_queue_t queue
; // Serial work queue.
11133 DNSServiceRef connection
; // Reference to the shared DNS-SD connection.
11134 DNSServiceRef getAddrInfo
; // Reference to the current DNSServiceGetAddrInfo operation.
11135 GAITestCase
* caseList
; // List of test cases.
11136 GAITestCase
* currentCase
; // Pointer to the current test case.
11137 GAITestItem
* currentItem
; // Pointer to the current test item.
11138 NanoTime64 caseStartTime
; // Start time of current test case in Unix time as nanoseconds.
11139 NanoTime64 caseEndTime
; // End time of current test case in Unix time as nanoseconds.
11140 unsigned int callDelayMs
; // Amount of time to wait before calling DNSServiceGetAddrInfo().
11141 Boolean skipPathEval
; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
11142 Boolean stopped
; // True if the tester has been stopped.
11143 Boolean badUDPMode
; // True if the test DNS server is to run in Bad UDP mode.
11144 dispatch_source_t timer
; // Timer for enforcing a test item's time limit.
11145 pcap_t
* pcap
; // Captures traffic between mDNSResponder and test DNS server.
11146 pid_t serverPID
; // PID of the test DNS server.
11147 int serverDelayMs
; // Additional time to have the server delay its responses by.
11148 int serverDefaultTTL
; // Default TTL for the server's records.
11149 GAITesterStopHandler_f stopHandler
; // User's stop handler.
11150 void * stopContext
; // User's event handler context.
11151 GAITesterResultsHandler_f resultsHandler
; // User's results handler.
11152 void * resultsContext
; // User's results handler context.
11154 // Variables for current test item.
11156 uint64_t bitmapV4
; // Bitmap of IPv4 results that have yet to be received.
11157 uint64_t bitmapV6
; // Bitmap of IPv6 results that have yet to be received.
11158 uint64_t startTicks
; // Start ticks of DNSServiceGetAddrInfo().
11159 uint64_t connTicks
; // Ticks when the connection was created.
11160 uint64_t firstTicks
; // Ticks when the first DNSServiceGetAddrInfo result was received.
11161 uint64_t endTicks
; // Ticks when the last DNSServiceGetAddrInfo result was received.
11162 Boolean gotFirstResult
; // True if the first result has been received.
11165 CF_CLASS_DEFINE( GAITester
);
11167 static void _GAITesterStartNextTest( GAITesterRef inTester
);
11168 static OSStatus
_GAITesterCreatePacketCapture( pcap_t
**outPCap
);
11169 static void _GAITesterFirstGAITimeout( void *inContext
);
11170 static void _GAITesterTimeout( void *inContext
);
11171 static void DNSSD_API
11172 _GAITesterFirstGAICallback(
11173 DNSServiceRef inSDRef
,
11174 DNSServiceFlags inFlags
,
11175 uint32_t inInterfaceIndex
,
11176 DNSServiceErrorType inError
,
11177 const char * inHostname
,
11178 const struct sockaddr
* inSockAddr
,
11180 void * inContext
);
11181 static void DNSSD_API
11182 _GAITesterGetAddrInfoCallback(
11183 DNSServiceRef inSDRef
,
11184 DNSServiceFlags inFlags
,
11185 uint32_t inInterfaceIndex
,
11186 DNSServiceErrorType inError
,
11187 const char * inHostname
,
11188 const struct sockaddr
* inSockAddr
,
11190 void * inContext
);
11191 static void _GAITesterCompleteCurrentTest( GAITesterRef inTester
, OSStatus inError
);
11193 #define ForgetPacketCapture( X ) ForgetCustom( X, pcap_close )
11197 const char * inName
,
11198 unsigned int inAddressCount
,
11199 GAITestAddrType inHasAddrs
,
11200 GAITestAddrType inWantAddrs
,
11201 unsigned int inTimeLimitMs
,
11202 GAITestItem
** outItem
);
11203 static OSStatus
GAITestItemDup( const GAITestItem
*inItem
, GAITestItem
**outItem
);
11204 static void GAITestItemFree( GAITestItem
*inItem
);
11208 dispatch_queue_t inQueue
,
11209 unsigned int inCallDelayMs
,
11210 int inServerDelayMs
,
11211 int inServerDefaultTTL
,
11212 Boolean inSkipPathEvaluation
,
11213 Boolean inBadUDPMode
,
11214 GAITesterRef
* outTester
)
11217 GAITesterRef obj
= NULL
;
11219 CF_OBJECT_CREATE( GAITester
, obj
, err
, exit
);
11221 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
11222 obj
->callDelayMs
= inCallDelayMs
;
11223 obj
->serverPID
= -1;
11224 obj
->serverDelayMs
= inServerDelayMs
;
11225 obj
->serverDefaultTTL
= inServerDefaultTTL
;
11226 obj
->skipPathEval
= inSkipPathEvaluation
;
11227 obj
->badUDPMode
= inBadUDPMode
;
11234 CFReleaseNullSafe( obj
);
11238 //===========================================================================================================================
11239 // _GAITesterFinalize
11240 //===========================================================================================================================
11242 static void _GAITesterFinalize( CFTypeRef inObj
)
11244 GAITesterRef
const me
= (GAITesterRef
) inObj
;
11245 GAITestCase
* testCase
;
11247 check( !me
->getAddrInfo
);
11248 check( !me
->connection
);
11249 check( !me
->timer
);
11250 dispatch_forget( &me
->queue
);
11251 while( ( testCase
= me
->caseList
) != NULL
)
11253 me
->caseList
= testCase
->next
;
11254 GAITestCaseFree( testCase
);
11258 //===========================================================================================================================
11260 //===========================================================================================================================
11262 static void _GAITesterStart( void *inContext
);
11263 static void _GAITesterStop( GAITesterRef me
, OSStatus inError
);
11265 static void GAITesterStart( GAITesterRef me
)
11268 dispatch_async_f( me
->queue
, me
, _GAITesterStart
);
11271 #define kGAITesterFirstGAITimeoutSecs 4
11273 static void _GAITesterStart( void *inContext
)
11276 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11277 DNSServiceFlags flags
;
11279 char tag
[ kGAITesterTagStringLen
+ 1 ];
11281 err
= SpawnCommand( &me
->serverPID
, "dnssdutil server --loopback --follow %lld%?s%?d%?s%?d%?s",
11282 (int64_t) getpid(),
11283 me
->serverDefaultTTL
>= 0, " --defaultTTL ",
11284 me
->serverDefaultTTL
>= 0, me
->serverDefaultTTL
,
11285 me
->serverDelayMs
>= 0, " --responseDelay ",
11286 me
->serverDelayMs
>= 0, me
->serverDelayMs
,
11287 me
->badUDPMode
, " --badUDPMode" );
11288 require_noerr_quiet( err
, exit
);
11290 SNPrintF( name
, sizeof( name
), "tag-gaitester-probe-%s.ipv4.d.test",
11291 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
11294 if( me
->skipPathEval
) flags
|= kDNSServiceFlagsPathEvaluationDone
;
11296 err
= DNSServiceGetAddrInfo( &me
->getAddrInfo
, flags
, kDNSServiceInterfaceIndexAny
, kDNSServiceProtocol_IPv4
, name
,
11297 _GAITesterFirstGAICallback
, me
);
11298 require_noerr( err
, exit
);
11300 err
= DNSServiceSetDispatchQueue( me
->getAddrInfo
, me
->queue
);
11301 require_noerr( err
, exit
);
11303 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kGAITesterFirstGAITimeoutSecs
),
11304 UINT64_C_safe( kGAITesterFirstGAITimeoutSecs
) * kNanosecondsPerSecond
/ 10, me
->queue
,
11305 _GAITesterFirstGAITimeout
, me
, &me
->timer
);
11306 require_noerr( err
, exit
);
11307 dispatch_resume( me
->timer
);
11310 if( err
) _GAITesterStop( me
, err
);
11313 //===========================================================================================================================
11315 //===========================================================================================================================
11317 static void _GAITesterUserStop( void *inContext
);
11319 static void GAITesterStop( GAITesterRef me
)
11322 dispatch_async_f( me
->queue
, me
, _GAITesterUserStop
);
11325 static void _GAITesterUserStop( void *inContext
)
11327 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11329 _GAITesterStop( me
, kCanceledErr
);
11333 static void _GAITesterStop( GAITesterRef me
, OSStatus inError
)
11337 ForgetPacketCapture( &me
->pcap
);
11338 dispatch_source_forget( &me
->timer
);
11339 DNSServiceForget( &me
->getAddrInfo
);
11340 DNSServiceForget( &me
->connection
);
11341 if( me
->serverPID
!= -1 )
11343 err
= kill( me
->serverPID
, SIGTERM
);
11344 err
= map_global_noerr_errno( err
);
11345 check_noerr( err
);
11346 me
->serverPID
= -1;
11351 me
->stopped
= true;
11352 if( me
->stopHandler
) me
->stopHandler( me
->stopContext
, inError
);
11357 //===========================================================================================================================
11358 // GAITesterAddTestCase
11359 //===========================================================================================================================
11361 static OSStatus
GAITesterAddTestCase( GAITesterRef me
, GAITestCase
*inCase
)
11364 GAITestCase
** ptr
;
11366 require_action_quiet( inCase
->itemList
, exit
, err
= kCountErr
);
11368 for( ptr
= &me
->caseList
; *ptr
; ptr
= &( *ptr
)->next
) {}
11376 //===========================================================================================================================
11377 // GAITesterSetStopHandler
11378 //===========================================================================================================================
11380 static void GAITesterSetStopHandler( GAITesterRef me
, GAITesterStopHandler_f inStopHandler
, void *inStopContext
)
11382 me
->stopHandler
= inStopHandler
;
11383 me
->stopContext
= inStopContext
;
11386 //===========================================================================================================================
11387 // GAITesterSetResultsHandler
11388 //===========================================================================================================================
11390 static void GAITesterSetResultsHandler( GAITesterRef me
, GAITesterResultsHandler_f inResultsHandler
, void *inResultsContext
)
11392 me
->resultsHandler
= inResultsHandler
;
11393 me
->resultsContext
= inResultsContext
;
11396 //===========================================================================================================================
11397 // _GAITesterStartNextTest
11398 //===========================================================================================================================
11400 static void _GAITesterStartNextTest( GAITesterRef me
)
11403 GAITestItem
* item
;
11404 DNSServiceFlags flags
;
11405 DNSServiceProtocol protocols
;
11408 if( me
->currentItem
) me
->currentItem
= me
->currentItem
->next
;
11410 if( !me
->currentItem
)
11412 if( me
->currentCase
)
11414 // No more test items means that the current test case has completed.
11416 me
->caseEndTime
= NanoTimeGetCurrent();
11418 if( me
->resultsHandler
)
11420 size_t resultCount
, i
;
11421 GAITestItemResult
* resultArray
;
11424 for( item
= me
->currentCase
->itemList
; item
; item
= item
->next
) ++resultCount
;
11425 check( resultCount
> 0 );
11427 resultArray
= (GAITestItemResult
*) calloc( resultCount
, sizeof( *resultArray
) );
11428 require_action( resultArray
, exit
, err
= kNoMemoryErr
);
11430 item
= me
->currentCase
->itemList
;
11431 for( i
= 0; i
< resultCount
; ++i
)
11433 resultArray
[ i
].name
= item
->name
;
11434 resultArray
[ i
].connectionTimeUs
= item
->connectionTimeUs
;
11435 resultArray
[ i
].firstTimeUs
= item
->firstTimeUs
;
11436 resultArray
[ i
].timeUs
= item
->timeUs
;
11437 resultArray
[ i
].error
= item
->error
;
11440 me
->resultsHandler( me
->currentCase
->title
, me
->caseStartTime
, me
->caseEndTime
, resultArray
, resultCount
,
11441 me
->resultsContext
);
11442 ForgetMem( &resultArray
);
11445 me
->currentCase
= me
->currentCase
->next
;
11446 if( !me
->currentCase
)
11455 me
->currentCase
= me
->caseList
;
11457 require_action_quiet( me
->currentCase
->itemList
, exit
, err
= kInternalErr
);
11458 me
->currentItem
= me
->currentCase
->itemList
;
11461 item
= me
->currentItem
;
11462 check( ( item
->addressCount
>= 1 ) && ( item
->addressCount
<= 64 ) );
11464 if( !item
->wantV4
) me
->bitmapV4
= 0;
11465 else if( !item
->hasV4
) me
->bitmapV4
= 1;
11466 else if( item
->addressCount
< 64 ) me
->bitmapV4
= ( UINT64_C( 1 ) << item
->addressCount
) - 1;
11467 else me
->bitmapV4
= ~UINT64_C( 0 );
11469 if( !item
->wantV6
) me
->bitmapV6
= 0;
11470 else if( !item
->hasV6
) me
->bitmapV6
= 1;
11471 else if( item
->addressCount
< 64 ) me
->bitmapV6
= ( UINT64_C( 1 ) << item
->addressCount
) - 1;
11472 else me
->bitmapV6
= ~UINT64_C( 0 );
11473 check( ( me
->bitmapV4
!= 0 ) || ( me
->bitmapV6
!= 0 ) );
11474 me
->gotFirstResult
= false;
11476 // Perform preliminary tasks if this is the start of a new test case.
11478 if( item
== me
->currentCase
->itemList
)
11480 // Flush mDNSResponder's cache.
11482 err
= systemf( NULL
, "killall -HUP mDNSResponder" );
11483 require_noerr( err
, exit
);
11486 me
->caseStartTime
= NanoTimeGetCurrent();
11487 me
->caseEndTime
= kNanoTime_Invalid
;
11490 // Start a packet capture.
11492 check( !me
->pcap
);
11493 err
= _GAITesterCreatePacketCapture( &me
->pcap
);
11494 require_noerr( err
, exit
);
11496 // Start timer for test item's time limit.
11498 check( !me
->timer
);
11499 if( item
->timeLimitMs
> 0 )
11501 unsigned int timeLimitMs
;
11503 timeLimitMs
= item
->timeLimitMs
;
11504 if( me
->callDelayMs
> 0 ) timeLimitMs
+= (unsigned int) me
->callDelayMs
;
11505 if( me
->serverDelayMs
> 0 ) timeLimitMs
+= (unsigned int) me
->serverDelayMs
;
11507 err
= DispatchTimerCreate( dispatch_time_milliseconds( timeLimitMs
), DISPATCH_TIME_FOREVER
,
11508 ( (uint64_t) timeLimitMs
) * kNanosecondsPerMillisecond
/ 10,
11509 me
->queue
, _GAITesterTimeout
, NULL
, me
, &me
->timer
);
11510 require_noerr( err
, exit
);
11511 dispatch_resume( me
->timer
);
11514 // Call DNSServiceGetAddrInfo().
11516 if( me
->callDelayMs
> 0 ) usleep( ( (useconds_t
) me
->callDelayMs
) * kMicrosecondsPerMillisecond
);
11518 flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsReturnIntermediates
;
11519 if( me
->skipPathEval
) flags
|= kDNSServiceFlagsPathEvaluationDone
;
11522 if( item
->wantV4
) protocols
|= kDNSServiceProtocol_IPv4
;
11523 if( item
->wantV6
) protocols
|= kDNSServiceProtocol_IPv6
;
11525 me
->startTicks
= UpTicks();
11527 check( !me
->connection
);
11528 err
= DNSServiceCreateConnection( &me
->connection
);
11529 require_noerr( err
, exit
);
11531 err
= DNSServiceSetDispatchQueue( me
->connection
, me
->queue
);
11532 require_noerr( err
, exit
);
11534 me
->connTicks
= UpTicks();
11536 check( !me
->getAddrInfo
);
11537 me
->getAddrInfo
= me
->connection
;
11538 err
= DNSServiceGetAddrInfo( &me
->getAddrInfo
, flags
, kDNSServiceInterfaceIndexAny
, protocols
, item
->name
,
11539 _GAITesterGetAddrInfoCallback
, me
);
11540 require_noerr( err
, exit
);
11543 if( err
|| done
) _GAITesterStop( me
, err
);
11546 //===========================================================================================================================
11547 // _GAITesterCreatePacketCapture
11548 //===========================================================================================================================
11550 static OSStatus
_GAITesterCreatePacketCapture( pcap_t
**outPCap
)
11554 struct bpf_program program
;
11555 char errBuf
[ PCAP_ERRBUF_SIZE
];
11557 pcap
= pcap_create( "lo0", errBuf
);
11558 require_action_string( pcap
, exit
, err
= kUnknownErr
, errBuf
);
11560 err
= pcap_set_buffer_size( pcap
, 512 * kBytesPerKiloByte
);
11561 require_noerr_action( err
, exit
, err
= kUnknownErr
);
11563 err
= pcap_set_snaplen( pcap
, 512 );
11564 require_noerr_action( err
, exit
, err
= kUnknownErr
);
11566 err
= pcap_set_immediate_mode( pcap
, 0 );
11567 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11569 err
= pcap_activate( pcap
);
11570 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11572 err
= pcap_setdirection( pcap
, PCAP_D_INOUT
);
11573 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11575 err
= pcap_setnonblock( pcap
, 1, errBuf
);
11576 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, errBuf
);
11578 err
= pcap_compile( pcap
, &program
, "udp port 53", 1, PCAP_NETMASK_UNKNOWN
);
11579 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11581 err
= pcap_setfilter( pcap
, &program
);
11582 pcap_freecode( &program
);
11583 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11589 if( pcap
) pcap_close( pcap
);
11593 //===========================================================================================================================
11594 // _GAITesterFirstGAITimeout
11595 //===========================================================================================================================
11597 static void _GAITesterFirstGAITimeout( void *inContext
)
11599 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11601 _GAITesterStop( me
, kNoResourcesErr
);
11604 //===========================================================================================================================
11605 // _GAITesterTimeout
11606 //===========================================================================================================================
11608 static void _GAITesterTimeout( void *inContext
)
11610 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11612 _GAITesterCompleteCurrentTest( me
, kTimeoutErr
);
11615 //===========================================================================================================================
11616 // _GAITesterFirstGAICallback
11617 //===========================================================================================================================
11619 static void DNSSD_API
11620 _GAITesterFirstGAICallback(
11621 DNSServiceRef inSDRef
,
11622 DNSServiceFlags inFlags
,
11623 uint32_t inInterfaceIndex
,
11624 DNSServiceErrorType inError
,
11625 const char * inHostname
,
11626 const struct sockaddr
* inSockAddr
,
11630 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11633 Unused( inInterfaceIndex
);
11634 Unused( inHostname
);
11635 Unused( inSockAddr
);
11638 if( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
)
11640 dispatch_source_forget( &me
->timer
);
11641 DNSServiceForget( &me
->getAddrInfo
);
11643 _GAITesterStartNextTest( me
);
11647 //===========================================================================================================================
11648 // _GAITesterGetAddrInfoCallback
11649 //===========================================================================================================================
11651 static void DNSSD_API
11652 _GAITesterGetAddrInfoCallback(
11653 DNSServiceRef inSDRef
,
11654 DNSServiceFlags inFlags
,
11655 uint32_t inInterfaceIndex
,
11656 DNSServiceErrorType inError
,
11657 const char * inHostname
,
11658 const struct sockaddr
* inSockAddr
,
11663 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11664 GAITestItem
* const item
= me
->currentItem
;
11665 const sockaddr_ip
* const sip
= (const sockaddr_ip
*) inSockAddr
;
11667 uint64_t * bitmapPtr
;
11672 Unused( inInterfaceIndex
);
11673 Unused( inHostname
);
11676 nowTicks
= UpTicks();
11678 require_action_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
, err
= kFlagErr
);
11680 // Check if we were expecting an IP address result of this type.
11682 if( sip
->sa
.sa_family
== AF_INET
)
11684 bitmapPtr
= &me
->bitmapV4
;
11685 hasAddr
= item
->hasV4
;
11687 else if( sip
->sa
.sa_family
== AF_INET6
)
11689 bitmapPtr
= &me
->bitmapV6
;
11690 hasAddr
= item
->hasV6
;
11701 uint32_t addrOffset
;
11703 require_noerr_action_quiet( inError
, exit
, err
= inError
);
11705 if( sip
->sa
.sa_family
== AF_INET
)
11707 const uint32_t addrV4
= ntohl( sip
->v4
.sin_addr
.s_addr
);
11709 if( strcasecmp( item
->name
, "localhost." ) == 0 )
11711 if( addrV4
== INADDR_LOOPBACK
) bitmask
= 1;
11715 addrOffset
= addrV4
- kDNSServerBaseAddrV4
;
11716 if( ( addrOffset
>= 1 ) && ( addrOffset
<= item
->addressCount
) )
11718 bitmask
= UINT64_C( 1 ) << ( addrOffset
- 1 );
11724 const uint8_t * const addrV6
= sip
->v6
.sin6_addr
.s6_addr
;
11726 if( strcasecmp( item
->name
, "localhost." ) == 0 )
11728 if( memcmp( addrV6
, in6addr_loopback
.s6_addr
, 16 ) == 0 ) bitmask
= 1;
11730 else if( memcmp( addrV6
, kDNSServerBaseAddrV6
, 15 ) == 0 )
11732 addrOffset
= addrV6
[ 15 ];
11733 if( ( addrOffset
>= 1 ) && ( addrOffset
<= item
->addressCount
) )
11735 bitmask
= UINT64_C( 1 ) << ( addrOffset
- 1 );
11742 require_action_quiet( inError
== kDNSServiceErr_NoSuchRecord
, exit
, err
= inError
? inError
: kUnexpectedErr
);
11745 require_action_quiet( bitmask
!= 0, exit
, err
= kValueErr
);
11746 require_action_quiet( *bitmapPtr
& bitmask
, exit
, err
= kDuplicateErr
);
11748 *bitmapPtr
&= ~bitmask
;
11749 if( !me
->gotFirstResult
)
11751 me
->firstTicks
= nowTicks
;
11752 me
->gotFirstResult
= true;
11757 if( err
|| ( ( me
->bitmapV4
== 0 ) && ( me
->bitmapV6
== 0 ) ) )
11759 me
->endTicks
= nowTicks
;
11760 _GAITesterCompleteCurrentTest( me
, err
);
11764 //===========================================================================================================================
11765 // _GAITesterCompleteCurrentTest
11766 //===========================================================================================================================
11769 _GAITesterGetDNSMessageFromPacket(
11770 const uint8_t * inPacketPtr
,
11771 size_t inPacketLen
,
11772 const uint8_t ** outMsgPtr
,
11773 size_t * outMsgLen
);
11775 static void _GAITesterCompleteCurrentTest( GAITesterRef me
, OSStatus inError
)
11778 GAITestItem
* const item
= me
->currentItem
;
11779 struct timeval timeStamps
[ 4 ];
11780 struct timeval
* tsPtr
;
11781 struct timeval
* tsQA
= NULL
;
11782 struct timeval
* tsQAAAA
= NULL
;
11783 struct timeval
* tsRA
= NULL
;
11784 struct timeval
* tsRAAAA
= NULL
;
11785 struct timeval
* t1
;
11786 struct timeval
* t2
;
11787 int64_t idleTimeUs
;
11788 uint8_t name
[ kDomainNameLengthMax
];
11790 dispatch_source_forget( &me
->timer
);
11791 DNSServiceForget( &me
->getAddrInfo
);
11792 DNSServiceForget( &me
->connection
);
11794 item
->error
= inError
;
11801 err
= DomainNameFromString( name
, item
->name
, NULL
);
11802 require_noerr( err
, exit
);
11804 tsPtr
= &timeStamps
[ 0 ];
11808 struct pcap_pkthdr
* pktHdr
;
11809 const uint8_t * packet
;
11810 const uint8_t * msgPtr
;
11812 const DNSHeader
* hdr
;
11813 unsigned int flags
;
11814 const uint8_t * ptr
;
11815 uint16_t qtype
, qclass
;
11816 uint8_t qname
[ kDomainNameLengthMax
];
11818 status
= pcap_next_ex( me
->pcap
, &pktHdr
, &packet
);
11819 if( status
!= 1 ) break;
11820 if( _GAITesterGetDNSMessageFromPacket( packet
, pktHdr
->caplen
, &msgPtr
, &msgLen
) != kNoErr
) continue;
11821 if( msgLen
< kDNSHeaderLength
) continue;
11823 hdr
= (const DNSHeader
*) msgPtr
;
11824 flags
= DNSHeaderGetFlags( hdr
);
11825 if( DNSFlagsGetOpCode( flags
) != kDNSOpCode_Query
) continue;
11826 if( DNSHeaderGetQuestionCount( hdr
) < 1 ) continue;
11828 ptr
= (const uint8_t *) &hdr
[ 1 ];
11829 if( DNSMessageExtractQuestion( msgPtr
, msgLen
, ptr
, qname
, &qtype
, &qclass
, NULL
) != kNoErr
) continue;
11830 if( qclass
!= kDNSServiceClass_IN
) continue;
11831 if( !DomainNameEqual( qname
, name
) ) continue;
11833 if( item
->wantV4
&& ( qtype
== kDNSServiceType_A
) )
11835 if( flags
& kDNSHeaderFlag_Response
)
11837 if( tsQA
&& !tsRA
)
11840 *tsRA
= pktHdr
->ts
;
11846 *tsQA
= pktHdr
->ts
;
11849 else if( item
->wantV6
&& ( qtype
== kDNSServiceType_AAAA
) )
11851 if( flags
& kDNSHeaderFlag_Response
)
11853 if( tsQAAAA
&& !tsRAAAA
)
11856 *tsRAAAA
= pktHdr
->ts
;
11859 else if( !tsQAAAA
)
11862 *tsQAAAA
= pktHdr
->ts
;
11867 // t1 is the time when the last query was sent.
11869 if( tsQA
&& tsQAAAA
) t1
= TIMEVAL_GT( *tsQA
, *tsQAAAA
) ? tsQA
: tsQAAAA
;
11870 else t1
= tsQA
? tsQA
: tsQAAAA
;
11872 // t2 is when the first response was received.
11874 if( tsRA
&& tsRAAAA
) t2
= TIMEVAL_LT( *tsRA
, *tsRAAAA
) ? tsRA
: tsRAAAA
;
11875 else t2
= tsRA
? tsRA
: tsRAAAA
;
11879 idleTimeUs
= TIMEVAL_USEC64_DIFF( *t2
, *t1
);
11880 if( idleTimeUs
< 0 ) idleTimeUs
= 0;
11887 item
->connectionTimeUs
= UpTicksToMicroseconds( me
->connTicks
- me
->startTicks
);
11888 item
->firstTimeUs
= UpTicksToMicroseconds( me
->firstTicks
- me
->connTicks
) - (uint64_t) idleTimeUs
;
11889 item
->timeUs
= UpTicksToMicroseconds( me
->endTicks
- me
->connTicks
) - (uint64_t) idleTimeUs
;
11892 ForgetPacketCapture( &me
->pcap
);
11893 if( err
) _GAITesterStop( me
, err
);
11894 else _GAITesterStartNextTest( me
);
11897 //===========================================================================================================================
11898 // _GAITesterGetDNSMessageFromPacket
11899 //===========================================================================================================================
11901 #define kHeaderSizeNullLink 4
11902 #define kHeaderSizeIPv4Min 20
11903 #define kHeaderSizeIPv6 40
11904 #define kHeaderSizeUDP 8
11906 #define kIPProtocolUDP 0x11
11909 _GAITesterGetDNSMessageFromPacket(
11910 const uint8_t * inPacketPtr
,
11911 size_t inPacketLen
,
11912 const uint8_t ** outMsgPtr
,
11913 size_t * outMsgLen
)
11916 const uint8_t * nullLink
;
11917 uint32_t addressFamily
;
11918 const uint8_t * ip
;
11921 const uint8_t * msg
;
11922 const uint8_t * const end
= &inPacketPtr
[ inPacketLen
];
11924 nullLink
= &inPacketPtr
[ 0 ];
11925 require_action_quiet( ( end
- nullLink
) >= kHeaderSizeNullLink
, exit
, err
= kUnderrunErr
);
11926 addressFamily
= ReadHost32( &nullLink
[ 0 ] );
11928 ip
= &nullLink
[ kHeaderSizeNullLink
];
11929 if( addressFamily
== AF_INET
)
11931 require_action_quiet( ( end
- ip
) >= kHeaderSizeIPv4Min
, exit
, err
= kUnderrunErr
);
11932 ipHeaderLen
= ( ip
[ 0 ] & 0x0F ) * 4;
11933 protocol
= ip
[ 9 ];
11935 else if( addressFamily
== AF_INET6
)
11937 require_action_quiet( ( end
- ip
) >= kHeaderSizeIPv6
, exit
, err
= kUnderrunErr
);
11938 ipHeaderLen
= kHeaderSizeIPv6
;
11939 protocol
= ip
[ 6 ];
11946 require_action_quiet( protocol
== kIPProtocolUDP
, exit
, err
= kTypeErr
);
11947 require_action_quiet( ( end
- ip
) >= ( ipHeaderLen
+ kHeaderSizeUDP
), exit
, err
= kUnderrunErr
);
11949 msg
= &ip
[ ipHeaderLen
+ kHeaderSizeUDP
];
11952 *outMsgLen
= (size_t)( end
- msg
);
11959 //===========================================================================================================================
11960 // GAITestCaseCreate
11961 //===========================================================================================================================
11963 static OSStatus
GAITestCaseCreate( const char *inTitle
, GAITestCase
**outCase
)
11968 obj
= (GAITestCase
*) calloc( 1, sizeof( *obj
) );
11969 require_action( obj
, exit
, err
= kNoMemoryErr
);
11971 obj
->title
= strdup( inTitle
);
11972 require_action( obj
->title
, exit
, err
= kNoMemoryErr
);
11979 if( obj
) GAITestCaseFree( obj
);
11983 //===========================================================================================================================
11985 //===========================================================================================================================
11987 static void GAITestCaseFree( GAITestCase
*inCase
)
11989 GAITestItem
* item
;
11991 while( ( item
= inCase
->itemList
) != NULL
)
11993 inCase
->itemList
= item
->next
;
11994 GAITestItemFree( item
);
11996 ForgetMem( &inCase
->title
);
12000 //===========================================================================================================================
12001 // GAITestCaseAddItem
12002 //===========================================================================================================================
12005 GAITestCaseAddItem(
12006 GAITestCase
* inCase
,
12007 unsigned int inAliasCount
,
12008 unsigned int inAddressCount
,
12010 GAITestAddrType inHasAddrs
,
12011 GAITestAddrType inWantAddrs
,
12012 unsigned int inTimeLimitMs
,
12013 unsigned int inItemCount
)
12016 GAITestItem
* item
;
12017 GAITestItem
* item2
;
12018 GAITestItem
* newItemList
= NULL
;
12019 GAITestItem
** itemPtr
;
12024 char tag
[ kGAITesterTagStringLen
+ 1 ];
12026 require_action_quiet( inItemCount
> 0, exit
, err
= kNoErr
);
12028 // Limit address count to 64 because we use 64-bit bitmaps for keeping track of addresses.
12030 require_action_quiet( ( inAddressCount
>= 1 ) && ( inAddressCount
<= 64 ), exit
, err
= kCountErr
);
12031 require_action_quiet( ( inAliasCount
>= 0 ) && ( inAliasCount
<= INT32_MAX
), exit
, err
= kCountErr
);
12032 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs
), exit
, err
= kValueErr
);
12035 end
= &name
[ countof( name
) ];
12037 // Add Alias label.
12039 if( inAliasCount
== 1 ) SNPrintF_Add( &ptr
, end
, "alias." );
12040 else if( inAliasCount
>= 2 ) SNPrintF_Add( &ptr
, end
, "alias-%u.", inAliasCount
);
12042 // Add Count label.
12044 SNPrintF_Add( &ptr
, end
, "count-%u.", inAddressCount
);
12048 if( inTTL
>= 0 ) SNPrintF_Add( &ptr
, end
, "ttl-%d.", inTTL
);
12052 SNPrintF_Add( &ptr
, end
, "tag-%s.",
12053 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
12055 // Add IPv4 or IPv6 label if necessary.
12057 if( inHasAddrs
== kGAITestAddrType_IPv4
) SNPrintF_Add( &ptr
, end
, "ipv4." );
12058 else if( inHasAddrs
== kGAITestAddrType_IPv6
) SNPrintF_Add( &ptr
, end
, "ipv6." );
12060 // Finally, add the d.test. labels.
12062 SNPrintF_Add( &ptr
, end
, "d.test." );
12066 err
= GAITestItemCreate( name
, inAddressCount
, inHasAddrs
, inWantAddrs
, inTimeLimitMs
, &item
);
12067 require_noerr( err
, exit
);
12069 newItemList
= item
;
12070 itemPtr
= &item
->next
;
12072 // Create repeat items.
12074 for( i
= 1; i
< inItemCount
; ++i
)
12076 err
= GAITestItemDup( item
, &item2
);
12077 require_noerr( err
, exit
);
12080 itemPtr
= &item2
->next
;
12083 // Append to test case's item list.
12085 for( itemPtr
= &inCase
->itemList
; *itemPtr
; itemPtr
= &( *itemPtr
)->next
) {}
12086 *itemPtr
= newItemList
;
12087 newItemList
= NULL
;
12090 while( ( item
= newItemList
) != NULL
)
12092 newItemList
= item
->next
;
12093 GAITestItemFree( item
);
12098 //===========================================================================================================================
12099 // GAITestCaseAddLocalHostItem
12100 //===========================================================================================================================
12103 GAITestCaseAddLocalHostItem(
12104 GAITestCase
* inCase
,
12105 GAITestAddrType inWantAddrs
,
12106 unsigned int inTimeLimitMs
,
12107 unsigned int inItemCount
)
12110 GAITestItem
* item
;
12111 GAITestItem
* item2
;
12112 GAITestItem
* newItemList
= NULL
;
12113 GAITestItem
** itemPtr
;
12116 require_action_quiet( inItemCount
> 1, exit
, err
= kNoErr
);
12118 err
= GAITestItemCreate( "localhost.", 1, kGAITestAddrType_Both
, inWantAddrs
, inTimeLimitMs
, &item
);
12119 require_noerr( err
, exit
);
12121 newItemList
= item
;
12122 itemPtr
= &item
->next
;
12124 // Create repeat items.
12126 for( i
= 1; i
< inItemCount
; ++i
)
12128 err
= GAITestItemDup( item
, &item2
);
12129 require_noerr( err
, exit
);
12132 itemPtr
= &item2
->next
;
12135 for( itemPtr
= &inCase
->itemList
; *itemPtr
; itemPtr
= &( *itemPtr
)->next
) {}
12136 *itemPtr
= newItemList
;
12137 newItemList
= NULL
;
12140 while( ( item
= newItemList
) != NULL
)
12142 newItemList
= item
->next
;
12143 GAITestItemFree( item
);
12148 //===========================================================================================================================
12149 // GAITestItemCreate
12150 //===========================================================================================================================
12154 const char * inName
,
12155 unsigned int inAddressCount
,
12156 GAITestAddrType inHasAddrs
,
12157 GAITestAddrType inWantAddrs
,
12158 unsigned int inTimeLimitMs
,
12159 GAITestItem
** outItem
)
12162 GAITestItem
* obj
= NULL
;
12164 require_action_quiet( inAddressCount
>= 1, exit
, err
= kCountErr
);
12165 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs
), exit
, err
= kValueErr
);
12166 require_action_quiet( GAITestAddrTypeIsValid( inWantAddrs
), exit
, err
= kValueErr
);
12168 obj
= (GAITestItem
*) calloc( 1, sizeof( *obj
) );
12169 require_action( obj
, exit
, err
= kNoMemoryErr
);
12171 obj
->name
= strdup( inName
);
12172 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
12174 obj
->addressCount
= inAddressCount
;
12175 obj
->hasV4
= ( inHasAddrs
& kGAITestAddrType_IPv4
) ? true : false;
12176 obj
->hasV6
= ( inHasAddrs
& kGAITestAddrType_IPv6
) ? true : false;
12177 obj
->wantV4
= ( inWantAddrs
& kGAITestAddrType_IPv4
) ? true : false;
12178 obj
->wantV6
= ( inWantAddrs
& kGAITestAddrType_IPv6
) ? true : false;
12179 obj
->error
= kInProgressErr
;
12180 obj
->timeLimitMs
= inTimeLimitMs
;
12187 if( obj
) GAITestItemFree( obj
);
12191 //===========================================================================================================================
12193 //===========================================================================================================================
12195 static OSStatus
GAITestItemDup( const GAITestItem
*inItem
, GAITestItem
**outItem
)
12200 obj
= (GAITestItem
*) calloc( 1, sizeof( *obj
) );
12201 require_action( obj
, exit
, err
= kNoMemoryErr
);
12207 obj
->name
= strdup( inItem
->name
);
12208 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
12216 if( obj
) GAITestItemFree( obj
);
12220 //===========================================================================================================================
12222 //===========================================================================================================================
12224 static void GAITestItemFree( GAITestItem
*inItem
)
12226 ForgetMem( &inItem
->name
);
12230 //===========================================================================================================================
12231 // MDNSDiscoveryTestCmd
12232 //===========================================================================================================================
12234 #define kMDNSDiscoveryTestFirstQueryTimeoutSecs 4
12238 DNSServiceRef query
; // Reference to DNSServiceQueryRecord for replier's "about" TXT record.
12239 dispatch_source_t queryTimer
; // Used to time out the "about" TXT record query.
12240 NanoTime64 startTime
; // When the test started.
12241 NanoTime64 endTime
; // When the test ended.
12242 pid_t replierPID
; // PID of mDNS replier.
12243 uint32_t ifIndex
; // Index of interface to run the replier on.
12244 unsigned int instanceCount
; // Desired number of service instances.
12245 unsigned int txtSize
; // Desired size of each service instance's TXT record data.
12246 unsigned int recordCountA
; // Desired number of A records per replier hostname.
12247 unsigned int recordCountAAAA
; // Desired number of AAAA records per replier hostname.
12248 unsigned int maxDropCount
; // Replier's --maxDropCount option argument.
12249 double ucastDropRate
; // Replier's probability of dropping a unicast response.
12250 double mcastDropRate
; // Replier's probability of dropping a multicast query or response.
12251 Boolean noAdditionals
; // True if the replier is to not include additional records in responses.
12252 Boolean useIPv4
; // True if the replier is to use IPv4.
12253 Boolean useIPv6
; // True if the replier is to use IPv6.
12254 Boolean flushedCache
; // True if mDNSResponder's record cache was flushed before testing.
12255 char * replierCommand
; // Command used to run the replier.
12256 char * serviceType
; // Type of services to browse for.
12257 ServiceBrowserRef browser
; // Service browser.
12258 unsigned int browseTimeSecs
; // Amount of time to spend browsing in seconds.
12259 const char * outputFilePath
; // File to write test results to. If NULL, then write to stdout.
12260 OutputFormatType outputFormat
; // Format of test results output.
12261 Boolean outputAppendNewline
; // True if a newline character should be appended to JSON output.
12262 char hostname
[ 32 + 1 ]; // Base hostname that the replier is to use for instance and host names.
12263 char tag
[ 4 + 1 ]; // Tag that the replier is to use in its service types.
12265 } MDNSDiscoveryTestContext
;
12267 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext
);
12268 static void DNSSD_API
12269 _MDNSDiscoveryTestAboutQueryCallback(
12270 DNSServiceRef inSDRef
,
12271 DNSServiceFlags inFlags
,
12272 uint32_t inInterfaceIndex
,
12273 DNSServiceErrorType inError
,
12274 const char * inFullName
,
12277 uint16_t inRDataLen
,
12278 const void * inRDataPtr
,
12280 void * inContext
);
12282 _MDNSDiscoveryTestServiceBrowserCallback(
12283 ServiceBrowserResults
* inResults
,
12285 void * inContext
);
12286 static Boolean
_MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName
, const uint8_t *inTXTPtr
, size_t inTXTLen
);
12288 static void MDNSDiscoveryTestCmd( void )
12291 MDNSDiscoveryTestContext
* context
;
12292 char queryName
[ sizeof_field( MDNSDiscoveryTestContext
, hostname
) + 15 ];
12294 context
= (MDNSDiscoveryTestContext
*) calloc( 1, sizeof( *context
) );
12295 require_action( context
, exit
, err
= kNoMemoryErr
);
12297 err
= CheckIntegerArgument( gMDNSDiscoveryTest_InstanceCount
, "instance count", 1, UINT16_MAX
);
12298 require_noerr_quiet( err
, exit
);
12300 err
= CheckIntegerArgument( gMDNSDiscoveryTest_TXTSize
, "TXT size", 1, UINT16_MAX
);
12301 require_noerr_quiet( err
, exit
);
12303 err
= CheckIntegerArgument( gMDNSDiscoveryTest_BrowseTimeSecs
, "browse time (seconds)", 1, INT_MAX
);
12304 require_noerr_quiet( err
, exit
);
12306 err
= CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountA
, "A record count", 0, 64 );
12307 require_noerr_quiet( err
, exit
);
12309 err
= CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountAAAA
, "AAAA record count", 0, 64 );
12310 require_noerr_quiet( err
, exit
);
12312 err
= CheckDoubleArgument( gMDNSDiscoveryTest_UnicastDropRate
, "unicast drop rate", 0.0, 1.0 );
12313 require_noerr_quiet( err
, exit
);
12315 err
= CheckDoubleArgument( gMDNSDiscoveryTest_MulticastDropRate
, "multicast drop rate", 0.0, 1.0 );
12316 require_noerr_quiet( err
, exit
);
12318 err
= CheckIntegerArgument( gMDNSDiscoveryTest_MaxDropCount
, "drop count", 0, 255 );
12319 require_noerr_quiet( err
, exit
);
12321 context
->replierPID
= -1;
12322 context
->instanceCount
= (unsigned int) gMDNSDiscoveryTest_InstanceCount
;
12323 context
->txtSize
= (unsigned int) gMDNSDiscoveryTest_TXTSize
;
12324 context
->browseTimeSecs
= (unsigned int) gMDNSDiscoveryTest_BrowseTimeSecs
;
12325 context
->recordCountA
= (unsigned int) gMDNSDiscoveryTest_RecordCountA
;
12326 context
->recordCountAAAA
= (unsigned int) gMDNSDiscoveryTest_RecordCountAAAA
;
12327 context
->ucastDropRate
= gMDNSDiscoveryTest_UnicastDropRate
;
12328 context
->mcastDropRate
= gMDNSDiscoveryTest_MulticastDropRate
;
12329 context
->maxDropCount
= (unsigned int) gMDNSDiscoveryTest_MaxDropCount
;
12330 context
->outputFilePath
= gMDNSDiscoveryTest_OutputFilePath
;
12331 context
->outputAppendNewline
= gMDNSDiscoveryTest_OutputAppendNewline
? true : false;
12332 context
->noAdditionals
= gMDNSDiscoveryTest_NoAdditionals
? true : false;
12333 context
->useIPv4
= ( gMDNSDiscoveryTest_UseIPv4
|| !gMDNSDiscoveryTest_UseIPv6
) ? true : false;
12334 context
->useIPv6
= ( gMDNSDiscoveryTest_UseIPv6
|| !gMDNSDiscoveryTest_UseIPv4
) ? true : false;
12336 if( gMDNSDiscoveryTest_Interface
)
12338 err
= InterfaceIndexFromArgString( gMDNSDiscoveryTest_Interface
, &context
->ifIndex
);
12339 require_noerr_quiet( err
, exit
);
12343 err
= _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All
, NULL
, &context
->ifIndex
);
12344 require_noerr_quiet( err
, exit
);
12347 err
= OutputFormatFromArgString( gMDNSDiscoveryTest_OutputFormat
, &context
->outputFormat
);
12348 require_noerr_quiet( err
, exit
);
12350 if( gMDNSDiscoveryTest_FlushCache
)
12352 err
= CheckRootUser();
12353 require_noerr_quiet( err
, exit
);
12355 err
= systemf( NULL
, "killall -HUP mDNSResponder" );
12356 require_noerr( err
, exit
);
12358 context
->flushedCache
= true;
12361 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( context
->hostname
) - 1,
12362 context
->hostname
);
12363 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( context
->tag
) - 1, context
->tag
);
12365 ASPrintF( &context
->serviceType
, "_t-%s-%u-%u._tcp", context
->tag
, context
->txtSize
, context
->instanceCount
);
12366 require_action( context
->serviceType
, exit
, err
= kUnknownErr
);
12368 ASPrintF( &context
->replierCommand
,
12369 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount %u "
12370 "--countA %u --countAAAA %u --udrop %.1f --mdrop %.1f --maxDropCount %u %?s%?s%?s",
12371 (int64_t) getpid(),
12375 context
->instanceCount
,
12376 context
->recordCountA
,
12377 context
->recordCountAAAA
,
12378 context
->ucastDropRate
,
12379 context
->mcastDropRate
,
12380 context
->maxDropCount
,
12381 context
->noAdditionals
, " --noAdditionals",
12382 context
->useIPv4
, " --ipv4",
12383 context
->useIPv6
, " --ipv6" );
12384 require_action_quiet( context
->replierCommand
, exit
, err
= kUnknownErr
);
12386 err
= SpawnCommand( &context
->replierPID
, "%s", context
->replierCommand
);
12387 require_noerr_quiet( err
, exit
);
12389 // Query for the replier's about TXT record. A response means it's fully up and running.
12391 SNPrintF( queryName
, sizeof( queryName
), "about.%s.local.", context
->hostname
);
12392 err
= DNSServiceQueryRecord( &context
->query
, kDNSServiceFlagsForceMulticast
, context
->ifIndex
, queryName
,
12393 kDNSServiceType_TXT
, kDNSServiceClass_IN
, _MDNSDiscoveryTestAboutQueryCallback
, context
);
12394 require_noerr( err
, exit
);
12396 err
= DNSServiceSetDispatchQueue( context
->query
, dispatch_get_main_queue() );
12397 require_noerr( err
, exit
);
12399 err
= DispatchTimerCreate( dispatch_time_seconds( kMDNSDiscoveryTestFirstQueryTimeoutSecs
),
12400 DISPATCH_TIME_FOREVER
, UINT64_C_safe( kMDNSDiscoveryTestFirstQueryTimeoutSecs
) * kNanosecondsPerSecond
/ 10, NULL
,
12401 _MDNSDiscoveryTestFirstQueryTimeout
, NULL
, context
, &context
->queryTimer
);
12402 require_noerr( err
, exit
);
12403 dispatch_resume( context
->queryTimer
);
12405 context
->startTime
= NanoTimeGetCurrent();
12412 //===========================================================================================================================
12413 // _MDNSDiscoveryTestFirstQueryTimeout
12414 //===========================================================================================================================
12416 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext
)
12418 MDNSDiscoveryTestContext
* const context
= (MDNSDiscoveryTestContext
*) inContext
;
12420 dispatch_source_forget( &context
->queryTimer
);
12422 FPrintF( stderr
, "error: Query for mdnsreplier's \"about\" TXT record timed out.\n" );
12426 //===========================================================================================================================
12427 // _MDNSDiscoveryTestAboutQueryCallback
12428 //===========================================================================================================================
12430 static void DNSSD_API
12431 _MDNSDiscoveryTestAboutQueryCallback(
12432 DNSServiceRef inSDRef
,
12433 DNSServiceFlags inFlags
,
12434 uint32_t inInterfaceIndex
,
12435 DNSServiceErrorType inError
,
12436 const char * inFullName
,
12439 uint16_t inRDataLen
,
12440 const void * inRDataPtr
,
12445 MDNSDiscoveryTestContext
* const context
= (MDNSDiscoveryTestContext
*) inContext
;
12448 Unused( inInterfaceIndex
);
12449 Unused( inFullName
);
12452 Unused( inRDataLen
);
12453 Unused( inRDataPtr
);
12457 require_noerr( err
, exit
);
12458 require_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
);
12460 DNSServiceForget( &context
->query
);
12461 dispatch_source_forget( &context
->queryTimer
);
12463 err
= ServiceBrowserCreate( dispatch_get_main_queue(), 0, "local.", context
->browseTimeSecs
, false, &context
->browser
);
12464 require_noerr( err
, exit
);
12466 err
= ServiceBrowserAddServiceType( context
->browser
, context
->serviceType
);
12467 require_noerr( err
, exit
);
12469 ServiceBrowserSetCallback( context
->browser
, _MDNSDiscoveryTestServiceBrowserCallback
, context
);
12470 ServiceBrowserStart( context
->browser
);
12473 if( err
) exit( 1 );
12476 //===========================================================================================================================
12477 // _MDNSDiscoveryTestServiceBrowserCallback
12478 //===========================================================================================================================
12480 #define kMDNSDiscoveryTestResultsKey_ReplierInfo CFSTR( "replierInfo" )
12481 #define kMDNSDiscoveryTestResultsKey_StartTime CFSTR( "startTime" )
12482 #define kMDNSDiscoveryTestResultsKey_EndTime CFSTR( "endTime" )
12483 #define kMDNSDiscoveryTestResultsKey_BrowseTimeSecs CFSTR( "browseTimeSecs" )
12484 #define kMDNSDiscoveryTestResultsKey_ServiceType CFSTR( "serviceType" )
12485 #define kMDNSDiscoveryTestResultsKey_FlushedCache CFSTR( "flushedCache" )
12486 #define kMDNSDiscoveryTestResultsKey_UnexpectedInstances CFSTR( "unexpectedInstances" )
12487 #define kMDNSDiscoveryTestResultsKey_MissingInstances CFSTR( "missingInstances" )
12488 #define kMDNSDiscoveryTestResultsKey_IncorrectInstances CFSTR( "incorrectInstances" )
12489 #define kMDNSDiscoveryTestResultsKey_Success CFSTR( "success" )
12490 #define kMDNSDiscoveryTestResultsKey_TotalResolveTime CFSTR( "totalResolveTimeUs" )
12492 #define kMDNSDiscoveryTestReplierInfoKey_Command CFSTR( "command" )
12493 #define kMDNSDiscoveryTestReplierInfoKey_InstanceCount CFSTR( "instanceCount" )
12494 #define kMDNSDiscoveryTestReplierInfoKey_TXTSize CFSTR( "txtSize" )
12495 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountA CFSTR( "recordCountA" )
12496 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA CFSTR( "recordCountAAAA" )
12497 #define kMDNSDiscoveryTestReplierInfoKey_Hostname CFSTR( "hostname" )
12498 #define kMDNSDiscoveryTestReplierInfoKey_NoAdditionals CFSTR( "noAdditionals" )
12499 #define kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate CFSTR( "ucastDropRate" )
12500 #define kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate CFSTR( "mcastDropRate" )
12501 #define kMDNSDiscoveryTestReplierInfoKey_MaxDropCount CFSTR( "maxDropCount" )
12503 #define kMDNSDiscoveryTestUnexpectedInstanceKey_Name CFSTR( "name" )
12504 #define kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex CFSTR( "interfaceIndex" )
12506 #define kMDNSDiscoveryTestIncorrectInstanceKey_Name CFSTR( "name" )
12507 #define kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve CFSTR( "didResolve" )
12508 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname CFSTR( "badHostname" )
12509 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadPort CFSTR( "badPort" )
12510 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT CFSTR( "badTXT" )
12511 #define kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs CFSTR( "unexpectedAddrs" )
12512 #define kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs CFSTR( "missingAddrs" )
12514 static void _MDNSDiscoveryTestServiceBrowserCallback( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
)
12517 MDNSDiscoveryTestContext
* const context
= (MDNSDiscoveryTestContext
*) inContext
;
12518 const SBRDomain
* domain
;
12519 const SBRServiceType
* type
;
12520 const SBRServiceInstance
* instance
;
12521 const SBRServiceInstance
** instanceArray
= NULL
;
12522 const SBRIPAddress
* ipaddr
;
12523 size_t hostnameLen
;
12528 CFMutableArrayRef unexpectedInstances
;
12529 CFMutableArrayRef missingInstances
;
12530 CFMutableArrayRef incorrectInstances
;
12531 CFMutableDictionaryRef plist
= NULL
;
12532 CFMutableDictionaryRef badDict
= NULL
;
12533 CFMutableArrayRef unexpectedAddrs
= NULL
;
12534 CFMutableArrayRef missingAddrs
= NULL
;
12535 uint64_t maxResolveTimeUs
;
12536 int success
= false;
12537 char startTime
[ 32 ];
12538 char endTime
[ 32 ];
12540 context
->endTime
= NanoTimeGetCurrent();
12543 require_noerr( err
, exit
);
12545 _NanoTime64ToTimestamp( context
->startTime
, startTime
, sizeof( startTime
) );
12546 _NanoTime64ToTimestamp( context
->endTime
, endTime
, sizeof( endTime
) );
12547 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
12551 "%kO=%s" // replierCommand
12552 "%kO=%lli" // txtSize
12553 "%kO=%lli" // instanceCount
12554 "%kO=%lli" // recordCountA
12555 "%kO=%lli" // recordCountAAAA
12556 "%kO=%s" // hostname
12557 "%kO=%b" // noAdditionals
12558 "%kO=%f" // ucastDropRate
12559 "%kO=%f" // mcastDropRate
12560 "%kO=%i" // maxDropCount
12562 "%kO=%s" // startTime
12563 "%kO=%s" // endTime
12564 "%kO=%lli" // browseTimeSecs
12565 "%kO=%s" // serviceType
12566 "%kO=%b" // flushedCache
12567 "%kO=[%@]" // unexpectedInstances
12568 "%kO=[%@]" // missingInstances
12569 "%kO=[%@]" // incorrectInstances
12571 kMDNSDiscoveryTestResultsKey_ReplierInfo
,
12572 kMDNSDiscoveryTestReplierInfoKey_Command
, context
->replierCommand
,
12573 kMDNSDiscoveryTestReplierInfoKey_InstanceCount
, (int64_t) context
->instanceCount
,
12574 kMDNSDiscoveryTestReplierInfoKey_TXTSize
, (int64_t) context
->txtSize
,
12575 kMDNSDiscoveryTestReplierInfoKey_RecordCountA
, (int64_t) context
->recordCountA
,
12576 kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA
, (int64_t) context
->recordCountAAAA
,
12577 kMDNSDiscoveryTestReplierInfoKey_Hostname
, context
->hostname
,
12578 kMDNSDiscoveryTestReplierInfoKey_NoAdditionals
, context
->noAdditionals
,
12579 kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate
, context
->ucastDropRate
,
12580 kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate
, context
->mcastDropRate
,
12581 kMDNSDiscoveryTestReplierInfoKey_MaxDropCount
, context
->maxDropCount
,
12582 kMDNSDiscoveryTestResultsKey_StartTime
, startTime
,
12583 kMDNSDiscoveryTestResultsKey_EndTime
, endTime
,
12584 kMDNSDiscoveryTestResultsKey_BrowseTimeSecs
, (int64_t) context
->browseTimeSecs
,
12585 kMDNSDiscoveryTestResultsKey_ServiceType
, context
->serviceType
,
12586 kMDNSDiscoveryTestResultsKey_FlushedCache
, context
->flushedCache
,
12587 kMDNSDiscoveryTestResultsKey_UnexpectedInstances
, &unexpectedInstances
,
12588 kMDNSDiscoveryTestResultsKey_MissingInstances
, &missingInstances
,
12589 kMDNSDiscoveryTestResultsKey_IncorrectInstances
, &incorrectInstances
);
12590 require_noerr( err
, exit
);
12592 for( domain
= inResults
->domainList
; domain
&& ( strcasecmp( domain
->name
, "local." ) != 0 ); domain
= domain
->next
) {}
12593 require_action( domain
, exit
, err
= kInternalErr
);
12595 for( type
= domain
->typeList
; type
&& ( strcasecmp( type
->name
, context
->serviceType
) != 0 ); type
= type
->next
) {}
12596 require_action( type
, exit
, err
= kInternalErr
);
12598 instanceArray
= (const SBRServiceInstance
**) calloc( context
->instanceCount
, sizeof( *instanceArray
) );
12599 require_action( instanceArray
, exit
, err
= kNoMemoryErr
);
12601 hostnameLen
= strlen( context
->hostname
);
12602 for( instance
= type
->instanceList
; instance
; instance
= instance
->next
)
12604 unsigned int instanceNumber
= 0;
12606 if( strcmp_prefix( instance
->name
, context
->hostname
) == 0 )
12608 ptr
= &instance
->name
[ hostnameLen
];
12609 if( ( ptr
[ 0 ] == ' ' ) && ( ptr
[ 1 ] == '(' ) )
12612 for( end
= ptr
; isdigit_safe( *end
); ++end
) {}
12613 if( DecimalTextToUInt32( ptr
, end
, &u32
, &ptr
) == kNoErr
)
12615 if( ( u32
>= 2 ) && ( u32
<= context
->instanceCount
) && ( ptr
[ 0 ] == ')' ) && ( ptr
[ 1 ] == '\0' ) )
12617 instanceNumber
= u32
;
12621 else if( *ptr
== '\0' )
12623 instanceNumber
= 1;
12626 if( ( instanceNumber
!= 0 ) && ( instance
->ifIndex
== context
->ifIndex
) )
12628 check( !instanceArray
[ instanceNumber
- 1 ] );
12629 instanceArray
[ instanceNumber
- 1 ] = instance
;
12633 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, unexpectedInstances
,
12638 kMDNSDiscoveryTestUnexpectedInstanceKey_Name
, instance
->name
,
12639 kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex
, (int64_t) instance
->ifIndex
);
12640 require_noerr( err
, exit
);
12644 maxResolveTimeUs
= 0;
12645 for( i
= 1; i
<= context
->instanceCount
; ++i
)
12647 int isHostnameValid
;
12650 instance
= instanceArray
[ i
- 1 ];
12655 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingInstances
, "%s", context
->hostname
);
12656 require_noerr( err
, exit
);
12660 char * instanceName
= NULL
;
12662 ASPrintF( &instanceName
, "%s (%u)", context
->hostname
, i
);
12663 require_action( instanceName
, exit
, err
= kUnknownErr
);
12665 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingInstances
, "%s", instanceName
);
12666 free( instanceName
);
12667 require_noerr( err
, exit
);
12672 if( !instance
->hostname
)
12674 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, incorrectInstances
,
12679 kMDNSDiscoveryTestIncorrectInstanceKey_Name
, instance
->name
,
12680 kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve
, false );
12681 require_noerr( err
, exit
);
12685 badDict
= CFDictionaryCreateMutable( NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
12686 require_action( badDict
, exit
, err
= kNoMemoryErr
);
12688 isHostnameValid
= false;
12689 if( strcmp_prefix( instance
->hostname
, context
->hostname
) == 0 )
12691 ptr
= &instance
->hostname
[ hostnameLen
];
12694 if( strcmp( ptr
, ".local." ) == 0 ) isHostnameValid
= true;
12696 else if( *ptr
== '-' )
12699 for( end
= ptr
; isdigit_safe( *end
); ++end
) {}
12700 if( DecimalTextToUInt32( ptr
, end
, &u32
, &ptr
) == kNoErr
)
12702 if( ( u32
== i
) && ( strcmp( ptr
, ".local." ) == 0 ) ) isHostnameValid
= true;
12706 if( !isHostnameValid
)
12708 err
= CFDictionarySetCString( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname
, instance
->hostname
,
12710 require_noerr( err
, exit
);
12713 if( instance
->port
!= (uint16_t)( kMDNSReplierPortBase
+ context
->txtSize
) )
12715 err
= CFDictionarySetInt64( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_BadPort
, instance
->port
);
12716 require_noerr( err
, exit
);
12719 isTXTValid
= false;
12720 if( instance
->txtLen
== context
->txtSize
)
12722 uint8_t name
[ kDomainNameLengthMax
];
12724 err
= DomainNameFromString( name
, instance
->name
, NULL
);
12725 require_noerr( err
, exit
);
12727 err
= DomainNameAppendString( name
, type
->name
, NULL
);
12728 require_noerr( err
, exit
);
12730 err
= DomainNameAppendString( name
, "local", NULL
);
12731 require_noerr( err
, exit
);
12733 if( _MDNSDiscoveryTestTXTRecordIsValid( name
, instance
->txtPtr
, instance
->txtLen
) ) isTXTValid
= true;
12737 char * hexStr
= NULL
;
12739 ASPrintF( &hexStr
, "%.4H", instance
->txtPtr
, (int) instance
->txtLen
, (int) instance
->txtLen
);
12740 require_action( hexStr
, exit
, err
= kUnknownErr
);
12742 err
= CFDictionarySetCString( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT
, hexStr
, kSizeCString
);
12744 require_noerr( err
, exit
);
12747 if( isHostnameValid
)
12749 uint64_t addrV4Bitmap
, addrV6Bitmap
, bitmask
, resolveTimeUs
;
12751 uint8_t addrV4
[ 4 ];
12752 uint8_t addrV6
[ 16 ];
12754 if( context
->recordCountA
< 64 ) addrV4Bitmap
= ( UINT64_C( 1 ) << context
->recordCountA
) - 1;
12755 else addrV4Bitmap
= ~UINT64_C( 0 );
12757 if( context
->recordCountAAAA
< 64 ) addrV6Bitmap
= ( UINT64_C( 1 ) << context
->recordCountAAAA
) - 1;
12758 else addrV6Bitmap
= ~UINT64_C( 0 );
12761 WriteBig16( &addrV4
[ 1 ], i
);
12764 memcpy( addrV6
, kMDNSReplierBaseAddrV6
, 16 );
12765 WriteBig16( &addrV6
[ 12 ], i
);
12767 unexpectedAddrs
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
12768 require_action( unexpectedAddrs
, exit
, err
= kNoMemoryErr
);
12771 for( ipaddr
= instance
->ipaddrList
; ipaddr
; ipaddr
= ipaddr
->next
)
12773 const uint8_t * addrPtr
;
12775 int isAddrValid
= false;
12777 if( ipaddr
->sip
.sa
.sa_family
== AF_INET
)
12779 addrPtr
= (const uint8_t *) &ipaddr
->sip
.v4
.sin_addr
.s_addr
;
12780 lsb
= addrPtr
[ 3 ];
12781 if( ( memcmp( addrPtr
, addrV4
, 3 ) == 0 ) && ( lsb
>= 1 ) && ( lsb
<= context
->recordCountA
) )
12783 bitmask
= UINT64_C( 1 ) << ( lsb
- 1 );
12784 addrV4Bitmap
&= ~bitmask
;
12785 isAddrValid
= true;
12788 else if( ipaddr
->sip
.sa
.sa_family
== AF_INET6
)
12790 addrPtr
= ipaddr
->sip
.v6
.sin6_addr
.s6_addr
;
12791 lsb
= addrPtr
[ 15 ];
12792 if( ( memcmp( addrPtr
, addrV6
, 15 ) == 0 ) && ( lsb
>= 1 ) && ( lsb
<= context
->recordCountAAAA
) )
12794 bitmask
= UINT64_C( 1 ) << ( lsb
- 1 );
12795 addrV6Bitmap
&= ~bitmask
;
12796 isAddrValid
= true;
12801 if( ipaddr
->resolveTimeUs
> resolveTimeUs
) resolveTimeUs
= ipaddr
->resolveTimeUs
;
12805 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, unexpectedAddrs
, "%##a", &ipaddr
->sip
);
12806 require_noerr( err
, exit
);
12810 resolveTimeUs
+= ( instance
->discoverTimeUs
+ instance
->resolveTimeUs
);
12811 if( resolveTimeUs
> maxResolveTimeUs
) maxResolveTimeUs
= resolveTimeUs
;
12813 if( CFArrayGetCount( unexpectedAddrs
) > 0 )
12815 CFDictionarySetValue( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs
, unexpectedAddrs
);
12817 ForgetCF( &unexpectedAddrs
);
12819 missingAddrs
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
12820 require_action( missingAddrs
, exit
, err
= kNoMemoryErr
);
12822 for( j
= 1; addrV4Bitmap
!= 0; ++j
)
12824 bitmask
= UINT64_C( 1 ) << ( j
- 1 );
12825 if( addrV4Bitmap
& bitmask
)
12827 addrV4Bitmap
&= ~bitmask
;
12828 addrV4
[ 3 ] = (uint8_t) j
;
12829 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingAddrs
, "%.4a", addrV4
);
12830 require_noerr( err
, exit
);
12833 for( j
= 1; addrV6Bitmap
!= 0; ++j
)
12835 bitmask
= UINT64_C( 1 ) << ( j
- 1 );
12836 if( addrV6Bitmap
& bitmask
)
12838 addrV6Bitmap
&= ~bitmask
;
12839 addrV6
[ 15 ] = (uint8_t) j
;
12840 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingAddrs
, "%.16a", addrV6
);
12841 require_noerr( err
, exit
);
12845 if( CFArrayGetCount( missingAddrs
) > 0 )
12847 CFDictionarySetValue( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs
, missingAddrs
);
12849 ForgetCF( &missingAddrs
);
12852 if( CFDictionaryGetCount( badDict
) > 0 )
12854 err
= CFDictionarySetCString( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_Name
, instance
->name
,
12856 require_noerr( err
, exit
);
12858 CFDictionarySetBoolean( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve
, true );
12859 CFArrayAppendValue( incorrectInstances
, badDict
);
12861 ForgetCF( &badDict
);
12864 if( ( CFArrayGetCount( unexpectedInstances
) == 0 ) &&
12865 ( CFArrayGetCount( missingInstances
) == 0 ) &&
12866 ( CFArrayGetCount( incorrectInstances
) == 0 ) )
12868 err
= CFDictionarySetInt64( plist
, kMDNSDiscoveryTestResultsKey_TotalResolveTime
, (int64_t) maxResolveTimeUs
);
12869 require_noerr( err
, exit
);
12876 CFDictionarySetBoolean( plist
, kMDNSDiscoveryTestResultsKey_Success
, success
);
12878 err
= OutputPropertyList( plist
, context
->outputFormat
, context
->outputFilePath
);
12879 require_noerr_quiet( err
, exit
);
12882 ForgetCF( &context
->browser
);
12883 if( context
->replierPID
!= -1 )
12885 kill( context
->replierPID
, SIGTERM
);
12886 context
->replierPID
= -1;
12888 FreeNullSafe( instanceArray
);
12889 CFReleaseNullSafe( plist
);
12890 CFReleaseNullSafe( badDict
);
12891 CFReleaseNullSafe( unexpectedAddrs
);
12892 CFReleaseNullSafe( missingAddrs
);
12893 exit( err
? 1 : ( success
? 0 : 2 ) );
12896 //===========================================================================================================================
12897 // _MDNSDiscoveryTestTXTRecordIsValid
12898 //===========================================================================================================================
12900 static Boolean
_MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName
, const uint8_t *inTXTPtr
, size_t inTXTLen
)
12904 const uint8_t * ptr
;
12905 size_t i
, wholeCount
, remCount
;
12906 uint8_t txtStr
[ 16 ];
12908 if( inTXTLen
== 0 ) return( false );
12910 hash
= _FNV1( inRecordName
, DomainNameLength( inRecordName
) );
12913 n
= MemPrintF( &txtStr
[ 1 ], 15, "hash=0x%08X", hash
);
12917 wholeCount
= inTXTLen
/ 16;
12918 for( i
= 0; i
< wholeCount
; ++i
)
12920 if( memcmp( ptr
, txtStr
, 16 ) != 0 ) return( false );
12924 remCount
= inTXTLen
% 16;
12927 txtStr
[ 0 ] = (uint8_t)( remCount
- 1 );
12928 if( memcmp( ptr
, txtStr
, remCount
) != 0 ) return( false );
12931 check( ptr
== &inTXTPtr
[ inTXTLen
] );
12935 //===========================================================================================================================
12937 //===========================================================================================================================
12939 #define kDotLocalTestPreparationTimeLimitSecs 5
12940 #define kDotLocalTestSubtestDurationSecs 5
12942 // Constants for SRV record query subtest.
12944 #define kDotLocalTestSRV_Priority 1
12945 #define kDotLocalTestSRV_Weight 0
12946 #define kDotLocalTestSRV_Port 80
12947 #define kDotLocalTestSRV_TargetName ( (const uint8_t *) "\x03" "www" "\x07" "example" "\x03" "com" )
12948 #define kDotLocalTestSRV_TargetStr "www.example.com."
12949 #define kDotLocalTestSRV_ResultStr "1 0 80 " kDotLocalTestSRV_TargetStr
12953 kDotLocalTestState_Unset
= 0,
12954 kDotLocalTestState_Preparing
= 1,
12955 kDotLocalTestState_GAIMDNSOnly
= 2,
12956 kDotLocalTestState_GAIDNSOnly
= 3,
12957 kDotLocalTestState_GAIBoth
= 4,
12958 kDotLocalTestState_GAINeither
= 5,
12959 kDotLocalTestState_GAINoSuchRecord
= 6,
12960 kDotLocalTestState_QuerySRV
= 7,
12961 kDotLocalTestState_Done
= 8
12963 } DotLocalTestState
;
12967 const char * testDesc
; // Description of the current subtest.
12968 char * queryName
; // Query name for GetAddrInfo or QueryRecord operation.
12969 dispatch_source_t timer
; // Timer used for limiting the time for each subtest.
12970 NanoTime64 startTime
; // Timestamp of when the subtest started.
12971 NanoTime64 endTime
; // Timestamp of when the subtest ended.
12972 CFMutableArrayRef correctResults
; // Operation results that were expected.
12973 CFMutableArrayRef duplicateResults
; // Operation results that were expected, but were already received.
12974 CFMutableArrayRef unexpectedResults
; // Operation results that were unexpected.
12975 OSStatus error
; // Subtest's error code.
12976 uint32_t addrDNSv4
; // If hasDNSv4 is true, the expected DNS IPv4 address for queryName.
12977 uint32_t addrMDNSv4
; // If hasMDNSv4 is true, the expected MDNS IPv4 address for queryName.
12978 uint8_t addrDNSv6
[ 16 ]; // If hasDNSv6 is true, the expected DNS IPv6 address for queryName.
12979 uint8_t addrMDNSv6
[ 16 ]; // If hasMDNSv6 is true, the expected MDNS IPv6 address for queryName.
12980 Boolean hasDNSv4
; // True if queryName has a DNS IPv4 address.
12981 Boolean hasDNSv6
; // True if queryName has a DNS IPv6 address.
12982 Boolean hasMDNSv4
; // True if queryName has an MDNS IPv4 address.
12983 Boolean hasMDNSv6
; // True if queryName has an MDNS IPv6 address.
12984 Boolean needDNSv4
; // True if operation is expecting, but hasn't received a DNS IPv4 result.
12985 Boolean needDNSv6
; // True if operation is expecting, but hasn't received a DNS IPv6 result.
12986 Boolean needMDNSv4
; // True if operation is expecting, but hasn't received an MDNS IPv4 result.
12987 Boolean needMDNSv6
; // True if operation is expecting, but hasn't received an MDNS IPv6 result.
12988 Boolean needSRV
; // True if operation is expecting, but hasn't received an SRV result.
12994 dispatch_source_t timer
; // Timer used for limiting the time for each state/subtest.
12995 DotLocalSubtest
* subtest
; // Current subtest's state.
12996 DNSServiceRef connection
; // Shared connection for DNS-SD operations.
12997 DNSServiceRef op
; // Reference for the current DNS-SD operation.
12998 DNSServiceRef op2
; // Reference for mdnsreplier probe query used during preparing state.
12999 DNSRecordRef localSOARef
; // Reference returned by DNSServiceRegisterRecord() for local. SOA record.
13000 char * replierCmd
; // Command used to invoke the mdnsreplier.
13001 char * serverCmd
; // Command used to invoke the test DNS server.
13002 CFMutableArrayRef reportsGAI
; // Reports for subtests that use DNSServiceGetAddrInfo.
13003 CFMutableArrayRef reportsQuerySRV
; // Reports for subtests that use DNSServiceQueryRecord for SRV records.
13004 NanoTime64 startTime
; // Timestamp for when the test started.
13005 NanoTime64 endTime
; // Timestamp for when the test ended.
13006 DotLocalTestState state
; // The test's current state.
13007 pid_t replierPID
; // PID of spawned mdnsreplier.
13008 pid_t serverPID
; // PID of spawned test DNS server.
13009 uint32_t ifIndex
; // Interface index used for mdnsreplier.
13010 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout.
13011 OutputFormatType outputFormat
; // Format of test results output.
13012 Boolean registeredSOA
; // True if the dummy local. SOA record was successfully registered.
13013 Boolean serverIsReady
; // True if response was received for test DNS server probe query.
13014 Boolean replierIsReady
; // True if response was received for mdnsreplier probe query.
13015 Boolean testFailed
; // True if at least one subtest failed.
13016 char labelStr
[ 20 + 1 ]; // Unique label string used for for making the query names used by subtests.
13017 // The format of this string is "dotlocal-test-<six random chars>".
13018 } DotLocalTestContext
;
13020 static void _DotLocalTestStateMachine( DotLocalTestContext
*inContext
);
13021 static void DNSSD_API
13022 _DotLocalTestProbeQueryRecordCallback(
13023 DNSServiceRef inSDRef
,
13024 DNSServiceFlags inFlags
,
13025 uint32_t inInterfaceIndex
,
13026 DNSServiceErrorType inError
,
13027 const char * inFullName
,
13030 uint16_t inRDataLen
,
13031 const void * inRDataPtr
,
13033 void * inContext
);
13034 static void DNSSD_API
13035 _DotLocalTestRegisterRecordCallback(
13036 DNSServiceRef inSDRef
,
13037 DNSRecordRef inRecordRef
,
13038 DNSServiceFlags inFlags
,
13039 DNSServiceErrorType inError
,
13040 void * inContext
);
13041 static void _DotLocalTestTimerHandler( void *inContext
);
13042 static void DNSSD_API
13043 _DotLocalTestGAICallback(
13044 DNSServiceRef inSDRef
,
13045 DNSServiceFlags inFlags
,
13046 uint32_t inInterfaceIndex
,
13047 DNSServiceErrorType inError
,
13048 const char * inHostname
,
13049 const struct sockaddr
* inSockAddr
,
13051 void * inContext
);
13052 static void DNSSD_API
13053 _DotLocalTestQueryRecordCallback(
13054 DNSServiceRef inSDRef
,
13055 DNSServiceFlags inFlags
,
13056 uint32_t inInterfaceIndex
,
13057 DNSServiceErrorType inError
,
13058 const char * inFullName
,
13061 uint16_t inRDataLen
,
13062 const void * inRDataPtr
,
13064 void * inContext
);
13066 static void DotLocalTestCmd( void )
13069 DotLocalTestContext
* context
;
13070 uint8_t * rdataPtr
;
13072 DNSServiceFlags flags
;
13073 char queryName
[ 64 ];
13074 char randBuf
[ 6 + 1 ]; // Large enough for four and six character random strings below.
13076 context
= (DotLocalTestContext
*) calloc( 1, sizeof( *context
) );
13077 require_action( context
, exit
, err
= kNoMemoryErr
);
13079 context
->startTime
= NanoTimeGetCurrent();
13080 context
->endTime
= kNanoTime_Invalid
;
13082 context
->state
= kDotLocalTestState_Preparing
;
13084 if( gDotLocalTest_Interface
)
13086 err
= InterfaceIndexFromArgString( gDotLocalTest_Interface
, &context
->ifIndex
);
13087 require_noerr_quiet( err
, exit
);
13091 err
= _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All
, NULL
, &context
->ifIndex
);
13092 require_noerr_quiet( err
, exit
);
13095 if( gDotLocalTest_OutputFilePath
)
13097 context
->outputFilePath
= strdup( gDotLocalTest_OutputFilePath
);
13098 require_action( context
->outputFilePath
, exit
, err
= kNoMemoryErr
);
13101 err
= OutputFormatFromArgString( gDotLocalTest_OutputFormat
, &context
->outputFormat
);
13102 require_noerr_quiet( err
, exit
);
13104 context
->reportsGAI
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13105 require_action( context
->reportsGAI
, exit
, err
= kNoMemoryErr
);
13107 context
->reportsQuerySRV
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13108 require_action( context
->reportsQuerySRV
, exit
, err
= kNoMemoryErr
);
13110 SNPrintF( context
->labelStr
, sizeof( context
->labelStr
), "dotlocal-test-%s",
13111 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, 6, randBuf
) );
13113 // Spawn an mdnsreplier.
13115 ASPrintF( &context
->replierCmd
,
13116 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount 2 --countA 1"
13118 (int64_t) getpid(), context
->ifIndex
, context
->labelStr
,
13119 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, 4, randBuf
) );
13120 require_action_quiet( context
->replierCmd
, exit
, err
= kUnknownErr
);
13122 err
= SpawnCommand( &context
->replierPID
, "%s", context
->replierCmd
);
13123 require_noerr( err
, exit
);
13125 // Spawn a test DNS server
13127 ASPrintF( &context
->serverCmd
,
13128 "dnssdutil server --loopback --follow %lld --port 0 --defaultTTL 300 --domain %s.local.",
13129 (int64_t) getpid(), context
->labelStr
);
13130 require_action_quiet( context
->serverCmd
, exit
, err
= kUnknownErr
);
13132 err
= SpawnCommand( &context
->serverPID
, "%s", context
->serverCmd
);
13133 require_noerr( err
, exit
);
13135 // Create a shared DNS-SD connection.
13137 err
= DNSServiceCreateConnection( &context
->connection
);
13138 require_noerr( err
, exit
);
13140 err
= DNSServiceSetDispatchQueue( context
->connection
, dispatch_get_main_queue() );
13141 require_noerr( err
, exit
);
13143 // Create probe query for DNS server, i.e., query for any name that has an A record.
13145 SNPrintF( queryName
, sizeof( queryName
), "tag-dotlocal-test-probe.ipv4.%s.local.", context
->labelStr
);
13147 flags
= kDNSServiceFlagsShareConnection
;
13148 #if( TARGET_OS_WATCH )
13149 flags
|= kDNSServiceFlagsPathEvaluationDone
;
13152 context
->op
= context
->connection
;
13153 err
= DNSServiceQueryRecord( &context
->op
, flags
, kDNSServiceInterfaceIndexAny
, queryName
, kDNSServiceType_A
,
13154 kDNSServiceClass_IN
, _DotLocalTestProbeQueryRecordCallback
, context
);
13155 require_noerr( err
, exit
);
13157 // Create probe query for mdnsreplier's "about" TXT record.
13159 SNPrintF( queryName
, sizeof( queryName
), "about.%s.local.", context
->labelStr
);
13161 flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsForceMulticast
;
13162 #if( TARGET_OS_WATCH )
13163 flags
|= kDNSServiceFlagsPathEvaluationDone
;
13166 context
->op2
= context
->connection
;
13167 err
= DNSServiceQueryRecord( &context
->op2
, flags
, context
->ifIndex
, queryName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
,
13168 _DotLocalTestProbeQueryRecordCallback
, context
);
13169 require_noerr( err
, exit
);
13171 // Register a dummy local. SOA record.
13173 err
= CreateSOARecordData( kRootLabel
, kRootLabel
, 1976040101, 1 * kSecondsPerDay
, 2 * kSecondsPerHour
,
13174 1000 * kSecondsPerHour
, 2 * kSecondsPerDay
, &rdataPtr
, &rdataLen
);
13175 require_noerr( err
, exit
);
13177 err
= DNSServiceRegisterRecord( context
->connection
, &context
->localSOARef
, kDNSServiceFlagsUnique
,
13178 kDNSServiceInterfaceIndexLocalOnly
, "local.", kDNSServiceType_SOA
, kDNSServiceClass_IN
, 1,
13179 rdataPtr
, 1 * kSecondsPerHour
, _DotLocalTestRegisterRecordCallback
, context
);
13180 require_noerr( err
, exit
);
13182 // Start timer for probe responses and SOA record registration.
13184 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestPreparationTimeLimitSecs
),
13185 INT64_C_safe( kDotLocalTestPreparationTimeLimitSecs
) * kNanosecondsPerSecond
/ 10, dispatch_get_main_queue(),
13186 _DotLocalTestTimerHandler
, context
, &context
->timer
);
13187 require_noerr( err
, exit
);
13188 dispatch_resume( context
->timer
);
13193 if( err
) ErrQuit( 1, "error: %#m\n", err
);
13196 //===========================================================================================================================
13197 // _DotLocalTestStateMachine
13198 //===========================================================================================================================
13200 static OSStatus
_DotLocalSubtestCreate( DotLocalSubtest
**outSubtest
);
13201 static void _DotLocalSubtestFree( DotLocalSubtest
*inSubtest
);
13202 static OSStatus
_DotLocalTestStartSubtest( DotLocalTestContext
*inContext
);
13203 static OSStatus
_DotLocalTestFinalizeSubtest( DotLocalTestContext
*inContext
);
13204 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext
*inContext
) ATTRIBUTE_NORETURN
;
13206 static void _DotLocalTestStateMachine( DotLocalTestContext
*inContext
)
13209 DotLocalTestState nextState
;
13211 DNSServiceForget( &inContext
->op
);
13212 DNSServiceForget( &inContext
->op2
);
13213 dispatch_source_forget( &inContext
->timer
);
13215 switch( inContext
->state
)
13217 case kDotLocalTestState_Preparing
: nextState
= kDotLocalTestState_GAIMDNSOnly
; break;
13218 case kDotLocalTestState_GAIMDNSOnly
: nextState
= kDotLocalTestState_GAIDNSOnly
; break;
13219 case kDotLocalTestState_GAIDNSOnly
: nextState
= kDotLocalTestState_GAIBoth
; break;
13220 case kDotLocalTestState_GAIBoth
: nextState
= kDotLocalTestState_GAINeither
; break;
13221 case kDotLocalTestState_GAINeither
: nextState
= kDotLocalTestState_GAINoSuchRecord
; break;
13222 case kDotLocalTestState_GAINoSuchRecord
: nextState
= kDotLocalTestState_QuerySRV
; break;
13223 case kDotLocalTestState_QuerySRV
: nextState
= kDotLocalTestState_Done
; break;
13224 default: err
= kStateErr
; goto exit
;
13227 if( inContext
->state
== kDotLocalTestState_Preparing
)
13229 if( !inContext
->registeredSOA
|| !inContext
->serverIsReady
|| !inContext
->replierIsReady
)
13231 FPrintF( stderr
, "Preparation timed out: Registered SOA? %s. Server ready? %s. mdnsreplier ready? %s.\n",
13232 YesNoStr( inContext
->registeredSOA
),
13233 YesNoStr( inContext
->serverIsReady
),
13234 YesNoStr( inContext
->replierIsReady
) );
13235 err
= kNotPreparedErr
;
13241 err
= _DotLocalTestFinalizeSubtest( inContext
);
13242 require_noerr( err
, exit
);
13245 inContext
->state
= nextState
;
13246 if( inContext
->state
== kDotLocalTestState_Done
) _DotLocalTestFinalizeAndExit( inContext
);
13247 err
= _DotLocalTestStartSubtest( inContext
);
13250 if( err
) ErrQuit( 1, "error: %#m\n", err
);
13253 //===========================================================================================================================
13254 // _DotLocalSubtestCreate
13255 //===========================================================================================================================
13257 static OSStatus
_DotLocalSubtestCreate( DotLocalSubtest
**outSubtest
)
13260 DotLocalSubtest
* obj
;
13262 obj
= (DotLocalSubtest
*) calloc( 1, sizeof( *obj
) );
13263 require_action( obj
, exit
, err
= kNoMemoryErr
);
13265 obj
->correctResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13266 require_action( obj
->correctResults
, exit
, err
= kNoMemoryErr
);
13268 obj
->duplicateResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13269 require_action( obj
->duplicateResults
, exit
, err
= kNoMemoryErr
);
13271 obj
->unexpectedResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13272 require_action( obj
->unexpectedResults
, exit
, err
= kNoMemoryErr
);
13279 if( obj
) _DotLocalSubtestFree( obj
);
13283 //===========================================================================================================================
13284 // _DotLocalSubtestFree
13285 //===========================================================================================================================
13287 static void _DotLocalSubtestFree( DotLocalSubtest
*inSubtest
)
13289 ForgetMem( &inSubtest
->queryName
);
13290 ForgetCF( &inSubtest
->correctResults
);
13291 ForgetCF( &inSubtest
->duplicateResults
);
13292 ForgetCF( &inSubtest
->unexpectedResults
);
13296 //===========================================================================================================================
13297 // _DotLocalTestStartSubtest
13298 //===========================================================================================================================
13300 static OSStatus
_DotLocalTestStartSubtest( DotLocalTestContext
*inContext
)
13303 DotLocalSubtest
* subtest
= NULL
;
13304 DNSServiceRef op
= NULL
;
13305 DNSServiceFlags flags
;
13307 err
= _DotLocalSubtestCreate( &subtest
);
13308 require_noerr( err
, exit
);
13310 if( inContext
->state
== kDotLocalTestState_GAIMDNSOnly
)
13312 ASPrintF( &subtest
->queryName
, "%s-2.local.", inContext
->labelStr
);
13313 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13315 subtest
->hasMDNSv4
= subtest
->needMDNSv4
= true;
13316 subtest
->hasMDNSv6
= subtest
->needMDNSv6
= true;
13318 subtest
->addrMDNSv4
= htonl( 0x00000201 ); // 0.0.2.1
13319 memcpy( subtest
->addrMDNSv6
, kMDNSReplierBaseAddrV6
, 16 ); // 2001:db8:2::2:1
13320 subtest
->addrMDNSv6
[ 13 ] = 2;
13321 subtest
->addrMDNSv6
[ 15 ] = 1;
13323 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAIMDNSOnly
;
13326 else if( inContext
->state
== kDotLocalTestState_GAIDNSOnly
)
13328 ASPrintF( &subtest
->queryName
, "tag-dns-only.%s.local.", inContext
->labelStr
);
13329 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13331 subtest
->hasDNSv4
= subtest
->needDNSv4
= true;
13332 subtest
->hasDNSv6
= subtest
->needDNSv6
= true;
13334 subtest
->addrDNSv4
= htonl( kDNSServerBaseAddrV4
+ 1 ); // 203.0.113.1
13335 memcpy( subtest
->addrDNSv6
, kDNSServerBaseAddrV6
, 16 ); // 2001:db8:1::1
13336 subtest
->addrDNSv6
[ 15 ] = 1;
13338 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAIDNSOnly
;
13341 else if( inContext
->state
== kDotLocalTestState_GAIBoth
)
13343 ASPrintF( &subtest
->queryName
, "%s.local.", inContext
->labelStr
);
13344 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13346 subtest
->hasDNSv4
= subtest
->needDNSv4
= true;
13347 subtest
->hasDNSv6
= subtest
->needDNSv6
= true;
13348 subtest
->hasMDNSv4
= subtest
->needMDNSv4
= true;
13349 subtest
->hasMDNSv6
= subtest
->needMDNSv6
= true;
13351 subtest
->addrDNSv4
= htonl( kDNSServerBaseAddrV4
+ 1 ); // 203.0.113.1
13352 memcpy( subtest
->addrDNSv6
, kDNSServerBaseAddrV6
, 16 ); // 2001:db8:1::1
13353 subtest
->addrDNSv6
[ 15 ] = 1;
13355 subtest
->addrMDNSv4
= htonl( 0x00000101 ); // 0.0.1.1
13356 memcpy( subtest
->addrMDNSv6
, kMDNSReplierBaseAddrV6
, 16 ); // 2001:db8:2::1:1
13357 subtest
->addrMDNSv6
[ 13 ] = 1;
13358 subtest
->addrMDNSv6
[ 15 ] = 1;
13360 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAIBoth
;
13363 else if( inContext
->state
== kDotLocalTestState_GAINeither
)
13365 ASPrintF( &subtest
->queryName
, "doesnotexit-%s.local.", inContext
->labelStr
);
13366 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13368 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAINeither
;
13371 else if( inContext
->state
== kDotLocalTestState_GAINoSuchRecord
)
13373 ASPrintF( &subtest
->queryName
, "doesnotexit-dns.%s.local.", inContext
->labelStr
);
13374 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13376 subtest
->hasDNSv4
= subtest
->needDNSv4
= true;
13377 subtest
->hasDNSv6
= subtest
->needDNSv6
= true;
13378 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAINoSuchRecord
;
13381 else if( inContext
->state
== kDotLocalTestState_QuerySRV
)
13383 ASPrintF( &subtest
->queryName
, "_http._tcp.srv-%u-%u-%u.%s%s.local.",
13384 kDotLocalTestSRV_Priority
, kDotLocalTestSRV_Weight
, kDotLocalTestSRV_Port
, kDotLocalTestSRV_TargetStr
,
13385 inContext
->labelStr
);
13386 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13388 subtest
->needSRV
= true;
13389 subtest
->testDesc
= kDotLocalTestSubtestDesc_QuerySRV
;
13398 // Start new operation.
13400 flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsReturnIntermediates
;
13401 #if( TARGET_OS_WATCH )
13402 flags
|= kDNSServiceFlagsPathEvaluationDone
;
13405 subtest
->startTime
= NanoTimeGetCurrent();
13406 subtest
->endTime
= kNanoTime_Invalid
;
13408 if( inContext
->state
== kDotLocalTestState_QuerySRV
)
13410 op
= inContext
->connection
;
13411 err
= DNSServiceQueryRecord( &op
, flags
, kDNSServiceInterfaceIndexAny
, subtest
->queryName
,
13412 kDNSServiceType_SRV
, kDNSServiceClass_IN
, _DotLocalTestQueryRecordCallback
, inContext
);
13413 require_noerr( err
, exit
);
13417 op
= inContext
->connection
;
13418 err
= DNSServiceGetAddrInfo( &op
, flags
, kDNSServiceInterfaceIndexAny
,
13419 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, subtest
->queryName
, _DotLocalTestGAICallback
, inContext
);
13420 require_noerr( err
, exit
);
13425 check( !inContext
->timer
);
13426 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestSubtestDurationSecs
),
13427 INT64_C_safe( kDotLocalTestSubtestDurationSecs
) * kNanosecondsPerSecond
/ 10, dispatch_get_main_queue(),
13428 _DotLocalTestTimerHandler
, inContext
, &inContext
->timer
);
13429 require_noerr( err
, exit
);
13430 dispatch_resume( inContext
->timer
);
13432 check( !inContext
->op
);
13433 inContext
->op
= op
;
13436 check( !inContext
->subtest
);
13437 inContext
->subtest
= subtest
;
13441 if( subtest
) _DotLocalSubtestFree( subtest
);
13442 if( op
) DNSServiceRefDeallocate( op
);
13446 //===========================================================================================================================
13447 // _DotLocalTestFinalizeSubtest
13448 //===========================================================================================================================
13450 #define kDotLocalTestReportKey_StartTime CFSTR( "startTime" ) // String.
13451 #define kDotLocalTestReportKey_EndTime CFSTR( "endTime" ) // String.
13452 #define kDotLocalTestReportKey_Success CFSTR( "success" ) // Boolean.
13453 #define kDotLocalTestReportKey_MDNSReplierCmd CFSTR( "replierCmd" ) // String.
13454 #define kDotLocalTestReportKey_DNSServerCmd CFSTR( "serverCmd" ) // String.
13455 #define kDotLocalTestReportKey_GetAddrInfoTests CFSTR( "testsGAI" ) // Array of Dictionaries.
13456 #define kDotLocalTestReportKey_QuerySRVTests CFSTR( "testsQuerySRV" ) // Array of Dictionaries.
13457 #define kDotLocalTestReportKey_Description CFSTR( "description" ) // String.
13458 #define kDotLocalTestReportKey_QueryName CFSTR( "queryName" ) // String.
13459 #define kDotLocalTestReportKey_Error CFSTR( "error" ) // Integer.
13460 #define kDotLocalTestReportKey_Results CFSTR( "results" ) // Dictionary of Arrays.
13461 #define kDotLocalTestReportKey_CorrectResults CFSTR( "correct" ) // Array of Strings
13462 #define kDotLocalTestReportKey_DuplicateResults CFSTR( "duplicates" ) // Array of Strings.
13463 #define kDotLocalTestReportKey_UnexpectedResults CFSTR( "unexpected" ) // Array of Strings.
13464 #define kDotLocalTestReportKey_MissingResults CFSTR( "missing" ) // Array of Strings.
13466 static OSStatus
_DotLocalTestFinalizeSubtest( DotLocalTestContext
*inContext
)
13469 DotLocalSubtest
* subtest
;
13470 CFMutableDictionaryRef reportDict
;
13471 CFMutableDictionaryRef resultsDict
;
13472 CFMutableArrayRef missingResults
, reportArray
;
13473 char startTime
[ 32 ];
13474 char endTime
[ 32 ];
13476 subtest
= inContext
->subtest
;
13477 inContext
->subtest
= NULL
;
13479 subtest
->endTime
= NanoTimeGetCurrent();
13480 _NanoTime64ToTimestamp( subtest
->startTime
, startTime
, sizeof( startTime
) );
13481 _NanoTime64ToTimestamp( subtest
->endTime
, endTime
, sizeof( endTime
) );
13484 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &reportDict
,
13486 "%kO=%s" // startTime
13487 "%kO=%s" // endTime
13488 "%kO=%s" // queryName
13489 "%kO=%s" // description
13490 "%kO={%@}" // results
13492 kDotLocalTestReportKey_StartTime
, startTime
,
13493 kDotLocalTestReportKey_EndTime
, endTime
,
13494 kDotLocalTestReportKey_QueryName
, subtest
->queryName
,
13495 kDotLocalTestReportKey_Description
, subtest
->testDesc
,
13496 kDotLocalTestReportKey_Results
, &resultsDict
);
13497 require_noerr( err
, exit
);
13499 missingResults
= NULL
;
13500 switch( inContext
->state
)
13502 case kDotLocalTestState_GAIMDNSOnly
:
13503 case kDotLocalTestState_GAIDNSOnly
:
13504 case kDotLocalTestState_GAIBoth
:
13505 case kDotLocalTestState_GAINeither
:
13506 if( subtest
->needDNSv4
|| subtest
->needDNSv6
|| subtest
->needMDNSv4
|| subtest
->needMDNSv6
)
13508 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &missingResults
,
13510 "%.4a" // Expected DNS IPv4 address
13511 "%.16a" // Expected DNS IPv6 address
13512 "%.4a" // Expected MDNS IPv4 address
13513 "%.16a" // Expected MDNS IPv6 address
13515 subtest
->needDNSv4
? &subtest
->addrDNSv4
: NULL
,
13516 subtest
->needDNSv6
? subtest
->addrDNSv6
: NULL
,
13517 subtest
->needMDNSv4
? &subtest
->addrMDNSv4
: NULL
,
13518 subtest
->needMDNSv6
? subtest
->addrMDNSv6
: NULL
);
13519 require_noerr( err
, exit
);
13523 case kDotLocalTestState_QuerySRV
:
13524 if( subtest
->needSRV
)
13526 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &missingResults
,
13528 "%s" // Expected SRV record data as a string.
13530 kDotLocalTestSRV_ResultStr
);
13531 require_noerr( err
, exit
);
13535 case kDotLocalTestState_GAINoSuchRecord
:
13536 if( subtest
->needDNSv4
|| subtest
->needDNSv6
)
13538 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &missingResults
,
13540 "%s" // No Such Record (A)
13541 "%s" // No Such Record (AAAA)
13543 subtest
->needDNSv4
? kNoSuchRecordAStr
: NULL
,
13544 subtest
->needDNSv6
? kNoSuchRecordAAAAStr
: NULL
);
13545 require_noerr( err
, exit
);
13554 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_CorrectResults
, subtest
->correctResults
);
13556 if( missingResults
)
13558 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_MissingResults
, missingResults
);
13559 ForgetCF( &missingResults
);
13560 if( !subtest
->error
) subtest
->error
= kNotFoundErr
;
13563 if( CFArrayGetCount( subtest
->unexpectedResults
) > 0 )
13565 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_UnexpectedResults
, subtest
->unexpectedResults
);
13566 if( !subtest
->error
) subtest
->error
= kUnexpectedErr
;
13569 if( CFArrayGetCount( subtest
->duplicateResults
) > 0 )
13571 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_DuplicateResults
, subtest
->duplicateResults
);
13572 if( !subtest
->error
) subtest
->error
= kDuplicateErr
;
13575 if( subtest
->error
) inContext
->testFailed
= true;
13576 err
= CFDictionarySetInt64( reportDict
, kDotLocalTestReportKey_Error
, subtest
->error
);
13577 require_noerr( err
, exit
);
13579 reportArray
= ( inContext
->state
== kDotLocalTestState_QuerySRV
) ? inContext
->reportsQuerySRV
: inContext
->reportsGAI
;
13580 CFArrayAppendValue( reportArray
, reportDict
);
13583 _DotLocalSubtestFree( subtest
);
13584 CFReleaseNullSafe( reportDict
);
13588 //===========================================================================================================================
13589 // _DotLocalTestFinalizeAndExit
13590 //===========================================================================================================================
13592 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext
*inContext
)
13595 CFPropertyListRef plist
;
13596 char timestampStart
[ 32 ];
13597 char timestampEnd
[ 32 ];
13599 check( !inContext
->subtest
);
13600 inContext
->endTime
= NanoTimeGetCurrent();
13602 if( inContext
->replierPID
!= -1 )
13604 kill( inContext
->replierPID
, SIGTERM
);
13605 inContext
->replierPID
= -1;
13607 if( inContext
->serverPID
!= -1 )
13609 kill( inContext
->serverPID
, SIGTERM
);
13610 inContext
->serverPID
= -1;
13612 err
= DNSServiceRemoveRecord( inContext
->connection
, inContext
->localSOARef
, 0 );
13613 require_noerr( err
, exit
);
13615 _NanoTime64ToTimestamp( inContext
->startTime
, timestampStart
, sizeof( timestampStart
) );
13616 _NanoTime64ToTimestamp( inContext
->endTime
, timestampEnd
, sizeof( timestampEnd
) );
13618 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
13620 "%kO=%s" // startTime
13621 "%kO=%s" // endTime
13622 "%kO=%O" // testsGAI
13623 "%kO=%O" // testsQuerySRV
13624 "%kO=%b" // success
13625 "%kO=%s" // replierCmd
13626 "%kO=%s" // serverCmd
13628 kDotLocalTestReportKey_StartTime
, timestampStart
,
13629 kDotLocalTestReportKey_EndTime
, timestampEnd
,
13630 kDotLocalTestReportKey_GetAddrInfoTests
, inContext
->reportsGAI
,
13631 kDotLocalTestReportKey_QuerySRVTests
, inContext
->reportsQuerySRV
,
13632 kDotLocalTestReportKey_Success
, inContext
->testFailed
? false : true,
13633 kDotLocalTestReportKey_MDNSReplierCmd
, inContext
->replierCmd
,
13634 kDotLocalTestReportKey_DNSServerCmd
, inContext
->serverCmd
);
13635 require_noerr( err
, exit
);
13637 ForgetCF( &inContext
->reportsGAI
);
13638 ForgetCF( &inContext
->reportsQuerySRV
);
13640 err
= OutputPropertyList( plist
, inContext
->outputFormat
, inContext
->outputFilePath
);
13641 CFRelease( plist
);
13642 require_noerr( err
, exit
);
13644 exit( inContext
->testFailed
? 2 : 0 );
13647 ErrQuit( 1, "error: %#m\n", err
);
13650 //===========================================================================================================================
13651 // _DotLocalTestProbeQueryRecordCallback
13652 //===========================================================================================================================
13654 static void DNSSD_API
13655 _DotLocalTestProbeQueryRecordCallback(
13656 DNSServiceRef inSDRef
,
13657 DNSServiceFlags inFlags
,
13658 uint32_t inInterfaceIndex
,
13659 DNSServiceErrorType inError
,
13660 const char * inFullName
,
13663 uint16_t inRDataLen
,
13664 const void * inRDataPtr
,
13668 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13670 Unused( inInterfaceIndex
);
13671 Unused( inFullName
);
13674 Unused( inRDataLen
);
13675 Unused( inRDataPtr
);
13678 check( context
->state
== kDotLocalTestState_Preparing
);
13680 require_quiet( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
, exit
);
13682 if( inSDRef
== context
->op
)
13684 DNSServiceForget( &context
->op
);
13685 context
->serverIsReady
= true;
13687 else if( inSDRef
== context
->op2
)
13689 DNSServiceForget( &context
->op2
);
13690 context
->replierIsReady
= true;
13693 if( context
->registeredSOA
&& context
->serverIsReady
&& context
->replierIsReady
)
13695 _DotLocalTestStateMachine( context
);
13702 //===========================================================================================================================
13703 // _DotLocalTestRegisterRecordCallback
13704 //===========================================================================================================================
13706 static void DNSSD_API
13707 _DotLocalTestRegisterRecordCallback(
13708 DNSServiceRef inSDRef
,
13709 DNSRecordRef inRecordRef
,
13710 DNSServiceFlags inFlags
,
13711 DNSServiceErrorType inError
,
13714 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13717 Unused( inRecordRef
);
13720 if( inError
) ErrQuit( 1, "error: local. SOA record registration failed: %#m\n", inError
);
13722 if( !context
->registeredSOA
)
13724 context
->registeredSOA
= true;
13725 if( context
->serverIsReady
&& context
->replierIsReady
) _DotLocalTestStateMachine( context
);
13729 //===========================================================================================================================
13730 // _DotLocalTestTimerHandler
13731 //===========================================================================================================================
13733 static void _DotLocalTestTimerHandler( void *inContext
)
13735 _DotLocalTestStateMachine( (DotLocalTestContext
*) inContext
);
13738 //===========================================================================================================================
13739 // _DotLocalTestGAICallback
13740 //===========================================================================================================================
13742 static void DNSSD_API
13743 _DotLocalTestGAICallback(
13744 DNSServiceRef inSDRef
,
13745 DNSServiceFlags inFlags
,
13746 uint32_t inInterfaceIndex
,
13747 DNSServiceErrorType inError
,
13748 const char * inHostname
,
13749 const struct sockaddr
* inSockAddr
,
13754 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13755 DotLocalSubtest
* const subtest
= context
->subtest
;
13756 const sockaddr_ip
* const sip
= (const sockaddr_ip
*) inSockAddr
;
13759 Unused( inInterfaceIndex
);
13760 Unused( inHostname
);
13763 require_action_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
, err
= kFlagErr
);
13764 require_action_quiet( ( sip
->sa
.sa_family
== AF_INET
) || ( sip
->sa
.sa_family
== AF_INET6
), exit
, err
= kTypeErr
);
13766 if( context
->state
== kDotLocalTestState_GAINoSuchRecord
)
13768 if( inError
== kDNSServiceErr_NoSuchRecord
)
13770 CFMutableArrayRef array
= NULL
;
13771 const char * noSuchRecordStr
;
13773 if( sip
->sa
.sa_family
== AF_INET
)
13775 array
= subtest
->needDNSv4
? subtest
->correctResults
: subtest
->duplicateResults
;
13776 subtest
->needDNSv4
= false;
13778 noSuchRecordStr
= kNoSuchRecordAStr
;
13782 array
= subtest
->needDNSv6
? subtest
->correctResults
: subtest
->duplicateResults
;
13783 subtest
->needDNSv6
= false;
13785 noSuchRecordStr
= kNoSuchRecordAAAAStr
;
13787 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, array
, "%s", noSuchRecordStr
);
13788 require_noerr( err
, fatal
);
13790 else if( !inError
)
13792 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, subtest
->unexpectedResults
, "%##a", sip
);
13793 require_noerr( err
, fatal
);
13805 CFMutableArrayRef array
= NULL
;
13807 if( sip
->sa
.sa_family
== AF_INET
)
13809 const uint32_t addrV4
= sip
->v4
.sin_addr
.s_addr
;
13811 if( subtest
->hasDNSv4
&& ( addrV4
== subtest
->addrDNSv4
) )
13813 array
= subtest
->needDNSv4
? subtest
->correctResults
: subtest
->duplicateResults
;
13814 subtest
->needDNSv4
= false;
13816 else if( subtest
->hasMDNSv4
&& ( addrV4
== subtest
->addrMDNSv4
) )
13818 array
= subtest
->needMDNSv4
? subtest
->correctResults
: subtest
->duplicateResults
;
13819 subtest
->needMDNSv4
= false;
13824 const uint8_t * const addrV6
= sip
->v6
.sin6_addr
.s6_addr
;
13826 if( subtest
->hasDNSv6
&& ( memcmp( addrV6
, subtest
->addrDNSv6
, 16 ) == 0 ) )
13828 array
= subtest
->needDNSv6
? subtest
->correctResults
: subtest
->duplicateResults
;
13829 subtest
->needDNSv6
= false;
13831 else if( subtest
->hasMDNSv6
&& ( memcmp( addrV6
, subtest
->addrMDNSv6
, 16 ) == 0 ) )
13833 array
= subtest
->needMDNSv6
? subtest
->correctResults
: subtest
->duplicateResults
;
13834 subtest
->needMDNSv6
= false;
13837 if( !array
) array
= subtest
->unexpectedResults
;
13838 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, array
, "%##a", sip
);
13839 require_noerr( err
, fatal
);
13841 else if( inError
== kDNSServiceErr_NoSuchRecord
)
13843 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, subtest
->unexpectedResults
, "%s",
13844 ( sip
->sa
.sa_family
== AF_INET
) ? kNoSuchRecordAStr
: kNoSuchRecordAAAAStr
);
13845 require_noerr( err
, fatal
);
13857 subtest
->error
= err
;
13858 _DotLocalTestStateMachine( context
);
13863 ErrQuit( 1, "error: %#m\n", err
);
13866 //===========================================================================================================================
13867 // _DotLocalTestQueryRecordCallback
13868 //===========================================================================================================================
13870 static void DNSSD_API
13871 _DotLocalTestQueryRecordCallback(
13872 DNSServiceRef inSDRef
,
13873 DNSServiceFlags inFlags
,
13874 uint32_t inInterfaceIndex
,
13875 DNSServiceErrorType inError
,
13876 const char * inFullName
,
13879 uint16_t inRDataLen
,
13880 const void * inRDataPtr
,
13885 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13886 DotLocalSubtest
* const subtest
= context
->subtest
;
13887 const dns_fixed_fields_srv
* fields
;
13888 const uint8_t * target
;
13889 const uint8_t * ptr
;
13890 const uint8_t * end
;
13892 unsigned int priority
, weight
, port
;
13893 CFMutableArrayRef array
;
13896 Unused( inInterfaceIndex
);
13897 Unused( inFullName
);
13900 check( context
->state
== kDotLocalTestState_QuerySRV
);
13903 require_noerr_quiet( err
, exit
);
13904 require_action_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
, err
= kFlagErr
);
13905 require_action_quiet( ( inType
== kDNSServiceType_SRV
) && ( inClass
== kDNSServiceClass_IN
), exit
, err
= kTypeErr
);
13906 require_action_quiet( inRDataLen
> sizeof( dns_fixed_fields_srv
), exit
, err
= kSizeErr
);
13908 fields
= (const dns_fixed_fields_srv
*) inRDataPtr
;
13909 priority
= dns_fixed_fields_srv_get_priority( fields
);
13910 weight
= dns_fixed_fields_srv_get_weight( fields
);
13911 port
= dns_fixed_fields_srv_get_port( fields
);
13912 target
= (const uint8_t *) &fields
[ 1 ];
13913 end
= ( (const uint8_t *) inRDataPtr
) + inRDataLen
;
13914 for( ptr
= target
; ( ptr
< end
) && ( *ptr
!= 0 ); ptr
+= ( 1 + *ptr
) ) {}
13916 if( ( priority
== kDotLocalTestSRV_Priority
) &&
13917 ( weight
== kDotLocalTestSRV_Weight
) &&
13918 ( port
== kDotLocalTestSRV_Port
) &&
13919 ( ptr
< end
) && DomainNameEqual( target
, kDotLocalTestSRV_TargetName
) )
13921 array
= subtest
->needSRV
? subtest
->correctResults
: subtest
->duplicateResults
;
13922 subtest
->needSRV
= false;
13926 array
= subtest
->unexpectedResults
;
13930 DNSRecordDataToString( inRDataPtr
, inRDataLen
, kDNSServiceType_SRV
, NULL
, 0, &rdataStr
);
13933 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, inRDataLen
);
13934 require_action( rdataStr
, fatal
, err
= kNoMemoryErr
);
13937 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, array
, "%s", rdataStr
);
13939 require_noerr( err
, fatal
);
13944 subtest
->error
= err
;
13945 _DotLocalTestStateMachine( context
);
13950 ErrQuit( 1, "error: %#m\n", err
);
13953 //===========================================================================================================================
13954 // ProbeConflictTestCmd
13955 //===========================================================================================================================
13957 #define kProbeConflictTestService_DefaultName "pctest-name"
13958 #define kProbeConflictTestService_Port 60000
13960 #define kProbeConflictTestTXTPtr "\x13" "PROBE-CONFLICT-TEST"
13961 #define kProbeConflictTestTXTLen sizeof_string( kProbeConflictTestTXTPtr )
13965 const char * description
;
13966 const char * program
;
13967 Boolean expectsRename
;
13969 } ProbeConflictTestCase
;
13971 #define kPCTProgPreWait "wait 1000;" // Wait 1 second before sending gratuitous response.
13972 #define kPCTProgPostWait "wait 8000;" // Wait 8 seconds after sending gratuitous response.
13973 // This allows ~2.75 seconds for probing and ~5 seconds for a rename.
13975 static const ProbeConflictTestCase kProbeConflictTestCases
[] =
13979 { "No probe conflicts.", kPCTProgPreWait
"probes n-n-n;" "send;" kPCTProgPostWait
, false },
13981 // One multicast probe conflict
13983 { "One multicast probe conflict (1).", kPCTProgPreWait
"probes m;" "send;" kPCTProgPostWait
, false },
13984 { "One multicast probe conflict (2).", kPCTProgPreWait
"probes n-m;" "send;" kPCTProgPostWait
, false },
13985 { "One multicast probe conflict (3).", kPCTProgPreWait
"probes n-n-m;" "send;" kPCTProgPostWait
, false },
13987 // One unicast probe conflict
13989 { "One unicast probe conflict (1).", kPCTProgPreWait
"probes u;" "send;" kPCTProgPostWait
, true },
13990 { "One unicast probe conflict (2).", kPCTProgPreWait
"probes n-u;" "send;" kPCTProgPostWait
, true },
13991 { "One unicast probe conflict (3).", kPCTProgPreWait
"probes n-n-u;" "send;" kPCTProgPostWait
, true },
13993 // One multicast and one unicast probe conflict
13995 { "Multicast and unicast probe conflict (1).", kPCTProgPreWait
"probes m-u;" "send;" kPCTProgPostWait
, true },
13996 { "Multicast and unicast probe conflict (2).", kPCTProgPreWait
"probes m-n-u;" "send;" kPCTProgPostWait
, true },
13997 { "Multicast and unicast probe conflict (3).", kPCTProgPreWait
"probes m-n-n-u;" "send;" kPCTProgPostWait
, true },
13998 { "Multicast and unicast probe conflict (4).", kPCTProgPreWait
"probes n-m-u;" "send;" kPCTProgPostWait
, true },
13999 { "Multicast and unicast probe conflict (5).", kPCTProgPreWait
"probes n-m-n-u;" "send;" kPCTProgPostWait
, true },
14000 { "Multicast and unicast probe conflict (6).", kPCTProgPreWait
"probes n-m-n-n-u;" "send;" kPCTProgPostWait
, true },
14001 { "Multicast and unicast probe conflict (7).", kPCTProgPreWait
"probes n-n-m-u;" "send;" kPCTProgPostWait
, true },
14002 { "Multicast and unicast probe conflict (8).", kPCTProgPreWait
"probes n-n-m-n-u;" "send;" kPCTProgPostWait
, true },
14003 { "Multicast and unicast probe conflict (9).", kPCTProgPreWait
"probes n-n-m-n-n-u;" "send;" kPCTProgPostWait
, true },
14005 // Two multicast probe conflicts
14007 { "Two multicast probe conflicts (1).", kPCTProgPreWait
"probes m-m;" "send;" kPCTProgPostWait
, true },
14008 { "Two multicast probe conflicts (2).", kPCTProgPreWait
"probes m-n-m;" "send;" kPCTProgPostWait
, true },
14009 { "Two multicast probe conflicts (3).", kPCTProgPreWait
"probes m-n-n-m;" "send;" kPCTProgPostWait
, true },
14010 { "Two multicast probe conflicts (4).", kPCTProgPreWait
"probes n-m-m;" "send;" kPCTProgPostWait
, true },
14011 { "Two multicast probe conflicts (5).", kPCTProgPreWait
"probes n-m-n-m-n;" "send;" kPCTProgPostWait
, true },
14012 { "Two multicast probe conflicts (6).", kPCTProgPreWait
"probes n-m-n-n-m;" "send;" kPCTProgPostWait
, true },
14013 { "Two multicast probe conflicts (7).", kPCTProgPreWait
"probes n-n-m-m;" "send;" kPCTProgPostWait
, true },
14014 { "Two multicast probe conflicts (8).", kPCTProgPreWait
"probes n-n-m-n-m;" "send;" kPCTProgPostWait
, true },
14015 { "Two multicast probe conflicts (9).", kPCTProgPreWait
"probes n-n-m-n-n-m;" "send;" kPCTProgPostWait
, true },
14018 #define kProbeConflictTestCaseCount countof( kProbeConflictTestCases )
14022 DNSServiceRef registration
; // Test service registration.
14023 NanoTime64 testStartTime
; // Test's start time.
14024 NanoTime64 startTime
; // Current test case's start time.
14025 MDNSColliderRef collider
; // mDNS collider object.
14026 CFMutableArrayRef results
; // Array of test case results.
14027 char * serviceName
; // Test service's instance name as a string. (malloced)
14028 char * serviceType
; // Test service's service type as a string. (malloced)
14029 uint8_t * recordName
; // FQDN of collider's record (same as test service's SRV+TXT records). (malloced)
14030 unsigned int testCaseIndex
; // Index of the current test case.
14031 uint32_t ifIndex
; // Index of the interface that the collider is to operate on.
14032 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout. (malloced)
14033 OutputFormatType outputFormat
; // Format of test report output.
14034 Boolean registered
; // True if the test service instance is currently registered.
14035 Boolean testFailed
; // True if at least one test case failed.
14037 } ProbeConflictTestContext
;
14039 static void DNSSD_API
14040 _ProbeConflictTestRegisterCallback(
14041 DNSServiceRef inSDRef
,
14042 DNSServiceFlags inFlags
,
14043 DNSServiceErrorType inError
,
14044 const char * inName
,
14045 const char * inType
,
14046 const char * inDomain
,
14047 void * inContext
);
14048 static void _ProbeConflictTestColliderStopHandler( void *inContext
, OSStatus inError
);
14049 static OSStatus
_ProbeConflictTestStartNextTest( ProbeConflictTestContext
*inContext
);
14050 static OSStatus
_ProbeConflictTestStopCurrentTest( ProbeConflictTestContext
*inContext
, Boolean inRenamed
);
14051 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext
*inContext
) ATTRIBUTE_NORETURN
;
14053 static void ProbeConflictTestCmd( void )
14056 ProbeConflictTestContext
* context
;
14057 const char * serviceName
;
14060 context
= (ProbeConflictTestContext
*) calloc( 1, sizeof( *context
) );
14061 require_action( context
, exit
, err
= kNoMemoryErr
);
14063 if( gProbeConflictTest_Interface
)
14065 err
= InterfaceIndexFromArgString( gProbeConflictTest_Interface
, &context
->ifIndex
);
14066 require_noerr_quiet( err
, exit
);
14070 err
= _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All
, NULL
, &context
->ifIndex
);
14071 require_noerr_quiet( err
, exit
);
14074 if( gProbeConflictTest_OutputFilePath
)
14076 context
->outputFilePath
= strdup( gProbeConflictTest_OutputFilePath
);
14077 require_action( context
->outputFilePath
, exit
, err
= kNoMemoryErr
);
14080 err
= OutputFormatFromArgString( gProbeConflictTest_OutputFormat
, &context
->outputFormat
);
14081 require_noerr_quiet( err
, exit
);
14083 context
->results
= CFArrayCreateMutable( NULL
, kProbeConflictTestCaseCount
, &kCFTypeArrayCallBacks
);
14084 require_action( context
->results
, exit
, err
= kNoMemoryErr
);
14086 serviceName
= gProbeConflictTest_UseComputerName
? NULL
: kProbeConflictTestService_DefaultName
;
14088 ASPrintF( &context
->serviceType
, "_pctest-%s._udp",
14089 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
14090 require_action( context
->serviceType
, exit
, err
= kNoMemoryErr
);
14092 context
->testStartTime
= NanoTimeGetCurrent();
14093 err
= DNSServiceRegister( &context
->registration
, 0, context
->ifIndex
, serviceName
, context
->serviceType
, "local.",
14094 NULL
, htons( kProbeConflictTestService_Port
), 0, NULL
, _ProbeConflictTestRegisterCallback
, context
);
14095 require_noerr( err
, exit
);
14097 err
= DNSServiceSetDispatchQueue( context
->registration
, dispatch_get_main_queue() );
14098 require_noerr( err
, exit
);
14106 //===========================================================================================================================
14107 // _ProbeConflictTestRegisterCallback
14108 //===========================================================================================================================
14110 static void DNSSD_API
14111 _ProbeConflictTestRegisterCallback(
14112 DNSServiceRef inSDRef
,
14113 DNSServiceFlags inFlags
,
14114 DNSServiceErrorType inError
,
14115 const char * inName
,
14116 const char * inType
,
14117 const char * inDomain
,
14121 ProbeConflictTestContext
* const context
= (ProbeConflictTestContext
*) inContext
;
14125 Unused( inDomain
);
14128 require_noerr( err
, exit
);
14130 if( !context
->registered
)
14132 if( inFlags
& kDNSServiceFlagsAdd
)
14135 size_t recordNameLen
;
14137 uint8_t name
[ kDomainNameLengthMax
];
14139 context
->registered
= true;
14141 FreeNullSafe( context
->serviceName
);
14142 context
->serviceName
= strdup( inName
);
14143 require_action( context
->serviceName
, exit
, err
= kNoMemoryErr
);
14145 err
= DomainNameFromString( name
, context
->serviceName
, NULL
);
14146 require_noerr( err
, exit
);
14148 err
= DomainNameAppendString( name
, context
->serviceType
, NULL
);
14149 require_noerr( err
, exit
);
14151 err
= DomainNameAppendString( name
, "local", NULL
);
14152 require_noerr( err
, exit
);
14154 ForgetMem( &context
->recordName
);
14155 err
= DomainNameDup( name
, &context
->recordName
, &recordNameLen
);
14156 require_noerr( err
, exit
);
14157 require_fatal( recordNameLen
> 0, "Record name length is zero." ); // Prevents dubious static analyzer warning.
14159 // Make the first label all caps so that it's easier to spot in system logs.
14161 ptr
= context
->recordName
;
14162 for( len
= *ptr
++; len
> 0; --len
, ++ptr
) *ptr
= (uint8_t) toupper_safe( *ptr
);
14164 err
= _ProbeConflictTestStartNextTest( context
);
14165 require_noerr( err
, exit
);
14170 if( !( inFlags
& kDNSServiceFlagsAdd
) )
14172 context
->registered
= false;
14173 err
= _ProbeConflictTestStopCurrentTest( context
, true );
14174 require_noerr( err
, exit
);
14180 if( err
) exit( 1 );
14183 //===========================================================================================================================
14184 // _ProbeConflictTestColliderStopHandler
14185 //===========================================================================================================================
14187 static void _ProbeConflictTestColliderStopHandler( void *inContext
, OSStatus inError
)
14190 ProbeConflictTestContext
* const context
= (ProbeConflictTestContext
*) inContext
;
14193 require_noerr_quiet( err
, exit
);
14195 ForgetCF( &context
->collider
);
14197 err
= _ProbeConflictTestStopCurrentTest( context
, false );
14198 require_noerr( err
, exit
);
14200 err
= _ProbeConflictTestStartNextTest( context
);
14201 require_noerr( err
, exit
);
14204 if( err
) exit( 1 );
14207 //===========================================================================================================================
14208 // _ProbeConflictTestStartNextTest
14209 //===========================================================================================================================
14211 static OSStatus
_ProbeConflictTestStartNextTest( ProbeConflictTestContext
*inContext
)
14214 const ProbeConflictTestCase
* testCase
;
14216 check( !inContext
->collider
);
14218 if( inContext
->testCaseIndex
< kProbeConflictTestCaseCount
)
14220 testCase
= &kProbeConflictTestCases
[ inContext
->testCaseIndex
];
14224 _ProbeConflictTestFinalizeAndExit( inContext
);
14227 err
= MDNSColliderCreate( dispatch_get_main_queue(), &inContext
->collider
);
14228 require_noerr( err
, exit
);
14230 err
= MDNSColliderSetProgram( inContext
->collider
, testCase
->program
);
14231 require_noerr( err
, exit
);
14233 err
= MDNSColliderSetRecord( inContext
->collider
, inContext
->recordName
, kDNSServiceType_TXT
,
14234 kProbeConflictTestTXTPtr
, kProbeConflictTestTXTLen
);
14235 require_noerr( err
, exit
);
14237 MDNSColliderSetProtocols( inContext
->collider
, kMDNSColliderProtocol_IPv4
);
14238 MDNSColliderSetInterfaceIndex( inContext
->collider
, inContext
->ifIndex
);
14239 MDNSColliderSetStopHandler( inContext
->collider
, _ProbeConflictTestColliderStopHandler
, inContext
);
14241 inContext
->startTime
= NanoTimeGetCurrent();
14242 err
= MDNSColliderStart( inContext
->collider
);
14243 require_noerr( err
, exit
);
14249 //===========================================================================================================================
14250 // _ProbeConflictTestStopCurrentTest
14251 //===========================================================================================================================
14253 #define kProbeConflictTestCaseResultKey_Description CFSTR( "description" )
14254 #define kProbeConflictTestCaseResultKey_StartTime CFSTR( "startTime" )
14255 #define kProbeConflictTestCaseResultKey_EndTime CFSTR( "endTime" )
14256 #define kProbeConflictTestCaseResultKey_ExpectedRename CFSTR( "expectedRename" )
14257 #define kProbeConflictTestCaseResultKey_ServiceName CFSTR( "serviceName" )
14258 #define kProbeConflictTestCaseResultKey_Passed CFSTR( "passed" )
14260 static OSStatus
_ProbeConflictTestStopCurrentTest( ProbeConflictTestContext
*inContext
, Boolean inRenamed
)
14263 const ProbeConflictTestCase
* testCase
;
14266 char startTime
[ 32 ];
14267 char endTime
[ 32 ];
14269 now
= NanoTimeGetCurrent();
14271 if( inContext
->collider
)
14273 MDNSColliderSetStopHandler( inContext
->collider
, NULL
, NULL
);
14274 MDNSColliderStop( inContext
->collider
);
14275 CFRelease( inContext
->collider
);
14276 inContext
->collider
= NULL
;
14279 testCase
= &kProbeConflictTestCases
[ inContext
->testCaseIndex
];
14280 passed
= ( ( testCase
->expectsRename
&& inRenamed
) || ( !testCase
->expectsRename
&& !inRenamed
) ) ? true : false;
14281 if( !passed
) inContext
->testFailed
= true;
14283 _NanoTime64ToTimestamp( inContext
->startTime
, startTime
, sizeof( startTime
) );
14284 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
14286 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, inContext
->results
,
14288 "%kO=%s" // description
14289 "%kO=%b" // expectedRename
14290 "%kO=%s" // startTime
14291 "%kO=%s" // endTime
14292 "%kO=%s" // serviceName
14295 kProbeConflictTestCaseResultKey_Description
, testCase
->description
,
14296 kProbeConflictTestCaseResultKey_ExpectedRename
, testCase
->expectsRename
,
14297 kProbeConflictTestCaseResultKey_StartTime
, startTime
,
14298 kProbeConflictTestCaseResultKey_EndTime
, endTime
,
14299 kProbeConflictTestCaseResultKey_ServiceName
, inContext
->serviceName
,
14300 kProbeConflictTestCaseResultKey_Passed
, passed
);
14301 require_noerr( err
, exit
);
14303 ++inContext
->testCaseIndex
;
14309 //===========================================================================================================================
14310 // _ProbeConflictTestFinalizeAndExit
14311 //===========================================================================================================================
14313 #define kProbeConflictTestReportKey_StartTime CFSTR( "startTime" )
14314 #define kProbeConflictTestReportKey_EndTime CFSTR( "endTime" )
14315 #define kProbeConflictTestReportKey_ServiceType CFSTR( "serviceType" )
14316 #define kProbeConflictTestReportKey_Results CFSTR( "results" )
14317 #define kProbeConflictTestReportKey_Passed CFSTR( "passed" )
14319 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext
*inContext
)
14322 CFPropertyListRef plist
;
14324 char startTime
[ 32 ];
14325 char endTime
[ 32 ];
14327 now
= NanoTimeGetCurrent();
14329 check( !inContext
->collider
);
14331 _NanoTime64ToTimestamp( inContext
->testStartTime
, startTime
, sizeof( startTime
) );
14332 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
14334 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
14336 "%kO=%s" // startTime
14337 "%kO=%s" // endTime
14338 "%kO=%s" // serviceType
14339 "%kO=%O" // results
14342 kProbeConflictTestReportKey_StartTime
, startTime
,
14343 kProbeConflictTestReportKey_EndTime
, endTime
,
14344 kProbeConflictTestReportKey_ServiceType
, inContext
->serviceType
,
14345 kProbeConflictTestReportKey_Results
, inContext
->results
,
14346 kProbeConflictTestReportKey_Passed
, inContext
->testFailed
? false : true );
14347 require_noerr( err
, exit
);
14348 ForgetCF( &inContext
->results
);
14350 err
= OutputPropertyList( plist
, inContext
->outputFormat
, inContext
->outputFilePath
);
14351 CFRelease( plist
);
14352 require_noerr( err
, exit
);
14354 exit( inContext
->testFailed
? 2 : 0 );
14357 ErrQuit( 1, "error: %#m\n", err
);
14360 //===========================================================================================================================
14361 // ExpensiveConstrainedsTestCmd
14362 //===========================================================================================================================
14364 #define NOTIFICATION_TIME_THRESHOLD 1500 // The maximum wating time allowed before notification happens
14365 #define TEST_REPETITION 2 // the number of repetition that one test has to passed
14366 #define LOOPBACK_INTERFACE_NAME "lo0"
14367 #define WIFI_TEST_QUESTION_NAME "www.example.com"
14368 #define EXPENSIVE_CONSTRAINED_MAX_RETRIES 1
14369 #define EXPENSIVE_CONSTRAINED_TEST_INTERVAL 5
14370 // Use "-n tag-expensive-test.ttl-86400.d.test." to run the test locally
14371 // #define LOOPBACK_TEST_QUESTION_NAME "tag-expensive-test.ttl-86400.d.test."
14373 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
14374 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
14375 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED CFSTR( "All Tests Passed" )
14376 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT CFSTR( "Subtest Results" )
14378 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
14379 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
14380 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME CFSTR( "Question Name" )
14381 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS CFSTR( "DNS Service Flags" )
14382 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS CFSTR( "Protocols" )
14383 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX CFSTR( "Interface Index" )
14384 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME CFSTR( "Interface Name" )
14385 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT CFSTR( "Result" )
14386 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR CFSTR( "Error Description" )
14387 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS CFSTR( "Test Progress" )
14389 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME CFSTR( "Start Time" )
14390 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME CFSTR( "End Time" )
14391 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE CFSTR( "State" )
14392 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT CFSTR( "Expected Result" )
14393 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT CFSTR( "Actual Result" )
14394 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW CFSTR( "Expensive Prev->Now" )
14395 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW CFSTR( "Constrained Prev->Now" )
14396 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK CFSTR( "Call Back" )
14398 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP CFSTR( "Timestamp" )
14399 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME CFSTR( "Answer Name" )
14400 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS CFSTR( "Add or Remove" )
14401 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE CFSTR( "Interface Index" )
14402 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS CFSTR( "Address" )
14404 // All the states that ends with _PREPARE represents the state where the test state is reset and initialized.
14405 enum ExpensiveConstrainedTestState
14408 TEST_EXPENSIVE_PREPARE
,
14409 TEST_EXPENSIVE
, // Test if mDNSResponder can handle "expensive" status change of the corresponding interface
14410 TEST_CONSTRAINED_PREPARE
,
14411 TEST_CONSTRAINED
, // Test if mDNSResponder can handle "constrained" status change of the corresponding interface
14412 TEST_EXPENSIVE_CONSTRAINED_PREPARE
,
14413 TEST_EXPENSIVE_CONSTRAINED
, // Test if mDNSResponder can handle "expensive" and "constrained" status change of the corresponding interface at the same time
14417 enum ExpensiveConstrainedTestOperation
14419 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.
14420 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.
14421 NO_UPDATE
// no status update notification
14426 uint32_t subtestIndex
; // The index of parameter for the subtest
14427 DNSServiceRef opRef
; // sdRef for the DNSServiceGetAddrInfo operation.
14428 const char * name
; // Hostname to resolve.
14429 DNSServiceFlags flags
; // Flags argument for DNSServiceGetAddrInfo().
14430 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceGetAddrInfo().
14431 uint32_t ifIndex
; // Interface index argument for DNSServiceGetAddrInfo().
14432 char ifName
[IFNAMSIZ
]; // Interface name for the given interface index.
14433 dispatch_source_t timer
; // The test will check if the current behavior is valid, which is called by
14434 // the timer per 2s.
14436 Boolean isExpensivePrev
; // If the interface is expensive in the previous test step.
14437 Boolean isExpensiveNow
; // If the interface is expensive now.
14438 Boolean isConstrainedPrev
; // If the interface is constrained in the previous test step.
14439 Boolean isConstrainedNow
; // If the interface is constrained now.
14440 Boolean startFromExpensive
; // All the test will start from expensive/constrained interface, so there won's be an answer until the interface is changed.
14441 uint8_t numOfRetries
; // the number of retries we can have if the test fail
14442 struct timeval updateTime
; // The time when interface status(expensive or constrained) is changed.
14443 struct timeval notificationTime
; // The time when callback function, which is passed to DNSServiceGetAddrInfo, gets called.
14444 uint32_t counter
; // To record how many times the test has repeated.
14445 enum ExpensiveConstrainedTestState state
; // The current test state.
14446 enum ExpensiveConstrainedTestOperation expectedOperation
; // the test expects this kind of notification
14447 enum ExpensiveConstrainedTestOperation operation
; // represents what notification the callback function gets.
14449 NanoTime64 testReport_startTime
; // when the entire test starts
14450 CFMutableArrayRef subtestReport
; // stores the log message for every subtest
14451 NanoTime64 subtestReport_startTime
; // when the subtest starts
14452 CFMutableArrayRef subtestProgress
; // one test iteration
14453 NanoTime64 subtestProgress_startTime
; // when the test iteration starts
14454 CFMutableArrayRef subtestProgress_callBack
; // array of ADD/REMOVE events
14455 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout. (malloced)
14456 OutputFormatType outputFormat
; // Format of test report output.
14457 } ExpensiveConstrainedContext
;
14459 // structure that controls how the subtest is run
14462 const char *qname
; // the name of the query, when the ends with ".d.test.", test will send query to local DNS server
14463 Boolean deny_expensive
; // if the query should avoid using expensive interface
14464 Boolean deny_constrained
; // if the query should avoid using constrained interface
14465 Boolean start_from_expensive
; // if the query should starts from using an expensive interface
14466 Boolean ipv4_query
; // only allow IPv4 query
14467 Boolean ipv6_query
; // only allow IPv6 query
14468 int8_t test_passed
; // if the subtest passes
14469 } ExpensiveConstrainedTestParams
;
14471 static ExpensiveConstrainedTestParams ExpensiveConstrainedSubtestParams
[] =
14473 // qname deny_expensive deny_constrained start_from_expensive ipv4_query ipv6_query
14474 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, true, -1},
14475 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, true, -1},
14476 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, true, -1},
14477 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, true, -1},
14478 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, true, -1},
14479 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, true, -1},
14481 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, false, -1},
14482 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, false, -1},
14483 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, false, -1},
14484 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, false, -1},
14485 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, false, -1},
14486 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, false, -1},
14488 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, false, true, -1},
14489 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, false, true, -1},
14490 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, false, true, -1},
14491 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, false, true, -1},
14492 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, false, true, -1},
14493 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, false, true, -1}
14496 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext
*context
);
14497 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext
*context
);
14498 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext
*context
);
14499 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext
*context
, uint32_t second
);
14500 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext
*context
);
14501 static void DNSSD_API
14502 ExpensiveConstrainedCallback(
14503 DNSServiceRef inSDRef
,
14504 DNSServiceFlags inFlags
,
14505 uint32_t inInterfaceIndex
,
14506 DNSServiceErrorType inError
,
14507 const char * inHostname
,
14508 const struct sockaddr
* inSockAddr
,
14510 void * inContext
);
14511 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext
*context
);
14512 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext
*context
);
14513 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext
*context
);
14514 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext
*context
, const char *error_description
);
14515 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext
*context
, Boolean allPassed
);
14516 static const char *ExpensiveConstrainedProtocolString(DNSServiceProtocol protocol
);
14517 static const char *ExpensiveConstrainedStateString(enum ExpensiveConstrainedTestState state
);
14518 static const char *ExpensiveConstrainedOperationString(enum ExpensiveConstrainedTestOperation operation
);
14519 static Boolean
expensiveConstrainedEndsWith( const char *str
, const char *suffix
);
14521 //===========================================================================================================================
14522 // ExpensiveConstrainedTestCmd
14523 //===========================================================================================================================
14525 static void ExpensiveConstrainedTestCmd( void )
14528 dispatch_source_t signalSource
= NULL
;
14529 ExpensiveConstrainedContext
* context
= NULL
;
14531 // Set up SIGINT handler.
14532 signal( SIGINT
, SIG_IGN
);
14533 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
14534 require_noerr( err
, exit
);
14535 dispatch_resume( signalSource
);
14537 // create the test context
14538 context
= (ExpensiveConstrainedContext
*) calloc( 1, sizeof(*context
) );
14539 require_action( context
, exit
, err
= kNoMemoryErr
);
14541 // get the command line option
14542 err
= OutputFormatFromArgString( gExpensiveConstrainedTest_OutputFormat
, &context
->outputFormat
);
14543 require_noerr_quiet( err
, exit
);
14544 if ( gExpensiveConstrainedTest_OutputFilePath
)
14546 context
->outputFilePath
= strdup( gExpensiveConstrainedTest_OutputFilePath
);
14547 require_noerr_quiet( context
->outputFilePath
, exit
);
14550 // initialize context
14551 context
->subtestIndex
= 0;
14552 context
->numOfRetries
= EXPENSIVE_CONSTRAINED_MAX_RETRIES
;
14554 // initialize the CFArray used to store the log
14555 context
->subtestReport
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14556 context
->testReport_startTime
= NanoTimeGetCurrent();
14558 // setup local DNS server
14559 ExpensiveConstrainedSetupLocalDNSServer( context
);
14561 ExpensiveConstrainedStartTestHandler( context
);
14569 //===========================================================================================================================
14570 // ExpensiveConstrainedSetupLocalDNSServer
14571 //===========================================================================================================================
14573 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext
*context
)
14575 pid_t current_pid
= getpid();
14576 OSStatus err
= SpawnCommand( &context
->serverPID
, "dnssdutil server -l --follow %d", current_pid
);
14579 FPrintF( stdout
, "dnssdutil server -l --follow <PID> failed, error: %d\n", err
);
14585 //===========================================================================================================================
14586 // ExpensiveConstrainedStartTestHandler
14587 //===========================================================================================================================
14589 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext
*context
)
14592 ExpensiveConstrainedSetupTimer( context
, EXPENSIVE_CONSTRAINED_TEST_INTERVAL
);
14594 // set the event handler for the 3s timer
14595 dispatch_source_set_event_handler( context
->timer
, ^{
14596 ExpensiveConstrainedTestTimerEventHandler( context
);
14599 dispatch_resume( context
->timer
);
14602 //===========================================================================================================================
14603 // ExpensiveConstrainedStartTestHandler
14604 //===========================================================================================================================
14606 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext
*context
)
14608 dispatch_cancel( context
->timer
);
14609 dispatch_release( context
->timer
);
14610 context
->timer
= NULL
;
14613 //===========================================================================================================================
14614 // ExpensiveConstrainedSetupTimer
14615 //===========================================================================================================================
14617 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext
*context
, uint32_t second
)
14619 // set the timer source, the event handler will be called for every "second" seconds
14620 context
->timer
= dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, dispatch_get_main_queue() );
14621 if ( context
->timer
== NULL
)
14623 FPrintF( stdout
, "dispatch_source_create:DISPATCH_SOURCE_TYPE_TIMER failed\n" );
14626 // the first block will be put into the queue "second"s after calling dispatch_resume
14627 dispatch_source_set_timer( context
->timer
, dispatch_time( DISPATCH_TIME_NOW
, second
* NSEC_PER_SEC
),
14628 (unsigned long long)(second
) * NSEC_PER_SEC
, 100ull * NSEC_PER_MSEC
);
14631 //===========================================================================================================================
14632 // ExpensiveConstrainedTestTimerEventHandler
14633 //===========================================================================================================================
14635 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext
*context
)
14638 char buffer
[ 1024 ];
14639 const char *errorDescription
= NULL
;
14641 // do not log the state if we are in transition state
14642 if (context
->state
!= TEST_BEGIN
14643 && context
->state
!= TEST_SUCCEEDED
14644 && context
->state
!= TEST_CONSTRAINED_PREPARE
14645 && context
->state
!= TEST_EXPENSIVE_CONSTRAINED_PREPARE
)
14646 ExpensiveConstrainedSubtestProgressReport( context
);
14648 switch ( context
->state
) {
14651 ExpensiveConstrainedStopTestHandler( context
);
14653 // clear mDNSResponder cache
14654 err
= systemf( NULL
, "killall -HUP mDNSResponder" );
14655 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed");
14657 // initialize the global parameters
14658 ExpensiveConstrainedInitializeContext( context
);
14660 // The local DNS server is set up on the local only interface.
14661 gExpensiveConstrainedTest_Interface
= LOOPBACK_INTERFACE_NAME
;
14662 strncpy( context
->ifName
, gExpensiveConstrainedTest_Interface
, sizeof( context
->ifName
) );
14664 // The local DNS server is unscoped, so we must set our question to unscoped.
14665 context
->ifIndex
= kDNSServiceInterfaceIndexAny
;
14667 // The question name must end with "d.test.", "tag-expensive-test.ttl-86400.d.test." for example, then the test will
14668 // use the local dns server set up previously to run the test locally.
14669 require_action( gExpensiveConstrainedTest_Name
!= NULL
&& expensiveConstrainedEndsWith( gExpensiveConstrainedTest_Name
, "d.test." ), test_failed
,
14670 SNPrintF( buffer
, sizeof( buffer
), "The question name (%s) must end with \"d.test.\".\n", gExpensiveConstrainedTest_Name
);
14671 errorDescription
= buffer
);
14673 // get the quesion name
14674 context
->name
= gExpensiveConstrainedTest_Name
;
14676 // set the initial state for the interface
14677 context
->startFromExpensive
= gExpensiveConstrainedTest_StartFromExpensive
;
14678 err
= systemf( NULL
, "ifconfig %s %sexpensive && ifconfig %s -constrained", context
->ifName
, context
->startFromExpensive
? "" : "-", context
->ifName
);
14679 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed");
14680 sleep( 5 ); // wait for 5s to allow the interface change event de delivered to others
14682 // get question flag
14683 if ( gExpensiveConstrainedTest_DenyExpensive
) context
->flags
|= kDNSServiceFlagsDenyExpensive
;
14684 if ( gExpensiveConstrainedTest_DenyConstrained
) context
->flags
|= kDNSServiceFlagsDenyConstrained
;
14685 if ( gExpensiveConstrainedTest_ProtocolIPv4
) context
->protocols
|= kDNSServiceProtocol_IPv4
;
14686 if ( gExpensiveConstrainedTest_ProtocolIPv6
) context
->protocols
|= kDNSServiceProtocol_IPv6
;
14688 // prevent mDNSResponder from doing extra path evaluation and changing the interface to others(such as Bluetooth)
14689 #if( TARGET_OS_WATCH )
14690 context
->flags
|= kDNSServiceFlagsPathEvaluationDone
;
14694 DNSServiceGetAddrInfo( &context
->opRef
, context
->flags
, context
->ifIndex
, context
->protocols
, context
->name
, ExpensiveConstrainedCallback
, context
);
14696 // set the initial test status
14697 context
->subtestReport_startTime
= NanoTimeGetCurrent();
14698 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14699 context
->state
= TEST_EXPENSIVE_PREPARE
; // start from expensive test
14700 context
->isExpensiveNow
= context
->startFromExpensive
? true : false;
14701 context
->isConstrainedNow
= false;
14702 context
->expectedOperation
= context
->isExpensiveNow
&& ( context
->flags
& kDNSServiceFlagsDenyExpensive
) ? NO_UPDATE
: RESULT_ADD
;
14703 context
->operation
= NO_UPDATE
;
14704 context
->subtestProgress
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14705 require_action( context
->subtestProgress
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14706 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14707 require_action( context
->subtestProgress
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14709 // set the queue where the callback will be called when there is an answer for the query
14710 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
14711 require_noerr( err
, test_failed
);
14713 ExpensiveConstrainedStartTestHandler( context
);
14716 case TEST_EXPENSIVE_PREPARE
:
14717 require_action( context
->isConstrainedNow
== false, test_failed
,
14718 SNPrintF( buffer
, sizeof( buffer
), "Interface %s should be unconstrained.\n", context
->ifName
);
14719 errorDescription
= buffer
);
14720 require_action( context
->expectedOperation
== context
->operation
, test_failed
,
14721 errorDescription
= "Operation is not expected" );
14723 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14724 context
->state
= TEST_EXPENSIVE
; // begin to test expensive flag
14725 context
->counter
= 0; // the number of test repetition that has passed
14726 context
->isExpensivePrev
= context
->isExpensiveNow
;
14727 context
->isExpensiveNow
= !context
->isExpensiveNow
; // flip the expensive status
14728 context
->isConstrainedPrev
= false; // the interface is currently unconstrained
14729 context
->isConstrainedNow
= false; // the interface will be unconstrained in the current test
14730 if ( gExpensiveConstrainedTest_DenyExpensive
)
14731 context
->expectedOperation
= context
->isExpensiveNow
? RESULT_RMV
: RESULT_ADD
;
14733 context
->expectedOperation
= NO_UPDATE
;
14734 context
->operation
= NO_UPDATE
; // NO_UPDATE means the call back function has not been called
14735 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14736 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14738 err
= systemf( NULL
, "ifconfig %s %sexpensive", context
->ifName
, context
->isExpensiveNow
? "" : "-" );
14739 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14741 // record the starting timestamp
14742 gettimeofday( &context
->updateTime
, NULL
);
14745 case TEST_EXPENSIVE
:
14746 // Since we are testing expensive flag, we should always turn the expensive flag on and off.
14747 require_action( context
->isExpensivePrev
^ context
->isExpensiveNow
, test_failed
,
14748 SNPrintF( buffer
, sizeof( buffer
), "The current expensive status should be different with the previous one: %d -> %d\n", context
->isExpensivePrev
, context
->isExpensiveNow
);
14749 errorDescription
= buffer
);
14750 // constrained flag is always turned off when testing expensive
14751 require_action( context
->isConstrainedNow
== false, test_failed
,
14752 SNPrintF( buffer
, sizeof( buffer
), "The interface %s should be unconstrained when testing \"expensive\"\n", context
->ifName
);
14753 errorDescription
= buffer
);
14754 require_action( context
->expectedOperation
== context
->operation
, test_failed
, errorDescription
= "Operation is not expected" );
14756 context
->counter
++; // one test repetition has passed
14757 if ( context
->counter
== TEST_REPETITION
) // expensive test finished
14759 // prepare to test constrained flag
14760 context
->state
= TEST_CONSTRAINED_PREPARE
;
14762 // reset the interface
14763 err
= systemf( NULL
, "ifconfig %s -expensive && ifconfig %s -constrained", context
->ifName
, context
->ifName
);
14764 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14766 context
->isExpensiveNow
= false;
14767 context
->isConstrainedNow
= false;
14768 gettimeofday( &context
->updateTime
, NULL
);
14772 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14773 context
->isExpensivePrev
= context
->isExpensiveNow
;
14774 context
->isExpensiveNow
= !context
->isExpensiveNow
; // flip the expensive status
14775 if ( gExpensiveConstrainedTest_DenyExpensive
)
14776 context
->expectedOperation
= context
->isExpensiveNow
? RESULT_RMV
: RESULT_ADD
;
14778 context
->expectedOperation
= NO_UPDATE
;
14779 context
->operation
= NO_UPDATE
;
14780 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14781 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14783 err
= systemf( NULL
, "ifconfig %s %sexpensive", context
->ifName
, context
->isExpensiveNow
? "" : "-" );
14784 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14786 gettimeofday( &context
->updateTime
, NULL
);
14789 case TEST_CONSTRAINED_PREPARE
:
14790 // The interface should be inexpensive and unconstrained when the constrained test starts
14791 require_action( context
->isExpensiveNow
== false, test_failed
, SNPrintF( buffer
, sizeof( buffer
), "Interface %s should be inexpensive.", context
->ifName
);
14792 errorDescription
= buffer
);
14793 require_action( context
->isConstrainedNow
== false, test_failed
, SNPrintF( buffer
, sizeof( buffer
), "Interface %s should be unconstrained.\n", context
->ifName
);
14794 errorDescription
= buffer
);
14796 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14797 context
->state
= TEST_CONSTRAINED
; // constrained interface is now under testing
14798 context
->counter
= 0;
14799 context
->isExpensivePrev
= false;
14800 context
->isExpensiveNow
= false;
14801 context
->isConstrainedPrev
= false;
14802 context
->isConstrainedNow
= true; // will set constrained flag on the interface
14803 if ( gExpensiveConstrainedTest_DenyConstrained
)
14804 context
->expectedOperation
= RESULT_RMV
;
14806 context
->expectedOperation
= NO_UPDATE
;
14807 context
->operation
= NO_UPDATE
;
14808 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14809 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14811 // change interface to the constrained one
14812 err
= systemf( NULL
, "ifconfig %s -expensive && ifconfig %s constrained", context
->ifName
, context
->ifName
);
14813 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14815 gettimeofday( &context
->updateTime
, NULL
);
14817 case TEST_CONSTRAINED
:
14818 // Since we are testing constrained flag, we should always turn the constrained flag on and off.
14819 require_action( context
->isConstrainedPrev
^ context
->isConstrainedNow
, test_failed
,
14820 SNPrintF( buffer
, sizeof( buffer
), "The current constrained status should be different with the previous one: %d -> %d\n", context
->isConstrainedPrev
, context
->isConstrainedNow
);
14821 errorDescription
= buffer
);
14822 require_action( context
->isExpensiveNow
== false, test_failed
,
14823 SNPrintF( buffer
, sizeof( buffer
), "The interface %s should be inexpensive when testing \"constrained\"\n", context
->ifName
);
14824 errorDescription
= buffer
);
14825 require_action( context
->expectedOperation
== context
->operation
, test_failed
, errorDescription
= "Operation is not expected");
14827 context
->counter
++;
14828 if (context
->counter
== TEST_REPETITION
)
14830 // test changing expensive and constrained flags at the same time
14831 context
->state
= TEST_EXPENSIVE_CONSTRAINED_PREPARE
;
14834 err
= systemf( NULL
, "ifconfig %s -expensive && ifconfig %s -constrained", context
->ifName
, context
->ifName
);
14835 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14837 context
->isExpensiveNow
= false;
14838 context
->isConstrainedNow
= false;
14839 gettimeofday( &context
->updateTime
, NULL
);
14843 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14844 context
->isConstrainedPrev
= context
->isConstrainedNow
;
14845 context
->isConstrainedNow
= !context
->isConstrainedNow
; // flip constrained flag
14846 if ( gExpensiveConstrainedTest_DenyConstrained
)
14847 context
->expectedOperation
= context
->isConstrainedNow
? RESULT_RMV
: RESULT_ADD
;
14849 context
->expectedOperation
= NO_UPDATE
;
14850 context
->operation
= NO_UPDATE
;
14851 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14852 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14854 err
= systemf( NULL
, "ifconfig %s %sconstrained", context
->ifName
, context
->isConstrainedNow
? "" : "-" );
14855 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14857 gettimeofday(&context
->updateTime
, NULL
);
14860 case TEST_EXPENSIVE_CONSTRAINED_PREPARE
:
14861 // The interface should be inexpensive and unconstrained when the constrained test starts
14862 require_action( context
->isExpensiveNow
== false, test_failed
,
14863 SNPrintF( buffer
, sizeof( buffer
), "Interface %s should be inexpensive.\n", context
->ifName
);
14864 errorDescription
= buffer
);
14865 require_action( context
->isConstrainedNow
== false, test_failed
,
14866 SNPrintF(buffer
, sizeof( buffer
), "Interface %s should be unconstrained.\n", context
->ifName
);
14867 errorDescription
= buffer
);
14869 // now flip expensive and constrained at the same time
14870 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14871 context
->state
= TEST_EXPENSIVE_CONSTRAINED
;
14872 context
->counter
= 0;
14873 context
->isExpensivePrev
= false;
14874 context
->isExpensiveNow
= true;
14875 context
->isConstrainedPrev
= false;
14876 context
->isConstrainedNow
= true;
14877 if (gExpensiveConstrainedTest_DenyConstrained
|| gExpensiveConstrainedTest_DenyExpensive
)
14878 context
->expectedOperation
= RESULT_RMV
;
14880 context
->expectedOperation
= NO_UPDATE
;
14881 context
->operation
= NO_UPDATE
;
14882 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14883 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14885 err
= systemf(NULL
, "ifconfig %s expensive && ifconfig %s constrained", context
->ifName
, context
->ifName
);
14886 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14888 gettimeofday( &context
->updateTime
, NULL
);
14890 case TEST_EXPENSIVE_CONSTRAINED
:
14891 // expensive and constrained flag should always be changed
14892 require_action( ( context
->isExpensivePrev
^ context
->isExpensiveNow
) && ( context
->isConstrainedPrev
^ context
->isConstrainedNow
), test_failed
,
14893 SNPrintF( buffer
, sizeof( buffer
), "Both expensive and constrained status need to be changed" );
14894 errorDescription
= buffer
);
14895 require_action( context
->isExpensiveNow
== context
->isConstrainedNow
, test_failed
, errorDescription
= "context->isExpensiveNow != context->isConstrainedNow" );
14896 require_action( context
->expectedOperation
== context
->operation
, test_failed
, errorDescription
= "Operation is not expected" );
14898 context
->counter
++;
14899 if ( context
->counter
== TEST_REPETITION
)
14901 context
->state
= TEST_SUCCEEDED
;
14905 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14906 context
->isExpensivePrev
= context
->isExpensiveNow
;
14907 context
->isExpensiveNow
= !context
->isExpensiveNow
;
14908 context
->isConstrainedPrev
= context
->isConstrainedNow
;
14909 context
->isConstrainedNow
= !context
->isConstrainedNow
;
14910 if (gExpensiveConstrainedTest_DenyConstrained
|| gExpensiveConstrainedTest_DenyExpensive
)
14911 context
->expectedOperation
= context
->isExpensiveNow
? RESULT_RMV
: RESULT_ADD
;
14913 context
->expectedOperation
= NO_UPDATE
;
14914 context
->operation
= NO_UPDATE
;
14915 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14916 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14918 err
= systemf( NULL
, "ifconfig %s %sexpensive && ifconfig %s %sconstrained", context
->ifName
, context
->isExpensiveNow
? "" : "-", context
->ifName
, context
->isConstrainedNow
? "" : "-" );
14919 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14921 gettimeofday( &context
->updateTime
, NULL
);
14926 ExpensiveConstrainedSubtestReport( context
, errorDescription
);
14927 ExpensiveConstrainedStopAndCleanTheTest( context
);
14928 if ( context
->numOfRetries
> 0 )
14930 context
->state
= TEST_BEGIN
;
14931 context
->numOfRetries
--;
14934 ExpensiveConstrainedSubtestParams
[context
->subtestIndex
++].test_passed
= 0;
14935 if (context
->subtestIndex
== (int) countof( ExpensiveConstrainedSubtestParams
))
14937 ExpensiveConstrainedFinalResultReport( context
, false );
14940 if (context
->timer
== NULL
)
14942 // If timer is NULL, it means that we encounter error before we set up the test handler, which is unrecoverable.
14943 ExpensiveConstrainedFinalResultReport( context
, false );
14946 context
->state
= TEST_BEGIN
;
14948 case TEST_SUCCEEDED
:
14949 ExpensiveConstrainedSubtestReport( context
, NULL
);
14950 ExpensiveConstrainedStopAndCleanTheTest( context
);
14951 ExpensiveConstrainedSubtestParams
[context
->subtestIndex
++].test_passed
= 1;
14952 if (context
->subtestIndex
== (int) countof( ExpensiveConstrainedSubtestParams
))
14954 // all the subtests have been run
14955 Boolean hasFailed
= false;
14956 for ( int i
= 0; i
< (int) countof( ExpensiveConstrainedSubtestParams
) && !hasFailed
; i
++ )
14957 hasFailed
= ( ExpensiveConstrainedSubtestParams
[i
].test_passed
!= 1 );
14959 ExpensiveConstrainedFinalResultReport( context
, !hasFailed
);
14960 exit( hasFailed
? 2 : 0 );
14962 context
->state
= TEST_BEGIN
;
14965 FPrintF( stdout
, "unknown error\n" );
14970 //===========================================================================================================================
14971 // ExpensiveConstrainedCallback
14972 //===========================================================================================================================
14974 static void DNSSD_API
14975 ExpensiveConstrainedCallback(
14976 __unused DNSServiceRef inSDRef
,
14977 DNSServiceFlags inFlags
,
14978 uint32_t inInterfaceIndex
,
14979 DNSServiceErrorType inError
,
14980 const char * inHostname
,
14981 const struct sockaddr
* inSockAddr
,
14982 __unused
uint32_t inTTL
,
14985 ExpensiveConstrainedContext
* const context
= (ExpensiveConstrainedContext
*)inContext
;
14987 const char * addrStr
;
14988 char addrStrBuf
[ kSockAddrStringMaxSize
];
14989 char inFlagsDescription
[ 128 ];
14991 char nowTimestamp
[ 32 ];
14993 switch ( inError
) {
14994 case kDNSServiceErr_NoError
:
14995 case kDNSServiceErr_NoSuchRecord
:
14998 case kDNSServiceErr_Timeout
:
14999 Exit( kExitReason_Timeout
);
15006 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
15008 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
15015 err
= SockAddrToString( inSockAddr
, kSockAddrStringFlagsNone
, addrStrBuf
);
15016 require_noerr( err
, exit
);
15017 addrStr
= addrStrBuf
;
15021 addrStr
= ( inSockAddr
->sa_family
== AF_INET
) ? kNoSuchRecordAStr
: kNoSuchRecordAAAAStr
;
15024 now
= NanoTimeGetCurrent();
15025 _NanoTime64ToTimestamp( now
, nowTimestamp
, sizeof( nowTimestamp
) );
15026 SNPrintF( inFlagsDescription
, sizeof( inFlagsDescription
), "%{du:cbflags}", inFlags
);
15027 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->subtestProgress_callBack
,
15035 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP
, nowTimestamp
,
15036 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME
, inHostname
,
15037 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS
, inFlagsDescription
,
15038 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE
, (int64_t) inInterfaceIndex
,
15039 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS
, addrStr
15041 require_noerr_quiet( err
, exit
);
15043 if ( inFlags
& kDNSServiceFlagsMoreComing
)
15046 if ( inFlags
& kDNSServiceFlagsAdd
)
15047 context
->operation
= RESULT_ADD
;
15049 context
->operation
= RESULT_RMV
;
15051 gettimeofday(&context
->notificationTime
, NULL
);
15053 if( err
) exit( 1 );
15056 //===========================================================================================================================
15057 // ExpensiveConstrainedInitializeContext
15058 //===========================================================================================================================
15060 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext
*context
)
15062 // clear the flags of the previous subtest
15063 context
->flags
= 0;
15064 context
->protocols
= 0;
15066 // get the parameter for the current subtest
15067 const ExpensiveConstrainedTestParams
*param
= &ExpensiveConstrainedSubtestParams
[context
->subtestIndex
];
15068 gExpensiveConstrainedTest_Name
= param
->qname
;
15069 gExpensiveConstrainedTest_DenyExpensive
= param
->deny_expensive
;
15070 gExpensiveConstrainedTest_DenyConstrained
= param
->deny_constrained
;
15071 gExpensiveConstrainedTest_StartFromExpensive
= param
->start_from_expensive
;
15072 gExpensiveConstrainedTest_ProtocolIPv4
= param
->ipv4_query
;
15073 gExpensiveConstrainedTest_ProtocolIPv6
= param
->ipv6_query
;
15076 //===========================================================================================================================
15077 // ExpensiveConstrainedStopAndCleanTheTest
15078 //===========================================================================================================================
15080 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext
*context
)
15082 // Stop the ongoing query
15083 if ( context
->opRef
!= NULL
)
15084 DNSServiceRefDeallocate( context
->opRef
);
15086 context
->opRef
= NULL
;
15087 context
->flags
= 0;
15088 context
->protocols
= 0;
15091 //===========================================================================================================================
15092 // ExpensiveConstrainedSubtestProgressReport
15093 //===========================================================================================================================
15095 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext
*context
)
15099 char startTime
[ 32 ];
15100 char endTime
[ 32 ];
15101 char expensive
[ 32 ];
15102 char constrained
[ 32 ];
15104 now
= NanoTimeGetCurrent();
15105 _NanoTime64ToTimestamp( context
->subtestProgress_startTime
, startTime
, sizeof( startTime
) );
15106 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
15108 snprintf( expensive
, sizeof( expensive
), "%s -> %s", context
->isExpensivePrev
? "True" : "False", context
->isExpensiveNow
? "True" : "False" );
15109 snprintf( constrained
, sizeof( constrained
), "%s -> %s", context
->isConstrainedPrev
? "True" : "False", context
->isConstrainedNow
? "True" : "False" );
15111 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->subtestProgress
,
15122 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME
, startTime
,
15123 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME
, endTime
,
15124 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE
, ExpensiveConstrainedStateString(context
->state
),
15125 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT
, ExpensiveConstrainedOperationString(context
->expectedOperation
),
15126 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT
, ExpensiveConstrainedOperationString(context
->operation
),
15127 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW
, expensive
,
15128 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW
, constrained
,
15129 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK
, context
->subtestProgress_callBack
15131 require_noerr( err
, exit
);
15132 ForgetCF( &context
->subtestProgress_callBack
);
15136 ErrQuit( 1, "error: %#m\n", err
);
15139 //===========================================================================================================================
15140 // ExpensiveConstrainedFinalSubtestReport
15141 //===========================================================================================================================
15143 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext
*context
, const char *error_description
)
15147 char startTime
[ 32 ];
15148 char endTime
[ 32 ];
15149 char flagDescription
[ 1024 ];
15151 now
= NanoTimeGetCurrent();
15152 _NanoTime64ToTimestamp( context
->subtestReport_startTime
, startTime
, sizeof( startTime
) );
15153 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
15154 SNPrintF( flagDescription
, sizeof( flagDescription
), "%#{flags}", context
->flags
, kDNSServiceFlagsDescriptors
);
15156 if (error_description
!= NULL
)
15158 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->subtestReport
,
15171 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME
, startTime
,
15172 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME
, endTime
,
15173 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME
, context
->name
,
15174 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS
, flagDescription
,
15175 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS
, ExpensiveConstrainedProtocolString( context
->protocols
),
15176 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX
, (int64_t) context
->ifIndex
,
15177 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME
, context
->ifName
,
15178 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT
, CFSTR( "Fail" ),
15179 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR
, error_description
,
15180 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS
, context
->subtestProgress
15185 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->subtestReport
,
15197 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME
, startTime
,
15198 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME
, endTime
,
15199 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME
, context
->name
,
15200 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS
, flagDescription
,
15201 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS
, ExpensiveConstrainedProtocolString( context
->protocols
),
15202 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX
, (int64_t) context
->ifIndex
,
15203 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME
, context
->ifName
,
15204 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT
, CFSTR( "Pass" ),
15205 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS
, context
->subtestProgress
15209 require_noerr( err
, exit
);
15210 ForgetCF( &context
->subtestProgress
);
15213 ErrQuit( 1, "error: %#m\n", err
);
15216 //===========================================================================================================================
15217 // ExpensiveConstrainedFinalResultReport
15218 //===========================================================================================================================
15220 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext
*context
, Boolean allPassed
)
15223 CFPropertyListRef plist
;
15225 char startTime
[ 32 ];
15226 char endTime
[ 32 ];
15228 now
= NanoTimeGetCurrent();
15229 _NanoTime64ToTimestamp( context
->testReport_startTime
, startTime
, sizeof( startTime
) );
15230 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
15232 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
15239 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME
, startTime
,
15240 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME
, endTime
,
15241 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED
, allPassed
,
15242 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT
, context
->subtestReport
15244 require_noerr( err
, exit
);
15245 ForgetCF( &context
->subtestReport
);
15247 err
= OutputPropertyList( plist
, context
->outputFormat
, context
->outputFilePath
);
15248 CFRelease( plist
);
15249 require_noerr( err
, exit
);
15253 ErrQuit( 1, "error: %#m\n", err
);
15256 //===========================================================================================================================
15257 // ExpensiveConstrainedProtocolString
15258 //===========================================================================================================================
15260 static const char *ExpensiveConstrainedProtocolString( DNSServiceProtocol protocol
)
15262 const char *str
= NULL
;
15263 switch ( protocol
) {
15264 case kDNSServiceProtocol_IPv4
:
15267 case kDNSServiceProtocol_IPv6
:
15270 case kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
:
15271 str
= "IPv4 & IPv6";
15279 //===========================================================================================================================
15280 // ExpensiveConstrainedStateString
15281 //===========================================================================================================================
15283 static const char *ExpensiveConstrainedStateString( enum ExpensiveConstrainedTestState state
)
15285 const char *str
= NULL
;
15288 str
= "TEST_BEGIN";
15290 case TEST_EXPENSIVE_PREPARE
:
15291 str
= "TEST_EXPENSIVE_PREPARE";
15293 case TEST_EXPENSIVE
:
15294 str
= "TEST_EXPENSIVE";
15296 case TEST_CONSTRAINED_PREPARE
:
15297 str
= "TEST_CONSTRAINED_PREPARE";
15299 case TEST_CONSTRAINED
:
15300 str
= "TEST_CONSTRAINED";
15302 case TEST_EXPENSIVE_CONSTRAINED_PREPARE
:
15303 str
= "TEST_EXPENSIVE_CONSTRAINED_PREPARE";
15305 case TEST_EXPENSIVE_CONSTRAINED
:
15306 str
= "TEST_EXPENSIVE_CONSTRAINED";
15309 str
= "TEST_FAILED";
15311 case TEST_SUCCEEDED
:
15312 str
= "TEST_SUCCEEDED";
15322 //===========================================================================================================================
15323 // ExpensiveConstrainedOperationString
15324 //===========================================================================================================================
15326 static const char *ExpensiveConstrainedOperationString( enum ExpensiveConstrainedTestOperation operation
)
15328 const char *str
= NULL
;
15329 switch ( operation
) {
15331 str
= "RESULT_ADD";
15334 str
= "RESULT_RMV";
15346 //===========================================================================================================================
15347 // expensiveConstrainedEndsWith
15348 //===========================================================================================================================
15349 static Boolean
expensiveConstrainedEndsWith( const char *str
, const char *suffix
)
15351 if ( !str
|| !suffix
)
15353 size_t lenstr
= strlen( str
);
15354 size_t lensuffix
= strlen( suffix
);
15355 if ( lensuffix
> lenstr
)
15357 return strncmp( str
+ lenstr
- lensuffix
, suffix
, lensuffix
) == 0;
15360 //===========================================================================================================================
15361 // RegistrationTestCmd
15362 //===========================================================================================================================
15364 typedef struct RegistrationSubtest RegistrationSubtest
;
15368 CFMutableArrayRef subtestReports
; // Array of subtest reports.
15369 dispatch_source_t timer
; // Timer to enforce subtest durations.
15370 dispatch_source_t sigSourceINT
; // SIGINT signal handler for a clean test exit.
15371 dispatch_source_t sigSourceTERM
; // SIGTERM signal handler for a clean test exit.
15372 RegistrationSubtest
* subtest
; // Current subtest.
15373 char * outputFilePath
; // Path of test result output file. If NULL, stdout will be used.
15374 OutputFormatType outputFormat
; // Format of test results output.
15375 CFStringRef computerNamePrev
; // Previous ComputerName.
15376 CFStringRef localHostNamePrev
; // Previous LocalHostName.
15377 NanoTime64 startTime
; // Test's start time.
15378 char * computerName
; // Temporary ComputerName to set during testing. (malloc'd)
15379 char * localHostName
; // Temporary LocalHostName to set during testing. (malloc'd)
15380 CFStringEncoding computerNamePrevEncoding
; // Previous ComputerName's encoding.
15381 int subtestIndex
; // Index of current subtest.
15382 Boolean computerNameSet
; // True if a temporary ComputerName was set.
15383 Boolean localHostNameSet
; // True if a temporary LocalHostName was set.
15384 Boolean failed
; // True if at least one non-skipped subtest failed.
15385 Boolean forBATS
; // True if the test is running in a BATS environment.
15387 } RegistrationTest
;
15391 kRegistrationInterfaceSet_Null
= 0,
15392 kRegistrationInterfaceSet_All
= 1,
15393 kRegistrationInterfaceSet_AllPlusAWDL
= 2,
15394 kRegistrationInterfaceSet_LoopbackOnly
= 3,
15395 kRegistrationInterfaceSet_AWDLOnly
= 4
15397 } RegistrationInterfaceSet
;
15401 RegistrationInterfaceSet interfaceSet
; // Interfaces to register the service over.
15402 Boolean useDefaultName
; // True if registration is to use the default service name.
15403 Boolean useLODiscovery
; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
15405 } RegistrationSubtestParams
;
15407 static const RegistrationSubtestParams kRegistrationSubtestParams
[] =
15409 { kRegistrationInterfaceSet_All
, true, false },
15410 { kRegistrationInterfaceSet_All
, false, false },
15411 { kRegistrationInterfaceSet_AllPlusAWDL
, true, false },
15412 { kRegistrationInterfaceSet_AllPlusAWDL
, false, false },
15413 { kRegistrationInterfaceSet_LoopbackOnly
, true, false },
15414 { kRegistrationInterfaceSet_LoopbackOnly
, false, false },
15415 { kRegistrationInterfaceSet_AWDLOnly
, true, false },
15416 { kRegistrationInterfaceSet_AWDLOnly
, false, false },
15417 { kRegistrationInterfaceSet_All
, true, true },
15418 { kRegistrationInterfaceSet_All
, false, true },
15419 { kRegistrationInterfaceSet_AllPlusAWDL
, true, true },
15420 { kRegistrationInterfaceSet_AllPlusAWDL
, false, true },
15421 { kRegistrationInterfaceSet_LoopbackOnly
, true, true },
15422 { kRegistrationInterfaceSet_LoopbackOnly
, false, true },
15423 { kRegistrationInterfaceSet_AWDLOnly
, true, true },
15424 { kRegistrationInterfaceSet_AWDLOnly
, false, true }
15429 NanoTime64 browseResultTime
; // Per-interface browse result time.
15430 NanoTime64 querySRVResultTime
; // Per-interface SRV record query result time.
15431 NanoTime64 queryTXTResultTime
; // Per-interface TXT record query result time.
15433 } RegistrationResultTimes
;
15437 MDNSInterfaceItem base
; // Underlying MDNSInterface linked-list item.
15438 RegistrationResultTimes times
; // Per-interface result times.
15440 } RegistrationInterfaceItem
;
15442 struct RegistrationSubtest
15444 DNSServiceRef registration
; // DNS-SD service registration.
15445 DNSServiceRef connection
; // Shared DNS-SD connection.
15446 DNSServiceRef browse
; // DNS-SD browse for service's type.
15447 DNSServiceRef querySRV
; // DNS-SD query request for service's SRV record.
15448 DNSServiceRef queryTXT
; // DNS-SD query request for service's TXT record.
15449 CFMutableArrayRef unexpected
; // Array of unexpected registration, browse, and query results.
15450 #if( TARGET_OS_WATCH )
15451 CFMutableArrayRef ignored
; // Array of unexpected, but ignored, browse and query results.
15453 const char * serviceName
; // Service's name.
15454 char * serviceNameCustom
; // Service's name if using a custom name. (malloc'd)
15455 char * serviceType
; // Service's service type. (malloc'd)
15456 size_t serviceTypeLen
; // C string length of service's service type.
15457 char * serviceFQDN
; // Service's FQDN, i.e., name of its SRV and TXT records.
15458 uint8_t * txtPtr
; // Pointer to service's TXT record data. (malloc'd)
15459 size_t txtLen
; // Length of service's TXT record data.
15460 RegistrationInterfaceItem
* ifList
; // If ifIndex == 0, interfaces that service should register over.
15461 RegistrationResultTimes ifTimes
; // If ifIndex != 0, result times for interface with that index.
15462 RegistrationTest
* test
; // Pointer to parent test.
15463 NanoTime64 startTime
; // Subtest's start time.
15464 char * description
; // Subtest's description. (malloc'd)
15465 uint32_t ifIndex
; // Interface index used for service registration.
15466 uint16_t port
; // Service's port number.
15467 Boolean useLODiscovery
; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
15468 Boolean includeAWDL
; // True if the IncludeAWDL flag was used during registration.
15469 Boolean ifIsAWDL
; // True if ifIndex is the index of an AWDL interface.
15470 Boolean skipped
; // True if this subtest is to be skipped.
15471 Boolean registered
; // True if the test service was successfully registered.
15472 Boolean useDefaultName
; // True if the service is to use the default service name.
15475 static OSStatus
_RegistrationTestCreate( RegistrationTest
**outTest
);
15476 static void _RegistrationTestFree( RegistrationTest
*inTest
);
15477 static void _RegistrationTestBegin( void *inContext
);
15478 static void _RegistrationTestProceed( RegistrationTest
*inTest
);
15479 static OSStatus
_RegistrationTestStart( RegistrationTest
*inTest
);
15480 static void _RegistrationTestStop( RegistrationTest
*inTest
);
15481 #define _RegistrationTestForget( X ) ForgetCustomEx( X, _RegistrationTestStop, _RegistrationTestFree )
15483 _RegistrationTestStartSubtest(
15484 RegistrationTest
* inTest
,
15485 const RegistrationSubtestParams
* inParams
,
15486 Boolean
* outSkipped
);
15487 static OSStatus
_RegistrationTestEndSubtest( RegistrationTest
*inTest
);
15488 static void _RegistrationTestEnd( RegistrationTest
*inTest
) ATTRIBUTE_NORETURN
;
15489 static void _RegistrationTestExit( RegistrationTest
*inTest
, OSStatus inError
) ATTRIBUTE_NORETURN
;
15490 static OSStatus
_RegistrationSubtestCreate( RegistrationSubtest
**outSubtest
);
15491 static void _RegistrationSubtestStop( RegistrationSubtest
*inSubtest
);
15492 static void _RegistrationSubtestFree( RegistrationSubtest
*inSubtest
);
15493 #define _RegistrationSubtestForget( X ) ForgetCustomEx( X, _RegistrationSubtestStop, _RegistrationSubtestFree )
15494 static OSStatus
_RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL
, RegistrationInterfaceItem
**outList
);
15496 _RegistrationTestCreateRandomTXTRecord(
15499 uint8_t ** outTXTPtr
,
15500 size_t * outTXTLen
);
15501 static void DNSSD_API
15502 _RegistrationSubtestRegisterCallback(
15503 DNSServiceRef inSDRef
,
15504 DNSServiceFlags inFlags
,
15505 DNSServiceErrorType inError
,
15506 const char * inName
,
15507 const char * inType
,
15508 const char * inDomain
,
15509 void * inContext
);
15510 static void DNSSD_API
15511 _RegistrationSubtestBrowseCallback(
15512 DNSServiceRef inSDRef
,
15513 DNSServiceFlags inFlags
,
15514 uint32_t inIfIndex
,
15515 DNSServiceErrorType inError
,
15516 const char * inServiceName
,
15517 const char * inServiceType
,
15518 const char * inDomain
,
15519 void * inContext
);
15520 static void DNSSD_API
15521 _RegistrationSubtestQueryCallback(
15522 DNSServiceRef inSDRef
,
15523 DNSServiceFlags inFlags
,
15524 uint32_t inIfIndex
,
15525 DNSServiceErrorType inError
,
15526 const char * inName
,
15529 uint16_t inRDataLen
,
15530 const void * inRDataPtr
,
15532 void * inContext
);
15533 static Boolean
_RegistrationSubtestValidServiceType( const RegistrationSubtest
*inSubtest
, const char *inServiceType
);
15534 static RegistrationResultTimes
*
15535 _RegistrationSubtestGetInterfaceResultTimes(
15536 RegistrationSubtest
* inSubtest
,
15537 uint32_t inIfIndex
,
15538 Boolean
* outIsAWDL
);
15539 static void _RegistrationTestTimerHandler( void *inContext
);
15540 #if( TARGET_OS_WATCH )
15541 static Boolean
_RegistrationTestInterfaceIsWiFi( const char *inIfName
);
15544 static void RegistrationTestCmd( void )
15547 RegistrationTest
* test
;
15549 err
= _RegistrationTestCreate( &test
);
15550 require_noerr( err
, exit
);
15552 if( gRegistrationTest_BATSEnvironment
) test
->forBATS
= true;
15553 if( gRegistrationTest_OutputFilePath
)
15555 test
->outputFilePath
= strdup( gRegistrationTest_OutputFilePath
);
15556 require_action( test
->outputFilePath
, exit
, err
= kNoMemoryErr
);
15559 err
= OutputFormatFromArgString( gRegistrationTest_OutputFormat
, &test
->outputFormat
);
15560 require_noerr_quiet( err
, exit
);
15562 dispatch_async_f( dispatch_get_main_queue(), test
, _RegistrationTestBegin
);
15566 if( test
) _RegistrationTestFree( test
);
15567 ErrQuit( 1, "error: %#m\n", err
);
15570 //===========================================================================================================================
15572 static OSStatus
_RegistrationTestCreate( RegistrationTest
**outTest
)
15575 RegistrationTest
* obj
;
15577 obj
= (RegistrationTest
*) calloc( 1, sizeof( *obj
) );
15578 require_action( obj
, exit
, err
= kNoMemoryErr
);
15580 obj
->subtestReports
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
15581 require_action( obj
->subtestReports
, exit
, err
= kNoMemoryErr
);
15588 if( obj
) _RegistrationTestFree( obj
);
15592 //===========================================================================================================================
15594 static void _RegistrationTestFree( RegistrationTest
*inTest
)
15596 check( !inTest
->timer
);
15597 check( !inTest
->sigSourceINT
);
15598 check( !inTest
->sigSourceTERM
);
15599 check( !inTest
->computerNameSet
);
15600 check( !inTest
->localHostNameSet
);
15601 check( !inTest
->subtest
);
15602 ForgetCF( &inTest
->subtestReports
);
15603 ForgetMem( &inTest
->outputFilePath
);
15604 ForgetCF( &inTest
->computerNamePrev
);
15605 ForgetCF( &inTest
->localHostNamePrev
);
15606 ForgetMem( &inTest
->computerName
);
15607 ForgetMem( &inTest
->localHostName
);
15610 //===========================================================================================================================
15612 static void _RegistrationTestBegin( void *inContext
)
15614 _RegistrationTestProceed( (RegistrationTest
*) inContext
);
15617 //===========================================================================================================================
15619 static void _RegistrationTestProceed( RegistrationTest
*inTest
)
15622 Boolean skippedSubtest
;
15628 if( !inTest
->startTime
)
15630 err
= _RegistrationTestStart( inTest
);
15631 require_noerr_quiet( err
, exit
);
15633 inTest
->startTime
= NanoTimeGetCurrent();
15637 err
= _RegistrationTestEndSubtest( inTest
);
15638 require_noerr( err
, exit
);
15640 ++inTest
->subtestIndex
;
15643 subtestIndex
= inTest
->subtestIndex
;
15644 if( subtestIndex
< (int) countof( kRegistrationSubtestParams
) )
15646 err
= _RegistrationTestStartSubtest( inTest
, &kRegistrationSubtestParams
[ subtestIndex
], &skippedSubtest
);
15647 require_noerr_quiet( err
, exit
);
15651 _RegistrationTestEnd( inTest
);
15654 } while( skippedSubtest
);
15657 if( err
) _RegistrationTestExit( inTest
, err
);
15660 //===========================================================================================================================
15662 static void _RegistrationTestSignalHandler( void *inContext
);
15664 static OSStatus
_RegistrationTestStart( RegistrationTest
*inTest
)
15669 // Save original ComputerName and LocalHostName.
15671 check( !inTest
->computerNamePrev
);
15672 inTest
->computerNamePrev
= SCDynamicStoreCopyComputerName( NULL
, &inTest
->computerNamePrevEncoding
);
15673 err
= map_scerror( inTest
->computerNamePrev
);
15674 require_noerr( err
, exit
);
15676 check( !inTest
->localHostNamePrev
);
15677 inTest
->localHostNamePrev
= SCDynamicStoreCopyLocalHostName( NULL
);
15678 err
= map_scerror( inTest
->localHostNamePrev
);
15679 require_noerr( err
, exit
);
15681 // Generate a unique test ComputerName.
15683 check( !inTest
->computerName
);
15684 ASPrintF( &inTest
->computerName
, "dnssdutil-regtest-computer-name-%s",
15685 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
15686 require_action( inTest
->computerName
, exit
, err
= kNoMemoryErr
);
15688 // Generate a unique test LocalHostName.
15690 check( !inTest
->localHostName
);
15691 ASPrintF( &inTest
->localHostName
, "dnssdutil-regtest-local-hostname-%s",
15692 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
15693 require_action( inTest
->localHostName
, exit
, err
= kNoMemoryErr
);
15695 // Set up SIGINT signal handler.
15697 signal( SIGINT
, SIG_IGN
);
15698 check( !inTest
->sigSourceINT
);
15699 err
= DispatchSignalSourceCreate( SIGINT
, _RegistrationTestSignalHandler
, inTest
, &inTest
->sigSourceINT
);
15700 require_noerr( err
, exit
);
15701 dispatch_resume( inTest
->sigSourceINT
);
15703 // Set up SIGTERM signal handler.
15705 signal( SIGTERM
, SIG_IGN
);
15706 check( !inTest
->sigSourceTERM
);
15707 err
= DispatchSignalSourceCreate( SIGTERM
, _RegistrationTestSignalHandler
, inTest
, &inTest
->sigSourceTERM
);
15708 require_noerr( err
, exit
);
15709 dispatch_resume( inTest
->sigSourceTERM
);
15711 // Set test ComputerName.
15713 check( !inTest
->computerNameSet
);
15714 err
= _SetComputerNameWithUTF8CString( inTest
->computerName
);
15715 require_noerr( err
, exit
);
15716 inTest
->computerNameSet
= true;
15718 // Set test LocalHostName.
15720 check( !inTest
->localHostNameSet
);
15721 err
= _SetLocalHostNameWithUTF8CString( inTest
->localHostName
);
15722 require_noerr( err
, exit
);
15723 inTest
->localHostNameSet
= true;
15726 if( err
) _RegistrationTestStop( inTest
);
15730 static void _RegistrationTestSignalHandler( void *inContext
)
15732 RegistrationTest
* const test
= (RegistrationTest
*) inContext
;
15734 FPrintF( stderr
, "Registration test got a SIGINT or SIGTERM signal, exiting..." );
15736 _RegistrationTestExit( test
, kCanceledErr
);
15739 //===========================================================================================================================
15741 static void _RegistrationTestStop( RegistrationTest
*inTest
)
15745 dispatch_source_forget( &inTest
->timer
);
15746 dispatch_source_forget( &inTest
->sigSourceINT
);
15747 dispatch_source_forget( &inTest
->sigSourceTERM
);
15748 _RegistrationSubtestForget( &inTest
->subtest
);
15749 if( inTest
->computerNameSet
)
15751 err
= _SetComputerName( inTest
->computerNamePrev
, inTest
->computerNamePrevEncoding
);
15752 check_noerr( err
);
15753 if( !err
) inTest
->computerNameSet
= false;
15755 if( inTest
->localHostNameSet
)
15757 err
= _SetLocalHostName( inTest
->localHostNamePrev
);
15758 check_noerr( err
);
15759 if( !err
) inTest
->localHostNameSet
= false;
15763 //===========================================================================================================================
15765 #define kRegistrationTestSubtestDurationSecs 5
15768 _RegistrationTestStartSubtest(
15769 RegistrationTest
* inTest
,
15770 const RegistrationSubtestParams
* inParams
,
15771 Boolean
* outSkipped
)
15774 RegistrationSubtest
* subtest
;
15775 const char * interfaceDesc
;
15776 DNSServiceFlags flags
;
15780 err
= _RegistrationSubtestCreate( &subtest
);
15781 require_noerr( err
, exit
);
15783 subtest
->test
= inTest
;
15784 subtest
->useDefaultName
= inParams
->useDefaultName
;
15785 subtest
->useLODiscovery
= inParams
->useLODiscovery
;
15787 // Determine registration interfaces.
15789 switch( inParams
->interfaceSet
)
15791 case kRegistrationInterfaceSet_All
:
15792 subtest
->ifIndex
= kDNSServiceInterfaceIndexAny
;
15794 if( !subtest
->useLODiscovery
)
15796 err
= _RegistrationTestInterfaceListCreate( false, &subtest
->ifList
);
15797 require_noerr( err
, exit
);
15799 interfaceDesc
= "all interfaces (excluding AWDL)";
15802 case kRegistrationInterfaceSet_AllPlusAWDL
:
15803 subtest
->ifIndex
= kDNSServiceInterfaceIndexAny
;
15804 subtest
->includeAWDL
= true;
15806 if( !subtest
->useLODiscovery
)
15808 err
= _RegistrationTestInterfaceListCreate( true, &subtest
->ifList
);
15809 require_noerr( err
, exit
);
15811 interfaceDesc
= "all interfaces (including AWDL)";
15814 case kRegistrationInterfaceSet_LoopbackOnly
:
15815 subtest
->ifIndex
= if_nametoindex( "lo0" );
15816 if( subtest
->ifIndex
== 0 )
15818 FPrintF( stderr
, "Failed to get index for loopback interface lo0.\n" );
15819 err
= kNoResourcesErr
;
15822 interfaceDesc
= "loopback interface";
15825 case kRegistrationInterfaceSet_AWDLOnly
:
15826 err
= _MDNSInterfaceGetAny( kMDNSInterfaceSubset_AWDL
, NULL
, &subtest
->ifIndex
);
15827 if( err
== kNotFoundErr
)
15829 FPrintF( stderr
, "Warning: No mDNS-capable AWDL interface is available.\n" );
15830 subtest
->skipped
= true;
15833 require_noerr( err
, exit
);
15835 subtest
->ifIsAWDL
= true;
15836 interfaceDesc
= "AWDL interface";
15844 // Create description.
15846 ASPrintF( &subtest
->description
, "Service registration over %s using %s service name.%s",
15847 interfaceDesc
, subtest
->useDefaultName
? "default" : "custom",
15848 subtest
->useLODiscovery
? " (LocalOnly discovery)" : "" );
15849 require_action( subtest
->description
, exit
, err
= kNoMemoryErr
);
15851 if( subtest
->skipped
)
15853 subtest
->startTime
= NanoTimeGetCurrent();
15857 // Generate a service name.
15859 if( subtest
->useDefaultName
)
15861 subtest
->serviceName
= inTest
->computerName
;
15865 ASPrintF( &subtest
->serviceNameCustom
, "dnssdutil-regtest-service-name-%s",
15866 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
15867 require_action( subtest
->serviceNameCustom
, exit
, err
= kNoMemoryErr
);
15869 subtest
->serviceName
= subtest
->serviceNameCustom
;
15872 // Generate a service type.
15874 ASPrintF( &subtest
->serviceType
, "_regtest-%s._udp",
15875 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
15876 require_action( subtest
->serviceType
, exit
, err
= kNoMemoryErr
);
15878 subtest
->serviceTypeLen
= strlen( subtest
->serviceType
);
15880 // Create SRV and TXT record name FQDN.
15882 ASPrintF( &subtest
->serviceFQDN
, "%s.%s.local.", subtest
->serviceName
, subtest
->serviceType
);
15883 require_action( subtest
->serviceFQDN
, exit
, err
= kNoMemoryErr
);
15885 // Generate a port number.
15887 subtest
->port
= (uint16_t) RandomRange( 60000, 65535 );
15889 // Generate TXT record data.
15891 err
= _RegistrationTestCreateRandomTXTRecord( 100, 1000, &subtest
->txtPtr
, &subtest
->txtLen
);
15892 require_noerr( err
, exit
);
15894 // Register service.
15896 subtest
->startTime
= NanoTimeGetCurrent();
15898 flags
= kDNSServiceFlagsNoAutoRename
;
15899 if( subtest
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
15900 err
= DNSServiceRegister( &subtest
->registration
, flags
, subtest
->ifIndex
,
15901 subtest
->useDefaultName
? NULL
: subtest
->serviceNameCustom
, subtest
->serviceType
, "local.",
15902 NULL
, htons( subtest
->port
), (uint16_t) subtest
->txtLen
, subtest
->txtPtr
,
15903 _RegistrationSubtestRegisterCallback
, subtest
);
15904 require_noerr( err
, exit
);
15906 err
= DNSServiceSetDispatchQueue( subtest
->registration
, dispatch_get_main_queue() );
15907 require_noerr( err
, exit
);
15911 check( !inTest
->timer
);
15912 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kRegistrationTestSubtestDurationSecs
),
15913 INT64_C_safe( kRegistrationTestSubtestDurationSecs
) * kNanosecondsPerSecond
/ 10, dispatch_get_main_queue(),
15914 _RegistrationTestTimerHandler
, inTest
, &inTest
->timer
);
15915 require_noerr( err
, exit
);
15916 dispatch_resume( inTest
->timer
);
15919 *outSkipped
= subtest
->skipped
;
15921 check( !inTest
->subtest
);
15922 inTest
->subtest
= subtest
;
15926 _RegistrationSubtestForget( &subtest
);
15930 //===========================================================================================================================
15932 #define kRegistrationTestReportKey_ComputerName CFSTR( "computerName" ) // String
15933 #define kRegistrationTestReportKey_Description CFSTR( "description" ) // String
15934 #define kRegistrationTestReportKey_Domain CFSTR( "domain" ) // String
15935 #define kRegistrationTestReportKey_EndTime CFSTR( "endTime" ) // String
15936 #define kRegistrationTestReportKey_Error CFSTR( "error" ) // Integer
15937 #define kRegistrationTestReportKey_Flags CFSTR( "flags" ) // Integer
15938 #define kRegistrationTestReportKey_IgnoredResults CFSTR( "ignoredResults" ) // Array of dictionaries
15939 #define kRegistrationTestReportKey_InterfaceIndex CFSTR( "ifIndex" ) // Integer
15940 #define kRegistrationTestReportKey_InterfaceName CFSTR( "ifName" ) // String
15941 #define kRegistrationTestReportKey_LocalHostName CFSTR( "localHostName" ) // String
15942 #define kRegistrationTestReportKey_MissingResults CFSTR( "missingResults" ) // Array of dictionaries
15943 #define kRegistrationTestReportKey_Pass CFSTR( "pass" ) // Boolean
15944 #define kRegistrationTestReportKey_Port CFSTR( "port" ) // Integer
15945 #define kRegistrationTestReportKey_RDataFormatted CFSTR( "rdataFormatted" ) // String
15946 #define kRegistrationTestReportKey_RDataHexString CFSTR( "rdataHexString" ) // String
15947 #define kRegistrationTestReportKey_RecordClass CFSTR( "recordClass" ) // Integer
15948 #define kRegistrationTestReportKey_RecordType CFSTR( "recordType" ) // Integer
15949 #define kRegistrationTestReportKey_Registered CFSTR( "registered" ) // Boolean
15950 #define kRegistrationTestReportKey_ResultType CFSTR( "resultType" ) // String
15951 #define kRegistrationTestReportKey_ServiceFQDN CFSTR( "serviceFQDN" ) // String
15952 #define kRegistrationTestReportKey_ServiceName CFSTR( "serviceName" ) // String
15953 #define kRegistrationTestReportKey_ServiceType CFSTR( "serviceType" ) // String
15954 #define kRegistrationTestReportKey_Skipped CFSTR( "skipped" ) // Boolean
15955 #define kRegistrationTestReportKey_StartTime CFSTR( "startTime" ) // String
15956 #define kRegistrationTestReportKey_Subtests CFSTR( "subtests" ) // Array of dictionaries
15957 #define kRegistrationTestReportKey_Timestamp CFSTR( "timestamp" ) // String
15958 #define kRegistrationTestReportKey_TXT CFSTR( "txt" ) // String
15959 #define kRegistrationTestReportKey_UnexpectedResults CFSTR( "unexpectedResults" ) // Array of dictionaries
15960 #define kRegistrationTestReportKey_UsedDefaultName CFSTR( "usedDefaultName" ) // Boolean
15961 #define kRegistrationTestReportKey_UsedLODiscovery CFSTR( "usedLODiscovery" ) // Boolean
15963 #define kRegistrationTestResultType_Browse CFSTR( "browse" )
15964 #define kRegistrationTestResultType_Query CFSTR( "query" )
15965 #define kRegistrationTestResultType_QuerySRV CFSTR( "querySRV" )
15966 #define kRegistrationTestResultType_QueryTXT CFSTR( "queryTXT" )
15967 #define kRegistrationTestResultType_Registration CFSTR( "registration" )
15970 _RegistrationTestAppendMissingResults(
15971 CFMutableArrayRef inMissingResults
,
15972 const RegistrationResultTimes
* inTimes
,
15973 uint32_t inIfIndex
,
15974 const char * inIfName
);
15976 static OSStatus
_RegistrationTestEndSubtest( RegistrationTest
*inTest
)
15979 RegistrationSubtest
* subtest
;
15980 CFMutableDictionaryRef subtestReport
;
15981 CFMutableArrayRef missing
;
15984 Boolean subtestFailed
;
15985 char startTime
[ 32 ];
15986 char endTime
[ 32 ];
15987 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
15989 now
= NanoTimeGetCurrent();
15991 subtest
= inTest
->subtest
;
15992 inTest
->subtest
= NULL
;
15993 _RegistrationSubtestStop( subtest
);
15996 subtestReport
= NULL
;
15998 if( subtest
->txtPtr
)
16000 err
= DNSRecordDataToString( subtest
->txtPtr
, subtest
->txtLen
, kDNSServiceType_TXT
, NULL
, 0, &txtStr
);
16001 require_noerr( err
, exit
);
16003 _NanoTime64ToTimestamp( subtest
->startTime
, startTime
, sizeof( startTime
) );
16004 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
16005 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &subtestReport
,
16007 "%kO=%s" // description
16008 "%kO=%s" // startTime
16009 "%kO=%s" // endTime
16010 "%kO=%s" // serviceFQDN
16011 "%kO=%lli" // ifIndex
16015 "%kO=%b" // registered
16016 "%kO=%b" // usedDefaultName
16017 "%kO=%b" // usedLODiscovery
16019 kRegistrationTestReportKey_Description
, subtest
->description
,
16020 kRegistrationTestReportKey_StartTime
, startTime
,
16021 kRegistrationTestReportKey_EndTime
, endTime
,
16022 kRegistrationTestReportKey_ServiceFQDN
, subtest
->serviceFQDN
,
16023 kRegistrationTestReportKey_InterfaceIndex
, (int64_t) subtest
->ifIndex
,
16024 kRegistrationTestReportKey_InterfaceName
, if_indextoname( subtest
->ifIndex
, ifNameBuf
),
16025 kRegistrationTestReportKey_Port
, (int64_t) subtest
->port
,
16026 kRegistrationTestReportKey_TXT
, txtStr
,
16027 kRegistrationTestReportKey_Registered
, (int) subtest
->registered
,
16028 kRegistrationTestReportKey_UsedDefaultName
, (int) subtest
->useDefaultName
,
16029 kRegistrationTestReportKey_UsedLODiscovery
, (int) subtest
->useLODiscovery
);
16030 ForgetMem( &txtStr
);
16031 require_noerr( err
, exit
);
16033 if( !subtest
->skipped
&& subtest
->registered
)
16035 missing
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
16036 require_action( missing
, exit
, err
= kNoMemoryErr
);
16038 if( subtest
->ifList
)
16040 RegistrationInterfaceItem
* item
;
16042 for( item
= subtest
->ifList
; item
; item
= (RegistrationInterfaceItem
*) item
->base
.next
)
16044 #if( TARGET_OS_WATCH )
16045 if( inTest
->forBATS
&& item
->base
.isWiFi
) continue;
16047 err
= _RegistrationTestAppendMissingResults( missing
, &item
->times
, item
->base
.ifIndex
, item
->base
.ifName
);
16048 require_noerr( err
, exit
);
16053 err
= _RegistrationTestAppendMissingResults( missing
, &subtest
->ifTimes
, subtest
->ifIndex
, NULL
);
16054 require_noerr( err
, exit
);
16057 subtestFailed
= false;
16058 if( CFArrayGetCount( missing
) > 0 )
16060 subtestFailed
= true;
16061 CFDictionarySetValue( subtestReport
, kRegistrationTestReportKey_MissingResults
, missing
);
16063 if( CFArrayGetCount( subtest
->unexpected
) > 0 )
16065 subtestFailed
= true;
16066 CFDictionarySetValue( subtestReport
, kRegistrationTestReportKey_UnexpectedResults
, subtest
->unexpected
);
16068 #if( TARGET_OS_WATCH )
16069 if( CFArrayGetCount( subtest
->ignored
) > 0 )
16071 CFDictionarySetValue( subtestReport
, kRegistrationTestReportKey_IgnoredResults
, subtest
->ignored
);
16077 subtestFailed
= true;
16080 CFDictionarySetBoolean( subtestReport
, kRegistrationTestReportKey_Pass
, subtestFailed
? false : true );
16081 if( subtestFailed
)
16083 CFDictionarySetBoolean( subtestReport
, kRegistrationTestReportKey_Skipped
, subtest
->skipped
);
16084 if( !subtest
->skipped
) inTest
->failed
= true;
16086 CFArrayAppendValue( inTest
->subtestReports
, subtestReport
);
16089 CFReleaseNullSafe( missing
);
16090 CFReleaseNullSafe( subtestReport
);
16091 _RegistrationSubtestFree( subtest
);
16096 _RegistrationTestAppendMissingResult(
16097 CFMutableArrayRef inMissingResults
,
16098 CFStringRef inType
,
16099 uint32_t inIfIndex
,
16100 const char * inIfName
);
16103 _RegistrationTestAppendMissingResults(
16104 CFMutableArrayRef inMissingResults
,
16105 const RegistrationResultTimes
* inTimes
,
16106 uint32_t inIfIndex
,
16107 const char * inIfName
)
16111 if( !inTimes
->browseResultTime
)
16113 err
= _RegistrationTestAppendMissingResult( inMissingResults
, kRegistrationTestResultType_Browse
,
16114 inIfIndex
, inIfName
);
16115 require_noerr( err
, exit
);
16117 if( !inTimes
->querySRVResultTime
)
16119 err
= _RegistrationTestAppendMissingResult( inMissingResults
, kRegistrationTestResultType_QuerySRV
,
16120 inIfIndex
, inIfName
);
16121 require_noerr( err
, exit
);
16123 if( !inTimes
->queryTXTResultTime
)
16125 err
= _RegistrationTestAppendMissingResult( inMissingResults
, kRegistrationTestResultType_QueryTXT
,
16126 inIfIndex
, inIfName
);
16127 require_noerr( err
, exit
);
16136 _RegistrationTestAppendMissingResult(
16137 CFMutableArrayRef inMissingResults
,
16138 CFStringRef inType
,
16139 uint32_t inIfIndex
,
16140 const char * inIfName
)
16143 char ifName
[ IF_NAMESIZE
+ 1 ];
16145 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, inMissingResults
,
16147 "%kO=%O" // resultType
16148 "%kO=%lli" // ifIndex
16151 kRegistrationTestReportKey_ResultType
, inType
,
16152 kRegistrationTestReportKey_InterfaceIndex
, (int64_t) inIfIndex
,
16153 kRegistrationTestReportKey_InterfaceName
, inIfName
? inIfName
: if_indextoname( inIfIndex
, ifName
) );
16157 //===========================================================================================================================
16159 static void _RegistrationTestEnd( RegistrationTest
*inTest
)
16163 CFPropertyListRef plist
;
16164 char startTime
[ 32 ];
16165 char endTime
[ 32 ];
16167 now
= NanoTimeGetCurrent();
16168 _NanoTime64ToTimestamp( inTest
->startTime
, startTime
, sizeof( startTime
) );
16169 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
16171 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
16173 "%kO=%s" // startTime
16174 "%kO=%s" // endTime
16175 "%kO=%s" // computerName
16176 "%kO=%s" // localHostName
16177 "%kO=%O" // subtests
16180 kRegistrationTestReportKey_StartTime
, startTime
,
16181 kRegistrationTestReportKey_EndTime
, endTime
,
16182 kRegistrationTestReportKey_ComputerName
, inTest
->computerName
,
16183 kRegistrationTestReportKey_LocalHostName
, inTest
->localHostName
,
16184 kRegistrationTestReportKey_Subtests
, inTest
->subtestReports
,
16185 kRegistrationTestReportKey_Pass
, inTest
->failed
? false : true );
16186 require_noerr( err
, exit
);
16188 err
= OutputPropertyList( plist
, inTest
->outputFormat
, inTest
->outputFilePath
);
16189 CFRelease( plist
);
16190 require_noerr( err
, exit
);
16193 _RegistrationTestExit( inTest
, err
);
16196 //===========================================================================================================================
16198 static void _RegistrationTestExit( RegistrationTest
*inTest
, OSStatus inError
)
16204 FPrintF( stderr
, "error: %#m\n", inError
);
16209 exitCode
= inTest
->failed
? 2 : 0;
16211 _RegistrationTestForget( &inTest
);
16215 //===========================================================================================================================
16217 static OSStatus
_RegistrationSubtestCreate( RegistrationSubtest
**outSubtest
)
16220 RegistrationSubtest
* obj
;
16222 obj
= (RegistrationSubtest
*) calloc( 1, sizeof( *obj
) );
16223 require_action( obj
, exit
, err
= kNoMemoryErr
);
16225 obj
->unexpected
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
16226 require_action( obj
->unexpected
, exit
, err
= kNoMemoryErr
);
16228 #if( TARGET_OS_WATCH )
16229 obj
->ignored
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
16230 require_action( obj
->ignored
, exit
, err
= kNoMemoryErr
);
16238 if( obj
) _RegistrationSubtestFree( obj
);
16242 //===========================================================================================================================
16244 static void _RegistrationSubtestFree( RegistrationSubtest
*inSubtest
)
16246 check( !inSubtest
->registration
);
16247 check( !inSubtest
->browse
);
16248 check( !inSubtest
->querySRV
);
16249 check( !inSubtest
->queryTXT
);
16250 check( !inSubtest
->connection
);
16251 ForgetMem( &inSubtest
->serviceNameCustom
);
16252 ForgetMem( &inSubtest
->serviceType
);
16253 ForgetMem( &inSubtest
->serviceFQDN
);
16254 ForgetMem( &inSubtest
->txtPtr
);
16255 ForgetCF( &inSubtest
->unexpected
);
16256 #if( TARGET_OS_WATCH )
16257 ForgetCF( &inSubtest
->ignored
);
16259 _MDNSInterfaceListForget( (MDNSInterfaceItem
**) &inSubtest
->ifList
);
16260 ForgetMem( &inSubtest
->description
);
16264 //===========================================================================================================================
16266 static void _RegistrationSubtestStop( RegistrationSubtest
*inSubtest
)
16268 DNSServiceForget( &inSubtest
->registration
);
16269 DNSServiceForget( &inSubtest
->browse
);
16270 DNSServiceForget( &inSubtest
->querySRV
);
16271 DNSServiceForget( &inSubtest
->queryTXT
);
16272 DNSServiceForget( &inSubtest
->connection
);
16275 //===========================================================================================================================
16277 static OSStatus
_RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL
, RegistrationInterfaceItem
**outList
)
16280 RegistrationInterfaceItem
* list
;
16281 const MDNSInterfaceSubset subset
= inIncludeAWDL
? kMDNSInterfaceSubset_All
: kMDNSInterfaceSubset_NonAWDL
;
16283 err
= _MDNSInterfaceListCreate( subset
, sizeof( *list
), (MDNSInterfaceItem
**) &list
);
16284 require_noerr( err
, exit
);
16292 //===========================================================================================================================
16295 _RegistrationTestCreateRandomTXTRecord(
16298 uint8_t ** outTXTPtr
,
16299 size_t * outTXTLen
)
16303 const uint8_t * txtEnd
;
16304 uint8_t * txtPtr
= NULL
;
16307 require_action_quiet( inMinLen
<= inMaxLen
, exit
, err
= kSizeErr
);
16309 txtLen
= RandomRange( inMinLen
, inMaxLen
);
16310 txtPtr
= (uint8_t *) malloc( txtLen
+ 1 );
16311 require_action( txtPtr
, exit
, err
= kNoMemoryErr
);
16313 _RandomStringExact( kAlphaNumericCharSet
, sizeof_string( kAlphaNumericCharSet
), txtLen
, (char *)txtPtr
);
16316 txtEnd
= &txtPtr
[ txtLen
];
16317 while( ptr
< txtEnd
)
16319 size_t maxLen
, len
;
16321 maxLen
= ( (size_t)( txtEnd
- ptr
) ) - 1;
16322 len
= RandomRange( 1, 255 );
16323 if( len
> maxLen
) len
= maxLen
;
16325 *ptr
= (uint8_t) len
;
16326 ptr
+= ( 1 + len
);
16328 check( ptr
== txtEnd
);
16332 *outTXTPtr
= txtPtr
;
16335 if( outTXTLen
) *outTXTLen
= txtLen
;
16339 FreeNullSafe( txtPtr
);
16343 //===========================================================================================================================
16345 static void DNSSD_API
16346 _RegistrationSubtestRegisterCallback(
16347 DNSServiceRef inSDRef
,
16348 DNSServiceFlags inFlags
,
16349 DNSServiceErrorType inError
,
16350 const char * inServiceName
,
16351 const char * inServiceType
,
16352 const char * inDomain
,
16356 const NanoTime64 now
= NanoTimeGetCurrent();
16357 RegistrationSubtest
* const subtest
= (RegistrationSubtest
*) inContext
;
16361 if( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
&&
16362 ( strcasecmp( inServiceName
, subtest
->serviceName
) == 0 ) &&
16363 _RegistrationSubtestValidServiceType( subtest
, inServiceType
) &&
16364 ( strcasecmp( inDomain
, "local." ) == 0 ) )
16366 if( !subtest
->registered
)
16368 DNSServiceRef sdRef
;
16369 const DNSServiceFlags flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsIncludeAWDL
;
16371 subtest
->registered
= true;
16373 // Create shared connection.
16375 check( !subtest
->connection
);
16376 err
= DNSServiceCreateConnection( &subtest
->connection
);
16377 require_noerr( err
, exit
);
16379 err
= DNSServiceSetDispatchQueue( subtest
->connection
, dispatch_get_main_queue() );
16380 require_noerr( err
, exit
);
16384 check( !subtest
->browse
);
16385 sdRef
= subtest
->connection
;
16386 err
= DNSServiceBrowse( &sdRef
, flags
,
16387 subtest
->useLODiscovery
? kDNSServiceInterfaceIndexLocalOnly
: kDNSServiceInterfaceIndexAny
,
16388 subtest
->serviceType
, "local.", _RegistrationSubtestBrowseCallback
, subtest
);
16389 require_noerr( err
, exit
);
16391 subtest
->browse
= sdRef
;
16396 char timestamp
[ 32 ];
16398 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, subtest
->unexpected
,
16400 "%kO=%O" // resultType
16401 "%kO=%s" // timestamp
16402 "%kO=%lli" // flags
16403 "%kO=%lli" // error
16404 "%kO=%s" // serviceName
16405 "%kO=%s" // serviceType
16408 kRegistrationTestReportKey_ResultType
, kRegistrationTestResultType_Registration
,
16409 kRegistrationTestReportKey_Timestamp
, _NanoTime64ToTimestamp( now
, timestamp
, sizeof( timestamp
) ),
16410 kRegistrationTestReportKey_Flags
, (int64_t) inFlags
,
16411 kRegistrationTestReportKey_Error
, (int64_t) inError
,
16412 kRegistrationTestReportKey_ServiceName
, inServiceName
,
16413 kRegistrationTestReportKey_ServiceType
, inServiceType
,
16414 kRegistrationTestReportKey_Domain
, inDomain
);
16415 require_noerr( err
, exit
);
16420 if( err
) _RegistrationTestExit( subtest
->test
, err
);
16423 //===========================================================================================================================
16425 static void DNSSD_API
16426 _RegistrationSubtestBrowseCallback(
16427 DNSServiceRef inSDRef
,
16428 DNSServiceFlags inFlags
,
16429 uint32_t inIfIndex
,
16430 DNSServiceErrorType inError
,
16431 const char * inServiceName
,
16432 const char * inServiceType
,
16433 const char * inDomain
,
16438 RegistrationSubtest
* const subtest
= (RegistrationSubtest
*) inContext
;
16439 Boolean serviceIsCorrect
, resultIsExpected
;
16443 now
= NanoTimeGetCurrent();
16444 if( !inError
&& ( strcasecmp( inServiceName
, subtest
->serviceName
) == 0 ) &&
16445 _RegistrationSubtestValidServiceType( subtest
, inServiceType
) && ( strcasecmp( inDomain
, "local." ) == 0 ) )
16447 serviceIsCorrect
= true;
16451 serviceIsCorrect
= false;
16454 resultIsExpected
= false;
16455 if( serviceIsCorrect
&& ( inFlags
& kDNSServiceFlagsAdd
) )
16457 RegistrationResultTimes
* times
;
16459 times
= _RegistrationSubtestGetInterfaceResultTimes( subtest
, inIfIndex
, NULL
);
16462 DNSServiceRef sdRef
;
16463 const DNSServiceFlags flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsIncludeAWDL
;
16466 resultIsExpected
= true;
16467 if( !times
->browseResultTime
) times
->browseResultTime
= now
;
16469 ifIndex
= subtest
->useLODiscovery
? kDNSServiceInterfaceIndexLocalOnly
: kDNSServiceInterfaceIndexAny
;
16470 if( !subtest
->querySRV
)
16472 // Start SRV record query.
16474 sdRef
= subtest
->connection
;
16475 err
= DNSServiceQueryRecord( &sdRef
, flags
, ifIndex
, subtest
->serviceFQDN
, kDNSServiceType_SRV
,
16476 kDNSServiceClass_IN
, _RegistrationSubtestQueryCallback
, subtest
);
16477 require_noerr( err
, exit
);
16479 subtest
->querySRV
= sdRef
;
16481 if( !subtest
->queryTXT
)
16483 // Start TXT record query.
16485 sdRef
= subtest
->connection
;
16486 err
= DNSServiceQueryRecord( &sdRef
, flags
, ifIndex
, subtest
->serviceFQDN
, kDNSServiceType_TXT
,
16487 kDNSServiceClass_IN
, _RegistrationSubtestQueryCallback
, subtest
);
16488 require_noerr( err
, exit
);
16490 subtest
->queryTXT
= sdRef
;
16495 if( !resultIsExpected
)
16497 CFMutableArrayRef resultArray
;
16498 char timestamp
[ 32 ];
16499 const char * ifNamePtr
;
16500 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
16502 ifNamePtr
= if_indextoname( inIfIndex
, ifNameBuf
);
16503 resultArray
= subtest
->unexpected
;
16504 #if( TARGET_OS_WATCH )
16505 if( subtest
->test
->forBATS
&& ( subtest
->ifIndex
== kDNSServiceInterfaceIndexAny
) &&
16506 ifNamePtr
&& _RegistrationTestInterfaceIsWiFi( ifNamePtr
) && serviceIsCorrect
)
16508 resultArray
= subtest
->ignored
;
16511 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, resultArray
,
16513 "%kO=%O" // resultType
16514 "%kO=%s" // timestamp
16515 "%kO=%lli" // flags
16516 "%kO=%lli" // ifIndex
16518 "%kO=%lli" // error
16519 "%kO=%s" // serviceName
16520 "%kO=%s" // serviceType
16523 kRegistrationTestReportKey_ResultType
, kRegistrationTestResultType_Browse
,
16524 kRegistrationTestReportKey_Timestamp
, _NanoTime64ToTimestamp( now
, timestamp
, sizeof( timestamp
) ),
16525 kRegistrationTestReportKey_Flags
, (int64_t) inFlags
,
16526 kRegistrationTestReportKey_InterfaceIndex
, (int64_t) inIfIndex
,
16527 kRegistrationTestReportKey_InterfaceName
, ifNamePtr
,
16528 kRegistrationTestReportKey_Error
, (int64_t) inError
,
16529 kRegistrationTestReportKey_ServiceName
, inServiceName
,
16530 kRegistrationTestReportKey_ServiceType
, inServiceType
,
16531 kRegistrationTestReportKey_Domain
, inDomain
);
16532 require_noerr( err
, exit
);
16537 if( err
) _RegistrationTestExit( subtest
->test
, err
);
16540 //===========================================================================================================================
16543 _RegistrationSubtestIsSRVRecordDataValid(
16544 RegistrationSubtest
* inSubtest
,
16545 const uint8_t * inRDataPtr
,
16547 Boolean inExpectRandHostname
);
16549 static void DNSSD_API
16550 _RegistrationSubtestQueryCallback(
16551 DNSServiceRef inSDRef
,
16552 DNSServiceFlags inFlags
,
16553 uint32_t inIfIndex
,
16554 DNSServiceErrorType inError
,
16555 const char * inName
,
16558 uint16_t inRDataLen
,
16559 const void * inRDataPtr
,
16565 RegistrationSubtest
* const subtest
= (RegistrationSubtest
*) inContext
;
16566 Boolean resultIsExpected
;
16571 now
= NanoTimeGetCurrent();
16572 resultIsExpected
= false;
16573 if( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
&& ( strcasecmp( inName
, subtest
->serviceFQDN
) == 0 ) &&
16574 ( inClass
== kDNSServiceClass_IN
) )
16576 RegistrationResultTimes
* times
;
16579 times
= _RegistrationSubtestGetInterfaceResultTimes( subtest
, inIfIndex
, &isAWDL
);
16582 if( inType
== kDNSServiceType_SRV
)
16584 Boolean expectRandHostname
;
16586 if( isAWDL
|| ( ( subtest
->ifIndex
== kDNSServiceInterfaceIndexAny
) && subtest
->includeAWDL
) )
16588 expectRandHostname
= true;
16592 expectRandHostname
= false;
16594 if( _RegistrationSubtestIsSRVRecordDataValid( subtest
, inRDataPtr
, inRDataLen
, expectRandHostname
) )
16596 resultIsExpected
= true;
16597 if( !times
->querySRVResultTime
) times
->querySRVResultTime
= now
;
16600 else if( inType
== kDNSServiceType_TXT
)
16602 if( MemEqual( inRDataPtr
, inRDataLen
, subtest
->txtPtr
, subtest
->txtLen
) )
16604 resultIsExpected
= true;
16605 if( !times
->queryTXTResultTime
) times
->queryTXTResultTime
= now
;
16611 if( !resultIsExpected
)
16613 CFMutableArrayRef resultArray
;
16614 CFMutableDictionaryRef resultDict
;
16615 CFStringRef rdataKey
;
16617 const char * ifNamePtr
;
16618 char timestamp
[ 32 ];
16619 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
16621 ifNamePtr
= if_indextoname( inIfIndex
, ifNameBuf
);
16622 resultArray
= subtest
->unexpected
;
16623 #if( TARGET_OS_WATCH )
16624 if( subtest
->test
->forBATS
&& ( subtest
->ifIndex
== kDNSServiceInterfaceIndexAny
) &&
16625 ifNamePtr
&& _RegistrationTestInterfaceIsWiFi( ifNamePtr
) && !inError
&&
16626 ( strcasecmp( inName
, subtest
->serviceFQDN
) == 0 ) )
16628 if( inType
== kDNSServiceType_SRV
)
16630 const Boolean expectRandHostname
= subtest
->includeAWDL
? true : false;
16632 if( _RegistrationSubtestIsSRVRecordDataValid( subtest
, inRDataPtr
, inRDataLen
, expectRandHostname
) )
16634 resultArray
= subtest
->ignored
;
16637 else if( inType
== kDNSServiceType_TXT
)
16639 if( MemEqual( inRDataPtr
, inRDataLen
, subtest
->txtPtr
, subtest
->txtLen
) )
16641 resultArray
= subtest
->ignored
;
16646 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &resultDict
,
16648 "%kO=%O" // resultType
16649 "%kO=%s" // timestamp
16650 "%kO=%lli" // flags
16651 "%kO=%lli" // ifIndex
16653 "%kO=%lli" // error
16654 "%kO=%s" // serviceFQDN
16655 "%kO=%lli" // recordType
16656 "%kO=%lli" // class
16658 kRegistrationTestReportKey_ResultType
, kRegistrationTestResultType_Query
,
16659 kRegistrationTestReportKey_Timestamp
, _NanoTime64ToTimestamp( now
, timestamp
, sizeof( timestamp
) ),
16660 kRegistrationTestReportKey_Flags
, (int64_t) inFlags
,
16661 kRegistrationTestReportKey_InterfaceIndex
, (int64_t) inIfIndex
,
16662 kRegistrationTestReportKey_InterfaceName
, ifNamePtr
,
16663 kRegistrationTestReportKey_Error
, (int64_t) inError
,
16664 kRegistrationTestReportKey_ServiceFQDN
, inName
,
16665 kRegistrationTestReportKey_RecordType
, (int64_t) inType
,
16666 kRegistrationTestReportKey_RecordClass
, (int64_t) inClass
);
16667 require_noerr( err
, exit
);
16670 DNSRecordDataToString( inRDataPtr
, inRDataLen
, inType
, NULL
, 0, &rdataStr
);
16673 rdataKey
= kRegistrationTestReportKey_RDataFormatted
;
16677 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, inRDataLen
);
16678 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
16680 rdataKey
= kRegistrationTestReportKey_RDataHexString
;
16682 err
= CFDictionarySetCString( resultDict
, rdataKey
, rdataStr
, kSizeCString
);
16683 ForgetMem( &rdataStr
);
16684 if( err
) CFRelease( resultDict
);
16685 require_noerr( err
, exit
);
16687 CFArrayAppendValue( resultArray
, resultDict
);
16688 CFRelease( resultDict
);
16693 if( err
) _RegistrationTestExit( subtest
->test
, err
);
16697 _RegistrationSubtestIsSRVRecordDataValid(
16698 RegistrationSubtest
* inSubtest
,
16699 const uint8_t * inRDataPtr
,
16701 Boolean inExpectRandHostname
)
16703 const dns_fixed_fields_srv
* fields
;
16704 const uint8_t * const end
= &inRDataPtr
[ inRDataLen
];
16705 const uint8_t * label
;
16711 require_quiet( inRDataLen
>= sizeof( dns_fixed_fields_srv
), exit
);
16713 fields
= (const dns_fixed_fields_srv
*) inRDataPtr
;
16714 port
= dns_fixed_fields_srv_get_port( fields
);
16715 require_quiet( port
== inSubtest
->port
, exit
);
16717 // First target label should be a UUID string for the AWDL interface.
16719 label
= (const uint8_t *) &fields
[ 1 ];
16720 require_quiet( ( end
- label
) >= 1, exit
);
16723 require_quiet( ( (size_t)( end
- label
) ) >= ( 1 + len
), exit
);
16725 if( inExpectRandHostname
)
16727 if( StringToUUID( (const char *) &label
[ 1 ], len
, false, NULL
) != kNoErr
) goto exit
;
16731 if( strnicmpx( &label
[ 1 ], len
, inSubtest
->test
->localHostName
) != 0 ) goto exit
;
16734 // Second target label should be "local".
16736 label
= &label
[ 1 + len
];
16737 require_quiet( ( end
- label
) >= 1, exit
);
16740 require_quiet( ( (size_t)( end
- label
) ) >= ( 1 + len
), exit
);
16742 if( ( len
!= kLocalLabel
[ 0 ] ) || ( _memicmp( &label
[ 1 ], &kLocalLabel
[ 1 ], kLocalLabel
[ 0 ] ) != 0 ) ) goto exit
;
16744 // Third target label should be the root label.
16746 label
= &label
[ 1 + len
];
16747 require_quiet( ( end
- label
) >= 1, exit
);
16750 if( len
!= 0 ) goto exit
;
16758 //===========================================================================================================================
16760 static Boolean
_RegistrationSubtestValidServiceType( const RegistrationSubtest
*inSubtest
, const char *inServiceType
)
16762 if( stricmp_prefix( inServiceType
, inSubtest
->serviceType
) == 0 )
16764 const char * const ptr
= &inServiceType
[ inSubtest
->serviceTypeLen
];
16766 if( ( ptr
[ 0 ] == '\0' ) || ( ( ptr
[ 0 ] == '.' ) && ( ptr
[ 1 ] == '\0' ) ) ) return( true );
16771 //===========================================================================================================================
16773 static RegistrationResultTimes
*
16774 _RegistrationSubtestGetInterfaceResultTimes(
16775 RegistrationSubtest
* inSubtest
,
16776 uint32_t inIfIndex
,
16777 Boolean
* outIsAWDL
)
16779 if( inSubtest
->ifList
)
16781 RegistrationInterfaceItem
* item
;
16783 for( item
= inSubtest
->ifList
; item
; item
= (RegistrationInterfaceItem
*) item
->base
.next
)
16785 if( inIfIndex
== item
->base
.ifIndex
)
16787 if( outIsAWDL
) *outIsAWDL
= item
->base
.isAWDL
? true : false;
16788 return( &item
->times
);
16794 if( inIfIndex
== inSubtest
->ifIndex
)
16796 if( outIsAWDL
) *outIsAWDL
= inSubtest
->ifIsAWDL
? true : false;
16797 return( &inSubtest
->ifTimes
);
16803 //===========================================================================================================================
16805 static void _RegistrationTestTimerHandler( void *inContext
)
16807 RegistrationTest
* const test
= (RegistrationTest
*) inContext
;
16809 dispatch_source_forget( &test
->timer
);
16810 _RegistrationTestProceed( test
);
16813 //===========================================================================================================================
16815 #if( TARGET_OS_WATCH )
16816 static Boolean
_RegistrationTestInterfaceIsWiFi( const char *inIfName
)
16818 NetTransportType type
= kNetTransportType_Undefined
;
16820 SocketGetInterfaceInfo( kInvalidSocketRef
, inIfName
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &type
);
16821 return( ( type
== kNetTransportType_WiFi
) ? true : false );
16825 //===========================================================================================================================
16827 //===========================================================================================================================
16829 #define kSSDPPort 1900
16833 HTTPHeader header
; // HTTP header object for sending and receiving.
16834 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
16835 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
16836 int receiveSecs
; // After send, the amount of time to spend receiving.
16837 uint32_t ifindex
; // Index of the interface over which to send the query.
16838 Boolean useIPv4
; // True if the query should be sent via IPv4 multicast.
16839 Boolean useIPv6
; // True if the query should be sent via IPv6 multicast.
16841 } SSDPDiscoverContext
;
16843 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext
*inContext
);
16844 static void SSDPDiscoverReadHandler( void *inContext
);
16845 static int SocketToPortNumber( SocketRef inSock
);
16846 static OSStatus
WriteSSDPSearchRequest( HTTPHeader
*inHeader
, const void *inHostSA
, int inMX
, const char *inST
);
16848 static void SSDPDiscoverCmd( void )
16851 struct timeval now
;
16852 SSDPDiscoverContext
* context
;
16853 dispatch_source_t signalSource
= NULL
;
16854 SocketRef sockV4
= kInvalidSocketRef
;
16855 SocketRef sockV6
= kInvalidSocketRef
;
16859 // Set up SIGINT handler.
16861 signal( SIGINT
, SIG_IGN
);
16862 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
16863 require_noerr( err
, exit
);
16864 dispatch_resume( signalSource
);
16866 // Check command parameters.
16868 if( gSSDPDiscover_ReceiveSecs
< -1 )
16870 FPrintF( stdout
, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs
);
16877 context
= (SSDPDiscoverContext
*) calloc( 1, sizeof( *context
) );
16878 require_action( context
, exit
, err
= kNoMemoryErr
);
16880 context
->receiveSecs
= gSSDPDiscover_ReceiveSecs
;
16881 context
->useIPv4
= ( gSSDPDiscover_UseIPv4
|| !gSSDPDiscover_UseIPv6
) ? true : false;
16882 context
->useIPv6
= ( gSSDPDiscover_UseIPv6
|| !gSSDPDiscover_UseIPv4
) ? true : false;
16884 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifindex
);
16885 require_noerr_quiet( err
, exit
);
16887 // Set up IPv4 socket.
16889 if( context
->useIPv4
)
16892 err
= UDPClientSocketOpen( AF_INET
, NULL
, 0, -1, &port
, &sockV4
);
16893 require_noerr( err
, exit
);
16895 err
= SocketSetMulticastInterface( sockV4
, NULL
, context
->ifindex
);
16896 require_noerr( err
, exit
);
16898 err
= setsockopt( sockV4
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *) &(uint8_t){ 1 }, (socklen_t
) sizeof( uint8_t ) );
16899 err
= map_socket_noerr_errno( sockV4
, err
);
16900 require_noerr( err
, exit
);
16903 // Set up IPv6 socket.
16905 if( context
->useIPv6
)
16907 err
= UDPClientSocketOpen( AF_INET6
, NULL
, 0, -1, NULL
, &sockV6
);
16908 require_noerr( err
, exit
);
16910 err
= SocketSetMulticastInterface( sockV6
, NULL
, context
->ifindex
);
16911 require_noerr( err
, exit
);
16913 err
= setsockopt( sockV6
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *) &(int){ 1 }, (socklen_t
) sizeof( int ) );
16914 err
= map_socket_noerr_errno( sockV6
, err
);
16915 require_noerr( err
, exit
);
16920 SSDPDiscoverPrintPrologue( context
);
16922 // Send mDNS query message.
16925 if( IsValidSocket( sockV4
) )
16927 struct sockaddr_in mcastAddr4
;
16929 memset( &mcastAddr4
, 0, sizeof( mcastAddr4
) );
16930 SIN_LEN_SET( &mcastAddr4
);
16931 mcastAddr4
.sin_family
= AF_INET
;
16932 mcastAddr4
.sin_port
= htons( kSSDPPort
);
16933 mcastAddr4
.sin_addr
.s_addr
= htonl( 0xEFFFFFFA ); // 239.255.255.250
16935 err
= WriteSSDPSearchRequest( &context
->header
, &mcastAddr4
, gSSDPDiscover_MX
, gSSDPDiscover_ST
);
16936 require_noerr( err
, exit
);
16938 n
= sendto( sockV4
, context
->header
.buf
, context
->header
.len
, 0, (const struct sockaddr
*) &mcastAddr4
,
16939 (socklen_t
) sizeof( mcastAddr4
) );
16940 err
= map_socket_value_errno( sockV4
, n
== (ssize_t
) context
->header
.len
, n
);
16943 FPrintF( stderr
, "*** Failed to send query on IPv4 socket with error %#m\n", err
);
16944 ForgetSocket( &sockV4
);
16948 if( gSSDPDiscover_Verbose
)
16950 gettimeofday( &now
, NULL
);
16951 FPrintF( stdout
, "---\n" );
16952 FPrintF( stdout
, "Send time: %{du:time}\n", &now
);
16953 FPrintF( stdout
, "Source Port: %d\n", SocketToPortNumber( sockV4
) );
16954 FPrintF( stdout
, "Destination: %##a\n", &mcastAddr4
);
16955 FPrintF( stdout
, "Message size: %zu\n", context
->header
.len
);
16956 FPrintF( stdout
, "HTTP header:\n%1{text}", context
->header
.buf
, context
->header
.len
);
16962 if( IsValidSocket( sockV6
) )
16964 struct sockaddr_in6 mcastAddr6
;
16966 memset( &mcastAddr6
, 0, sizeof( mcastAddr6
) );
16967 SIN6_LEN_SET( &mcastAddr6
);
16968 mcastAddr6
.sin6_family
= AF_INET6
;
16969 mcastAddr6
.sin6_port
= htons( kSSDPPort
);
16970 mcastAddr6
.sin6_addr
.s6_addr
[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C
16971 mcastAddr6
.sin6_addr
.s6_addr
[ 1 ] = 0x02;
16972 mcastAddr6
.sin6_addr
.s6_addr
[ 15 ] = 0x0C;
16974 err
= WriteSSDPSearchRequest( &context
->header
, &mcastAddr6
, gSSDPDiscover_MX
, gSSDPDiscover_ST
);
16975 require_noerr( err
, exit
);
16977 n
= sendto( sockV6
, context
->header
.buf
, context
->header
.len
, 0, (const struct sockaddr
*) &mcastAddr6
,
16978 (socklen_t
) sizeof( mcastAddr6
) );
16979 err
= map_socket_value_errno( sockV6
, n
== (ssize_t
) context
->header
.len
, n
);
16982 FPrintF( stderr
, "*** Failed to send query on IPv6 socket with error %#m\n", err
);
16983 ForgetSocket( &sockV6
);
16987 if( gSSDPDiscover_Verbose
)
16989 gettimeofday( &now
, NULL
);
16990 FPrintF( stdout
, "---\n" );
16991 FPrintF( stdout
, "Send time: %{du:time}\n", &now
);
16992 FPrintF( stdout
, "Source Port: %d\n", SocketToPortNumber( sockV6
) );
16993 FPrintF( stdout
, "Destination: %##a\n", &mcastAddr6
);
16994 FPrintF( stdout
, "Message size: %zu\n", context
->header
.len
);
16995 FPrintF( stdout
, "HTTP header:\n%1{text}", context
->header
.buf
, context
->header
.len
);
17000 require_action_quiet( sendCount
> 0, exit
, err
= kUnexpectedErr
);
17002 // If there's no wait period after the send, then exit.
17004 if( context
->receiveSecs
== 0 ) goto exit
;
17006 // Create dispatch read sources for socket(s).
17008 if( IsValidSocket( sockV4
) )
17010 SocketContext
* sockCtx
;
17012 err
= SocketContextCreate( sockV4
, context
, &sockCtx
);
17013 require_noerr( err
, exit
);
17014 sockV4
= kInvalidSocketRef
;
17016 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, SSDPDiscoverReadHandler
, SocketContextCancelHandler
, sockCtx
,
17017 &context
->readSourceV4
);
17018 if( err
) ForgetSocketContext( &sockCtx
);
17019 require_noerr( err
, exit
);
17021 dispatch_resume( context
->readSourceV4
);
17024 if( IsValidSocket( sockV6
) )
17026 SocketContext
* sockCtx
;
17028 err
= SocketContextCreate( sockV6
, context
, &sockCtx
);
17029 require_noerr( err
, exit
);
17030 sockV6
= kInvalidSocketRef
;
17032 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, SSDPDiscoverReadHandler
, SocketContextCancelHandler
, sockCtx
,
17033 &context
->readSourceV6
);
17034 if( err
) ForgetSocketContext( &sockCtx
);
17035 require_noerr( err
, exit
);
17037 dispatch_resume( context
->readSourceV6
);
17040 if( context
->receiveSecs
> 0 )
17042 dispatch_after_f( dispatch_time_seconds( context
->receiveSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
17048 ForgetSocket( &sockV4
);
17049 ForgetSocket( &sockV6
);
17050 dispatch_source_forget( &signalSource
);
17051 exit( err
? 1 : 0 );
17054 static int SocketToPortNumber( SocketRef inSock
)
17060 len
= (socklen_t
) sizeof( sip
);
17061 err
= getsockname( inSock
, &sip
.sa
, &len
);
17062 err
= map_socket_noerr_errno( inSock
, err
);
17063 check_noerr( err
);
17064 return( err
? -1 : SockAddrGetPort( &sip
) );
17067 static OSStatus
WriteSSDPSearchRequest( HTTPHeader
*inHeader
, const void *inHostSA
, int inMX
, const char *inST
)
17071 err
= HTTPHeader_InitRequest( inHeader
, "M-SEARCH", "*", "HTTP/1.1" );
17072 require_noerr( err
, exit
);
17074 err
= HTTPHeader_SetField( inHeader
, "Host", "%##a", inHostSA
);
17075 require_noerr( err
, exit
);
17077 err
= HTTPHeader_SetField( inHeader
, "ST", "%s", inST
? inST
: "ssdp:all" );
17078 require_noerr( err
, exit
);
17080 err
= HTTPHeader_SetField( inHeader
, "Man", "\"ssdp:discover\"" );
17081 require_noerr( err
, exit
);
17083 err
= HTTPHeader_SetField( inHeader
, "MX", "%d", inMX
);
17084 require_noerr( err
, exit
);
17086 err
= HTTPHeader_Commit( inHeader
);
17087 require_noerr( err
, exit
);
17093 //===========================================================================================================================
17094 // SSDPDiscoverPrintPrologue
17095 //===========================================================================================================================
17097 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext
*inContext
)
17099 const int receiveSecs
= inContext
->receiveSecs
;
17100 const char * ifName
;
17101 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
17102 NetTransportType ifType
;
17104 ifName
= if_indextoname( inContext
->ifindex
, ifNameBuf
);
17106 ifType
= kNetTransportType_Undefined
;
17107 if( ifName
) SocketGetInterfaceInfo( kInvalidSocketRef
, ifName
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &ifType
);
17109 FPrintF( stdout
, "Interface: %s/%d/%s\n",
17110 ifName
? ifName
: "?", inContext
->ifindex
, NetTransportTypeToString( ifType
) );
17111 FPrintF( stdout
, "IP protocols: %?s%?s%?s\n",
17112 inContext
->useIPv4
, "IPv4", ( inContext
->useIPv4
&& inContext
->useIPv6
), ", ", inContext
->useIPv6
, "IPv6" );
17113 FPrintF( stdout
, "Receive duration: " );
17114 if( receiveSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", receiveSecs
, receiveSecs
!= 1, 's' );
17115 else FPrintF( stdout
, "∞\n" );
17116 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
17119 //===========================================================================================================================
17120 // SSDPDiscoverReadHandler
17121 //===========================================================================================================================
17123 static Boolean
_HTTPHeader_Validate( HTTPHeader
*inHeader
);
17125 static void SSDPDiscoverReadHandler( void *inContext
)
17128 struct timeval now
;
17129 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
17130 SSDPDiscoverContext
* const context
= (SSDPDiscoverContext
*) sockCtx
->userContext
;
17131 HTTPHeader
* const header
= &context
->header
;
17132 sockaddr_ip fromAddr
;
17135 gettimeofday( &now
, NULL
);
17137 err
= SocketRecvFrom( sockCtx
->sock
, header
->buf
, sizeof( header
->buf
), &msgLen
, &fromAddr
, sizeof( fromAddr
),
17138 NULL
, NULL
, NULL
, NULL
);
17139 require_noerr( err
, exit
);
17141 FPrintF( stdout
, "---\n" );
17142 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
17143 FPrintF( stdout
, "Source: %##a\n", &fromAddr
);
17144 FPrintF( stdout
, "Message size: %zu\n", msgLen
);
17145 header
->len
= msgLen
;
17146 if( _HTTPHeader_Validate( header
) )
17148 FPrintF( stdout
, "HTTP header:\n%1{text}", header
->buf
, header
->len
);
17149 if( header
->extraDataLen
> 0 )
17151 FPrintF( stdout
, "HTTP body: %1.1H", header
->extraDataPtr
, (int) header
->extraDataLen
, INT_MAX
);
17156 FPrintF( stdout
, "Invalid HTTP message:\n%1.1H", header
->buf
, (int) msgLen
, INT_MAX
);
17161 if( err
) exit( 1 );
17164 //===========================================================================================================================
17165 // _HTTPHeader_Validate
17167 // Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid.
17168 // This assumes the "buf" and "len" fields are set. The other fields are set by this function.
17170 // Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework.
17171 //===========================================================================================================================
17173 static Boolean
_HTTPHeader_Validate( HTTPHeader
*inHeader
)
17178 // Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
17180 require( inHeader
->len
< sizeof( inHeader
->buf
), exit
);
17181 src
= inHeader
->buf
;
17182 end
= src
+ inHeader
->len
;
17183 if( ( ( end
- src
) >= 4 ) && ( src
[ 0 ] == '$' ) )
17189 // Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted.
17190 // $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over.
17194 while( ( src
< end
) && ( src
[ 0 ] != '\n' ) ) ++src
;
17195 if( src
>= end
) goto exit
;
17197 if( ( ( end
- src
) >= 2 ) && ( src
[ 0 ] == '\r' ) && ( src
[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF
17202 else if( ( ( end
- src
) >= 1 ) && ( src
[ 0 ] == '\n' ) ) // LFLF
17209 inHeader
->extraDataPtr
= src
;
17210 inHeader
->extraDataLen
= (size_t)( end
- src
);
17211 inHeader
->len
= (size_t)( src
- inHeader
->buf
);
17218 #if( TARGET_OS_DARWIN )
17219 //===========================================================================================================================
17221 //===========================================================================================================================
17223 // res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h).
17225 SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv
);
17226 SOFT_LINK_FUNCTION_EX( resolv
, res_9_query
,
17228 ( const char *dname
, int class, int type
, u_char
*answer
, int anslen
),
17229 ( dname
, class, type
, answer
, anslen
) );
17231 // res_query() from libinfo
17233 SOFT_LINK_LIBRARY_EX( "/usr/lib", info
);
17234 SOFT_LINK_FUNCTION_EX( info
, res_query
,
17236 ( const char *dname
, int class, int type
, u_char
*answer
, int anslen
),
17237 ( dname
, class, type
, answer
, anslen
) );
17239 typedef int ( *res_query_f
)( const char *dname
, int class, int type
, u_char
*answer
, int anslen
);
17241 static void ResQueryCmd( void )
17244 res_query_f res_query_ptr
;
17246 uint16_t type
, class;
17247 uint8_t answer
[ 1024 ];
17249 // Get pointer to one of the res_query() functions.
17251 if( gResQuery_UseLibInfo
)
17253 if( !SOFT_LINK_HAS_FUNCTION( info
, res_query
) )
17255 FPrintF( stderr
, "Failed to soft link res_query from libinfo.\n" );
17256 err
= kNotFoundErr
;
17259 res_query_ptr
= soft_res_query
;
17263 if( !SOFT_LINK_HAS_FUNCTION( resolv
, res_9_query
) )
17265 FPrintF( stderr
, "Failed to soft link res_query from libresolv.\n" );
17266 err
= kNotFoundErr
;
17269 res_query_ptr
= soft_res_9_query
;
17272 // Get record type.
17274 err
= RecordTypeFromArgString( gResQuery_Type
, &type
);
17275 require_noerr( err
, exit
);
17277 // Get record class.
17279 if( gResQuery_Class
)
17281 err
= RecordClassFromArgString( gResQuery_Class
, &class );
17282 require_noerr( err
, exit
);
17286 class = kDNSServiceClass_IN
;
17291 FPrintF( stdout
, "Name: %s\n", gResQuery_Name
);
17292 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
17293 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
17294 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
17295 FPrintF( stdout
, "---\n" );
17297 // Call res_query().
17299 n
= res_query_ptr( gResQuery_Name
, class, type
, (u_char
*) answer
, (int) sizeof( answer
) );
17302 FPrintF( stderr
, "res_query() failed with error: %d (%s).\n", h_errno
, hstrerror( h_errno
) );
17309 FPrintF( stdout
, "Message size: %d\n\n%{du:dnsmsg}", n
, answer
, (size_t) n
);
17312 if( err
) exit( 1 );
17315 //===========================================================================================================================
17316 // ResolvDNSQueryCmd
17317 //===========================================================================================================================
17319 // 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
17320 // avoid including the header file.
17322 typedef void * dns_handle_t
;
17324 SOFT_LINK_FUNCTION_EX( resolv
, dns_open
, dns_handle_t
, ( const char *path
), ( path
) );
17325 SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv
, dns_free
, ( dns_handle_t
*dns
), ( dns
) );
17326 SOFT_LINK_FUNCTION_EX( resolv
, dns_query
,
17334 struct sockaddr
* from
,
17335 uint32_t * fromlen
),
17336 ( dns
, name
, dnsclass
, dnstype
, buf
, len
, from
, fromlen
) );
17338 static void ResolvDNSQueryCmd( void )
17342 dns_handle_t dns
= NULL
;
17343 uint16_t type
, class;
17346 uint8_t answer
[ 1024 ];
17348 // Make sure that the required symbols are available.
17350 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_open
) )
17352 FPrintF( stderr
, "Failed to soft link dns_open from libresolv.\n" );
17353 err
= kNotFoundErr
;
17357 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_free
) )
17359 FPrintF( stderr
, "Failed to soft link dns_free from libresolv.\n" );
17360 err
= kNotFoundErr
;
17364 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_query
) )
17366 FPrintF( stderr
, "Failed to soft link dns_query from libresolv.\n" );
17367 err
= kNotFoundErr
;
17371 // Get record type.
17373 err
= RecordTypeFromArgString( gResolvDNSQuery_Type
, &type
);
17374 require_noerr( err
, exit
);
17376 // Get record class.
17378 if( gResolvDNSQuery_Class
)
17380 err
= RecordClassFromArgString( gResolvDNSQuery_Class
, &class );
17381 require_noerr( err
, exit
);
17385 class = kDNSServiceClass_IN
;
17390 dns
= soft_dns_open( gResolvDNSQuery_Path
);
17393 FPrintF( stderr
, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path
);
17400 FPrintF( stdout
, "Name: %s\n", gResolvDNSQuery_Name
);
17401 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
17402 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
17403 FPrintF( stdout
, "Path: %s\n", gResolvDNSQuery_Path
? gResolvDNSQuery_Name
: "<NULL>" );
17404 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
17405 FPrintF( stdout
, "---\n" );
17407 // Call dns_query().
17409 memset( &from
, 0, sizeof( from
) );
17410 fromLen
= (uint32_t) sizeof( from
);
17411 n
= soft_dns_query( dns
, gResolvDNSQuery_Name
, class, type
, (char *) answer
, (uint32_t) sizeof( answer
), &from
.sa
,
17415 FPrintF( stderr
, "dns_query() failed with error: %d (%s).\n", h_errno
, hstrerror( h_errno
) );
17422 FPrintF( stdout
, "From: %##a\n", &from
);
17423 FPrintF( stdout
, "Message size: %d\n\n%{du:dnsmsg}", n
, answer
, (size_t) n
);
17426 if( dns
) soft_dns_free( dns
);
17427 if( err
) exit( 1 );
17430 //===========================================================================================================================
17432 //===========================================================================================================================
17435 _CFHostResolveCallback(
17437 CFHostInfoType inInfoType
,
17438 const CFStreamError
* inError
,
17441 static void CFHostCmd( void )
17446 CFHostRef host
= NULL
;
17447 CFHostClientContext context
;
17448 CFStreamError streamErr
;
17450 name
= CFStringCreateWithCString( kCFAllocatorDefault
, gCFHost_Name
, kCFStringEncodingUTF8
);
17451 require_action( name
, exit
, err
= kUnknownErr
);
17453 host
= CFHostCreateWithName( kCFAllocatorDefault
, name
);
17455 require_action( host
, exit
, err
= kUnknownErr
);
17457 memset( &context
, 0, sizeof( context
) );
17458 success
= CFHostSetClient( host
, _CFHostResolveCallback
, &context
);
17459 require_action( success
, exit
, err
= kUnknownErr
);
17461 CFHostScheduleWithRunLoop( host
, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode
);
17465 FPrintF( stdout
, "Hostname: %s\n", gCFHost_Name
);
17466 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
17467 FPrintF( stdout
, "---\n" );
17469 success
= CFHostStartInfoResolution( host
, kCFHostAddresses
, &streamErr
);
17470 require_action( success
, exit
, err
= kUnknownErr
);
17476 CFReleaseNullSafe( host
);
17477 if( err
) exit( 1 );
17480 static void _CFHostResolveCallback( CFHostRef inHost
, CFHostInfoType inInfoType
, const CFStreamError
*inError
, void *inInfo
)
17483 struct timeval now
;
17485 gettimeofday( &now
, NULL
);
17487 Unused( inInfoType
);
17490 if( inError
&& ( inError
->domain
!= 0 ) && ( inError
->error
) )
17492 err
= inError
->error
;
17493 if( inError
->domain
== kCFStreamErrorDomainNetDB
)
17495 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
17499 FPrintF( stderr
, "Error %#m\n", err
);
17504 CFArrayRef addresses
;
17506 CFDataRef addrData
;
17507 const struct sockaddr
* sockAddr
;
17508 Boolean wasResolved
= false;
17510 addresses
= CFHostGetAddressing( inHost
, &wasResolved
);
17511 check( wasResolved
);
17515 count
= CFArrayGetCount( addresses
);
17516 for( i
= 0; i
< count
; ++i
)
17518 addrData
= CFArrayGetCFDataAtIndex( addresses
, i
, &err
);
17519 require_noerr( err
, exit
);
17521 sockAddr
= (const struct sockaddr
*) CFDataGetBytePtr( addrData
);
17522 FPrintF( stdout
, "%##a\n", sockAddr
);
17528 FPrintF( stdout
, "---\n" );
17529 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
17531 if( gCFHost_WaitSecs
> 0 ) sleep( (unsigned int) gCFHost_WaitSecs
);
17534 exit( err
? 1 : 0 );
17537 //===========================================================================================================================
17540 // Note: Based on ajn's supplemental test tool.
17541 //===========================================================================================================================
17543 static void DNSConfigAddCmd( void )
17546 CFMutableDictionaryRef dict
= NULL
;
17547 CFMutableArrayRef array
= NULL
;
17549 SCDynamicStoreRef store
= NULL
;
17550 CFStringRef key
= NULL
;
17553 // Create dictionary.
17555 dict
= CFDictionaryCreateMutable( NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
17556 require_action( dict
, exit
, err
= kNoMemoryErr
);
17558 // Add DNS server IP addresses.
17560 array
= CFArrayCreateMutable( NULL
, (CFIndex
) gDNSConfigAdd_IPAddrCount
, &kCFTypeArrayCallBacks
);
17561 require_action( array
, exit
, err
= kNoMemoryErr
);
17563 for( i
= 0; i
< gDNSConfigAdd_IPAddrCount
; ++i
)
17565 CFStringRef addrStr
;
17567 addrStr
= CFStringCreateWithCString( NULL
, gDNSConfigAdd_IPAddrArray
[ i
], kCFStringEncodingUTF8
);
17568 require_action( addrStr
, exit
, err
= kUnknownErr
);
17570 CFArrayAppendValue( array
, addrStr
);
17571 CFRelease( addrStr
);
17574 CFDictionarySetValue( dict
, kSCPropNetDNSServerAddresses
, array
);
17575 ForgetCF( &array
);
17577 // Add domains, if any.
17579 array
= CFArrayCreateMutable( NULL
, (CFIndex
) Min( gDNSConfigAdd_DomainCount
, 1 ), &kCFTypeArrayCallBacks
);
17580 require_action( array
, exit
, err
= kNoMemoryErr
);
17582 if( gDNSConfigAdd_DomainCount
> 0 )
17584 for( i
= 0; i
< gDNSConfigAdd_DomainCount
; ++i
)
17586 CFStringRef domainStr
;
17588 domainStr
= CFStringCreateWithCString( NULL
, gDNSConfigAdd_DomainArray
[ i
], kCFStringEncodingUTF8
);
17589 require_action( domainStr
, exit
, err
= kUnknownErr
);
17591 CFArrayAppendValue( array
, domainStr
);
17592 CFRelease( domainStr
);
17597 // There are no domains, but the domain array needs to be non-empty, so add a zero-length string to the array.
17599 CFArrayAppendValue( array
, CFSTR( "" ) );
17602 CFDictionarySetValue( dict
, kSCPropNetDNSSupplementalMatchDomains
, array
);
17603 ForgetCF( &array
);
17605 // Add interface, if any.
17607 if( gDNSConfigAdd_Interface
)
17609 err
= CFDictionarySetCString( dict
, kSCPropInterfaceName
, gDNSConfigAdd_Interface
, kSizeCString
);
17610 require_noerr( err
, exit
);
17612 CFDictionarySetValue( dict
, kSCPropNetDNSConfirmedServiceID
, gDNSConfigAdd_ID
);
17615 // Set dictionary in dynamic store.
17617 store
= SCDynamicStoreCreate( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
17618 err
= map_scerror( store
);
17619 require_noerr( err
, exit
);
17621 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
, gDNSConfigAdd_ID
, kSCEntNetDNS
);
17622 require_action( key
, exit
, err
= kUnknownErr
);
17624 success
= SCDynamicStoreSetValue( store
, key
, dict
);
17625 require_action( success
, exit
, err
= kUnknownErr
);
17628 CFReleaseNullSafe( dict
);
17629 CFReleaseNullSafe( array
);
17630 CFReleaseNullSafe( store
);
17631 CFReleaseNullSafe( key
);
17632 gExitCode
= err
? 1 : 0;
17635 //===========================================================================================================================
17636 // DNSConfigRemoveCmd
17637 //===========================================================================================================================
17639 static void DNSConfigRemoveCmd( void )
17642 SCDynamicStoreRef store
= NULL
;
17643 CFStringRef key
= NULL
;
17646 store
= SCDynamicStoreCreate( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
17647 err
= map_scerror( store
);
17648 require_noerr( err
, exit
);
17650 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
, gDNSConfigRemove_ID
, kSCEntNetDNS
);
17651 require_action( key
, exit
, err
= kUnknownErr
);
17653 success
= SCDynamicStoreRemoveValue( store
, key
);
17654 require_action( success
, exit
, err
= kUnknownErr
);
17657 CFReleaseNullSafe( store
);
17658 CFReleaseNullSafe( key
);
17659 gExitCode
= err
? 1 : 0;
17662 //===========================================================================================================================
17664 //===========================================================================================================================
17666 static OSStatus
_XPCDictionaryCreateFromString( const char *inString
, xpc_object_t
*outDict
);
17668 static void XPCSendCmd( void )
17671 xpc_object_t msg
, reply
;
17673 err
= _XPCDictionaryCreateFromString( gXPCSend_MessageStr
, &msg
);
17674 require_noerr_quiet( err
, exit
);
17676 FPrintF( stdout
, "Service: %s\n", gXPCSend_ServiceName
);
17677 FPrintF( stdout
, "Message: %s\n", gXPCSend_MessageStr
);
17678 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
17679 FPrintF( stdout
, "---\n" );
17680 FPrintF( stdout
, "XPC Message:\n%{xpc}\n", msg
);
17682 err
= xpc_send_message_sync( gXPCSend_ServiceName
, 0, 0, msg
, &reply
);
17683 xpc_forget( &msg
);
17684 require_noerr_quiet( err
, exit
);
17686 FPrintF( stdout
, "XPC Reply:\n%{xpc}\n", reply
);
17687 FPrintF( stdout
, "---\n" );
17688 FPrintF( stdout
, "End time: %{du:time}\n", NULL
);
17689 xpc_forget( &reply
);
17692 if( err
) ErrQuit( 1, "error: %#m\n", err
);
17695 //===========================================================================================================================
17696 // _XPCDictionaryCreateFromString
17697 //===========================================================================================================================
17699 #define kXPCObjectPrefix_Bool "bool:"
17700 #define kXPCObjectPrefix_Data "data:"
17701 #define kXPCObjectPrefix_Int64 "int:"
17702 #define kXPCObjectPrefix_String "string:"
17703 #define kXPCObjectPrefix_UInt64 "uint:"
17704 #define kXPCObjectPrefix_UUID "uuid:"
17706 typedef struct XPCListItem XPCListItem
;
17709 XPCListItem
* next
;
17714 static OSStatus
_XPCListItemCreate( xpc_object_t inObject
, const char *inKey
, XPCListItem
**outItem
);
17715 static void _XPCListItemFree( XPCListItem
*inItem
);
17716 static void _XPCListFree( XPCListItem
*inList
);
17718 static OSStatus
_XPCObjectFromString( const char *inString
, xpc_object_t
*outObject
);
17720 static OSStatus
_XPCDictionaryCreateFromString( const char *inString
, xpc_object_t
*outDict
)
17723 xpc_object_t container
;
17724 const char * ptr
= inString
;
17725 const char * const end
= inString
+ strlen( inString
);
17726 XPCListItem
* list
= NULL
;
17728 container
= xpc_dictionary_create( NULL
, NULL
, 0 );
17729 require_action( container
, exit
, err
= kNoMemoryErr
);
17733 xpc_type_t containerType
;
17734 xpc_object_t value
;
17736 char keyStr
[ 256 ];
17737 char valStr
[ 256 ];
17739 // At this point, zero or more of the current container's elements have been parsed.
17740 // Skip the white space leading up to the container's next element, if any, or the container's end.
17742 while( isspace_safe( *ptr
) ) ++ptr
;
17744 // Check if we're done with the current container.
17747 if( c
== '\0' ) break;
17749 containerType
= xpc_get_type( container
);
17750 if( ( ( containerType
== XPC_TYPE_DICTIONARY
) && ( c
== '}' ) ) ||
17751 ( ( containerType
== XPC_TYPE_ARRAY
) && ( c
== ']' ) ) )
17753 XPCListItem
* item
;
17756 require_action_quiet( item
, exit
, err
= kMalformedErr
);
17760 // Add the current container to its parent container.
17764 xpc_dictionary_set_value( item
->obj
, item
->key
, container
);
17768 xpc_array_append_value( item
->obj
, container
);
17771 // Continue with the parent container.
17773 xpc_release( container
);
17774 container
= xpc_retain( item
->obj
);
17776 _XPCListItemFree( item
);
17780 // If the current container is a dictionary, parse the key string.
17782 if( containerType
== XPC_TYPE_DICTIONARY
)
17784 err
= _ParseEscapedString( ptr
, end
, "={}[]" kWhiteSpaceCharSet
, keyStr
, sizeof( keyStr
), NULL
, NULL
, &ptr
);
17785 require_noerr_quiet( err
, exit
);
17788 require_action_quiet( c
== '=', exit
, err
= kMalformedErr
);
17792 // Check if the value is a dictionary ({...}) or an array ([...]).
17795 if( ( c
== '{' ) || ( c
== '[' ) )
17797 XPCListItem
* item
;
17801 // Save the current container.
17803 err
= _XPCListItemCreate( container
, ( containerType
== XPC_TYPE_DICTIONARY
) ? keyStr
: NULL
, &item
);
17804 require_noerr( err
, exit
);
17810 // Create and continue with the child container.
17812 xpc_release( container
);
17815 container
= xpc_dictionary_create( NULL
, NULL
, 0 );
17816 require_action( container
, exit
, err
= kNoMemoryErr
);
17820 container
= xpc_array_create( NULL
, 0 );
17821 require_action( container
, exit
, err
= kNoMemoryErr
);
17826 // Parse the value string.
17828 err
= _ParseEscapedString( ptr
, end
, "{}[]" kWhiteSpaceCharSet
, valStr
, sizeof( valStr
), NULL
, NULL
, &ptr
);
17829 require_noerr_quiet( err
, exit
);
17831 err
= _XPCObjectFromString( valStr
, &value
);
17832 require_noerr_quiet( err
, exit
);
17834 if( containerType
== XPC_TYPE_DICTIONARY
)
17836 xpc_dictionary_set_value( container
, keyStr
, value
);
17840 xpc_array_append_value( container
, value
);
17842 xpc_forget( &value
);
17844 require_action_quiet( !list
, exit
, err
= kMalformedErr
);
17846 check( container
);
17847 check( xpc_get_type( container
) == XPC_TYPE_DICTIONARY
);
17849 *outDict
= container
;
17854 xpc_release_null_safe( container
);
17855 if( list
) _XPCListFree( list
);
17859 static OSStatus
_XPCObjectFromString( const char *inString
, xpc_object_t
*outObject
)
17862 xpc_object_t object
;
17868 else if( stricmp_prefix( inString
, kXPCObjectPrefix_Bool
) == 0 )
17870 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_Bool
);
17873 if( IsTrueString( str
, kSizeCString
) )
17877 else if( IsFalseString( str
, kSizeCString
) )
17887 object
= xpc_bool_create( value
);
17888 require_action( object
, exit
, err
= kNoMemoryErr
);
17893 else if( stricmp_prefix( inString
, kXPCObjectPrefix_Data
) == 0 )
17895 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_Data
);
17899 err
= HexToDataCopy( str
, kSizeCString
, kHexToData_DefaultFlags
, &dataPtr
, &dataLen
, NULL
);
17900 require_noerr( err
, exit
);
17902 object
= xpc_data_create( dataPtr
, dataLen
);
17904 require_action( object
, exit
, err
= kNoMemoryErr
);
17909 else if( stricmp_prefix( inString
, kXPCObjectPrefix_Int64
) == 0 )
17911 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_Int64
);
17914 i64
= _StringToInt64( str
, &err
);
17915 require_noerr_quiet( err
, exit
);
17917 object
= xpc_int64_create( i64
);
17918 require_action( object
, exit
, err
= kNoMemoryErr
);
17923 else if( stricmp_prefix( inString
, kXPCObjectPrefix_String
) == 0 )
17925 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_String
);
17927 object
= xpc_string_create( str
);
17928 require_action( object
, exit
, err
= kNoMemoryErr
);
17933 else if( stricmp_prefix( inString
, kXPCObjectPrefix_UInt64
) == 0 )
17935 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_UInt64
);
17938 u64
= _StringToUInt64( str
, &err
);
17939 require_noerr_quiet( err
, exit
);
17941 object
= xpc_uint64_create( u64
);
17942 require_action( object
, exit
, err
= kNoMemoryErr
);
17947 else if( stricmp_prefix( inString
, kXPCObjectPrefix_UUID
) == 0 )
17949 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_UUID
);
17952 err
= uuid_parse( str
, uuid
);
17953 require_noerr_action_quiet( err
, exit
, err
= kValueErr
);
17955 object
= xpc_uuid_create( uuid
);
17956 require_action( object
, exit
, err
= kNoMemoryErr
);
17959 // Unsupported prefix
17967 *outObject
= object
;
17974 static OSStatus
_XPCListItemCreate( xpc_object_t inObject
, const char *inKey
, XPCListItem
**outItem
)
17977 XPCListItem
* item
;
17979 item
= (XPCListItem
*) calloc( 1, sizeof( *item
) );
17980 require_action( item
, exit
, err
= kNoMemoryErr
);
17982 item
->obj
= xpc_retain( inObject
);
17983 if( ( xpc_get_type( item
->obj
) == XPC_TYPE_DICTIONARY
) && inKey
)
17985 item
->key
= strdup( inKey
);
17986 require_action( item
->key
, exit
, err
= kNoMemoryErr
);
17994 if( item
) _XPCListItemFree( item
);
17998 static void _XPCListItemFree( XPCListItem
*inItem
)
18000 xpc_forget( &inItem
->obj
);
18001 ForgetMem( &inItem
->key
);
18005 static void _XPCListFree( XPCListItem
*inList
)
18007 XPCListItem
* item
;
18009 while( ( item
= inList
) != NULL
)
18011 inList
= item
->next
;
18012 _XPCListItemFree( item
);
18015 #endif // TARGET_OS_DARWIN
18017 #if( MDNSRESPONDER_PROJECT )
18018 //===========================================================================================================================
18019 // InterfaceMonitorCmd
18020 //===========================================================================================================================
18022 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor
);
18023 static void _InterfaceMonitorSignalHandler( void *inContext
);
18025 static void InterfaceMonitorCmd( void )
18028 mdns_interface_monitor_t monitor
;
18029 dispatch_source_t signalSource
= NULL
;
18031 __block
int exitCode
;
18033 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
18034 require_noerr_quiet( err
, exit
);
18036 monitor
= mdns_interface_monitor_create( ifIndex
);
18037 require_action( monitor
, exit
, err
= kNoResourcesErr
);
18040 mdns_interface_monitor_set_queue( monitor
, dispatch_get_main_queue() );
18041 mdns_interface_monitor_set_event_handler( monitor
,
18042 ^( mdns_event_t inEvent
, OSStatus inError
)
18046 case mdns_event_error
:
18047 FPrintF( stderr
, "error: Interface monitor failed: %#m\n", inError
);
18048 mdns_interface_monitor_invalidate( monitor
);
18052 case mdns_event_invalidated
:
18053 FPrintF( stdout
, "Interface monitor invalidated.\n" );
18054 mdns_release( monitor
);
18058 FPrintF( stdout
, "Unhandled event '%s' (%ld)\n", mdns_event_to_string( inEvent
), (long) inEvent
);
18062 mdns_interface_monitor_set_update_handler( monitor
,
18063 ^( __unused mdns_interface_flags_t inChangeFlags
)
18065 _InterfaceMonitorPrint( monitor
);
18068 _InterfaceMonitorPrint( monitor
);
18069 mdns_interface_monitor_activate( monitor
);
18071 signal( SIGINT
, SIG_IGN
);
18072 err
= DispatchSignalSourceCreate( SIGINT
, _InterfaceMonitorSignalHandler
, monitor
, &signalSource
);
18073 require_noerr( err
, exit
);
18074 dispatch_resume( signalSource
);
18079 if( err
) ErrQuit( 1, "error: %#m\n", err
);
18082 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor
)
18084 FPrintF( stdout
, "%{du:time} %@\n", NULL
, inMonitor
);
18087 static void _InterfaceMonitorSignalHandler( void *inContext
)
18089 mdns_interface_monitor_invalidate( (mdns_interface_monitor_t
) inContext
);
18092 //===========================================================================================================================
18094 //===========================================================================================================================
18096 static void _DNSProxyCallback( DNSXConnRef inConnection
, DNSXErrorType inError
);
18097 static void _DNSProxyCmdSignalHandler( void *inContext
);
18099 static void DNSProxyCmd( void )
18103 DNSXConnRef connection
;
18104 IfIndex inputIfIndexes
[ MaxInputIf
];
18105 dispatch_source_t sigIntSource
= NULL
;
18106 dispatch_source_t sigTermSource
= NULL
;
18107 uint32_t outputIfIndex
;
18108 char ifName
[ kInterfaceNameBufLen
];
18110 if( gDNSProxy_InputInterfaceCount
> MaxInputIf
)
18112 FPrintF( stderr
, "error: Invalid input interface count: %zu > %d (max).\n",
18113 gDNSProxy_InputInterfaceCount
, MaxInputIf
);
18118 for( i
= 0; i
< gDNSProxy_InputInterfaceCount
; ++i
)
18122 err
= InterfaceIndexFromArgString( gDNSProxy_InputInterfaces
[ i
], &ifIndex
);
18123 require_noerr_quiet( err
, exit
);
18125 inputIfIndexes
[ i
] = ifIndex
;
18127 while( i
< MaxInputIf
) inputIfIndexes
[ i
++ ] = 0; // Remaining interface indexes are required to be 0.
18129 if( gDNSProxy_OutputInterface
)
18131 err
= InterfaceIndexFromArgString( gDNSProxy_OutputInterface
, &outputIfIndex
);
18132 require_noerr_quiet( err
, exit
);
18136 outputIfIndex
= kDNSIfindexAny
;
18139 FPrintF( stdout
, "Input Interfaces:" );
18140 for( i
= 0; i
< gDNSProxy_InputInterfaceCount
; ++i
)
18142 const uint32_t ifIndex
= (uint32_t) inputIfIndexes
[ i
];
18144 FPrintF( stdout
, "%s %u (%s)", ( i
== 0 ) ? "" : ",", ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
18146 FPrintF( stdout
, "\n" );
18147 FPrintF( stdout
, "Output Interface: %u (%s)\n", outputIfIndex
, InterfaceIndexToName( outputIfIndex
, ifName
) );
18148 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
18149 FPrintF( stdout
, "---\n" );
18152 err
= DNSXEnableProxy( &connection
, kDNSProxyEnable
, inputIfIndexes
, outputIfIndex
, dispatch_get_main_queue(),
18153 _DNSProxyCallback
);
18154 require_noerr_quiet( err
, exit
);
18156 signal( SIGINT
, SIG_IGN
);
18157 err
= DispatchSignalSourceCreate( SIGINT
, _DNSProxyCmdSignalHandler
, connection
, &sigIntSource
);
18158 require_noerr( err
, exit
);
18159 dispatch_activate( sigIntSource
);
18161 signal( SIGTERM
, SIG_IGN
);
18162 err
= DispatchSignalSourceCreate( SIGTERM
, _DNSProxyCmdSignalHandler
, connection
, &sigTermSource
);
18163 require_noerr( err
, exit
);
18164 dispatch_activate( sigTermSource
);
18169 if( err
) ErrQuit( 1, "error: %#m\n", err
);
18172 static void _DNSProxyCallback( DNSXConnRef inConnection
, DNSXErrorType inError
)
18174 Unused( inConnection
);
18176 if( inError
) ErrQuit( 1, "error: DNS proxy failed: %#m\n", inError
);
18179 static void _DNSProxyCmdSignalHandler( void *inContext
)
18181 DNSXConnRef
const connection
= (DNSXConnRef
) inContext
;
18182 struct timeval now
;
18184 gettimeofday( &now
, NULL
);
18186 DNSXRefDeAlloc( connection
);
18188 FPrintF( stdout
, "---\n" );
18189 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
18193 #endif // MDNSRESPONDER_PROJECT
18195 //===========================================================================================================================
18196 // DaemonVersionCmd
18197 //===========================================================================================================================
18199 static void DaemonVersionCmd( void )
18202 uint32_t size
, version
;
18205 size
= (uint32_t) sizeof( version
);
18206 err
= DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion
, &version
, &size
);
18207 require_noerr( err
, exit
);
18209 FPrintF( stdout
, "Daemon version: %s\n", SourceVersionToCString( version
, strBuf
) );
18212 if( err
) exit( 1 );
18215 //===========================================================================================================================
18217 //===========================================================================================================================
18219 static void Exit( void *inContext
)
18221 const char * const reason
= (const char *) inContext
;
18223 FPrintF( stdout
, "---\n" );
18224 FPrintF( stdout
, "End time: %{du:time}\n", NULL
);
18225 if( reason
) FPrintF( stdout
, "End reason: %s\n", reason
);
18229 //===========================================================================================================================
18230 // _PrintFExtensionTimestampHandler
18231 //===========================================================================================================================
18234 _PrintFExtensionTimestampHandler(
18235 PrintFContext
* inContext
,
18236 PrintFFormat
* inFormat
,
18237 PrintFVAList
* inArgs
,
18238 void * inUserContext
)
18240 struct timeval now
;
18241 const struct timeval
* tv
;
18242 struct tm
* localTime
;
18245 char dateTimeStr
[ 32 ];
18247 Unused( inUserContext
);
18249 tv
= va_arg( inArgs
->args
, const struct timeval
* );
18250 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
18254 gettimeofday( &now
, NULL
);
18257 localTime
= localtime( &tv
->tv_sec
);
18258 len
= strftime( dateTimeStr
, sizeof( dateTimeStr
), "%Y-%m-%d %H:%M:%S", localTime
);
18259 if( len
== 0 ) dateTimeStr
[ 0 ] = '\0';
18261 n
= PrintFCore( inContext
, "%s.%06u", dateTimeStr
, (unsigned int) tv
->tv_usec
);
18267 //===========================================================================================================================
18268 // _PrintFExtensionDNSMessageHandler
18269 //===========================================================================================================================
18272 _PrintFExtensionDNSMessageHandler(
18273 PrintFContext
* inContext
,
18274 PrintFFormat
* inFormat
,
18275 PrintFVAList
* inArgs
,
18276 void * inUserContext
)
18279 const void * msgPtr
;
18284 Boolean printRawRData
;
18286 Unused( inUserContext
);
18288 msgPtr
= va_arg( inArgs
->args
, const void * );
18289 msgLen
= va_arg( inArgs
->args
, size_t );
18290 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
18292 isMDNS
= ( inFormat
->altForm
> 0 ) ? true : false;
18293 printRawRData
= ( inFormat
->precision
> 0 ) ? true : false;
18294 err
= DNSMessageToText( msgPtr
, msgLen
, isMDNS
, printRawRData
, &text
);
18297 n
= PrintFCore( inContext
, "%*{text}", inFormat
->fieldWidth
, text
, kSizeCString
);
18302 n
= PrintFCore( inContext
, "%*.1H", inFormat
->fieldWidth
, msgPtr
, (int) msgLen
, (int) msgLen
);
18309 //===========================================================================================================================
18310 // _PrintFExtensionCallbackFlagsHandler
18311 //===========================================================================================================================
18314 _PrintFExtensionCallbackFlagsHandler(
18315 PrintFContext
* inContext
,
18316 PrintFFormat
* inFormat
,
18317 PrintFVAList
* inArgs
,
18318 void * inUserContext
)
18320 DNSServiceFlags flags
;
18323 Unused( inUserContext
);
18325 flags
= va_arg( inArgs
->args
, DNSServiceFlags
);
18326 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
18328 n
= PrintFCore( inContext
, "%08X %s%c %c%c",
18329 flags
, DNSServiceFlagsToAddRmvStr( flags
),
18330 ( flags
& kDNSServiceFlagsMoreComing
) ? '+' : ' ',
18331 ( flags
& kDNSServiceFlagAnsweredFromCache
) ? 'C' : ' ',
18332 ( flags
& kDNSServiceFlagsExpiredAnswer
) ? '*' : ' ' );
18338 //===========================================================================================================================
18339 // _PrintFExtensionDNSRecordDataHandler
18340 //===========================================================================================================================
18343 _PrintFExtensionDNSRecordDataHandler(
18344 PrintFContext
* inContext
,
18345 PrintFFormat
* inFormat
,
18346 PrintFVAList
* inArgs
,
18347 void * inUserContext
)
18349 const void * rdataPtr
;
18350 unsigned int rdataLen
, rdataType
;
18353 Unused( inUserContext
);
18355 rdataType
= va_arg( inArgs
->args
, unsigned int );
18356 rdataPtr
= va_arg( inArgs
->args
, const void * );
18357 rdataLen
= va_arg( inArgs
->args
, unsigned int );
18358 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
18360 check( inFormat
->fieldWidth
< INT_MAX
);
18361 fieldWidth
= inFormat
->leftJustify
? -( (int) inFormat
->fieldWidth
) : ( (int) inFormat
->fieldWidth
);
18365 char * rdataStr
= NULL
;
18367 DNSRecordDataToString( rdataPtr
, rdataLen
, rdataType
, NULL
, 0, &rdataStr
);
18370 n
= PrintFCore( inContext
, "%*s", fieldWidth
, rdataStr
);
18375 n
= PrintFCore( inContext
, "%*H", fieldWidth
, rdataPtr
, rdataLen
, rdataLen
);
18380 n
= PrintFCore( inContext
, "%*s", fieldWidth
, "<< ZERO-LENGTH RDATA >>" );
18387 //===========================================================================================================================
18388 // GetDNSSDFlagsFromOpts
18389 //===========================================================================================================================
18391 static DNSServiceFlags
GetDNSSDFlagsFromOpts( void )
18393 DNSServiceFlags flags
;
18395 flags
= (DNSServiceFlags
) gDNSSDFlags
;
18396 if( flags
& kDNSServiceFlagsShareConnection
)
18398 FPrintF( stderr
, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
18399 kDNSServiceFlagsShareConnection
);
18402 if( gDNSSDFlag_AllowExpiredAnswers
) flags
|= kDNSServiceFlagsAllowExpiredAnswers
;
18403 if( gDNSSDFlag_BrowseDomains
) flags
|= kDNSServiceFlagsBrowseDomains
;
18404 if( gDNSSDFlag_DenyCellular
) flags
|= kDNSServiceFlagsDenyCellular
;
18405 if( gDNSSDFlag_DenyConstrained
) flags
|= kDNSServiceFlagsDenyConstrained
;
18406 if( gDNSSDFlag_DenyExpensive
) flags
|= kDNSServiceFlagsDenyExpensive
;
18407 if( gDNSSDFlag_ForceMulticast
) flags
|= kDNSServiceFlagsForceMulticast
;
18408 if( gDNSSDFlag_IncludeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
18409 if( gDNSSDFlag_KnownUnique
) flags
|= kDNSServiceFlagsKnownUnique
;
18410 if( gDNSSDFlag_NoAutoRename
) flags
|= kDNSServiceFlagsNoAutoRename
;
18411 if( gDNSSDFlag_PathEvaluationDone
) flags
|= kDNSServiceFlagsPathEvaluationDone
;
18412 if( gDNSSDFlag_RegistrationDomains
) flags
|= kDNSServiceFlagsRegistrationDomains
;
18413 if( gDNSSDFlag_ReturnIntermediates
) flags
|= kDNSServiceFlagsReturnIntermediates
;
18414 if( gDNSSDFlag_Shared
) flags
|= kDNSServiceFlagsShared
;
18415 if( gDNSSDFlag_SuppressUnusable
) flags
|= kDNSServiceFlagsSuppressUnusable
;
18416 if( gDNSSDFlag_Timeout
) flags
|= kDNSServiceFlagsTimeout
;
18417 if( gDNSSDFlag_UnicastResponse
) flags
|= kDNSServiceFlagsUnicastResponse
;
18418 if( gDNSSDFlag_Unique
) flags
|= kDNSServiceFlagsUnique
;
18419 if( gDNSSDFlag_WakeOnResolve
) flags
|= kDNSServiceFlagsWakeOnResolve
;
18424 //===========================================================================================================================
18425 // CreateConnectionFromArgString
18426 //===========================================================================================================================
18429 CreateConnectionFromArgString(
18430 const char * inString
,
18431 dispatch_queue_t inQueue
,
18432 DNSServiceRef
* outSDRef
,
18433 ConnectionDesc
* outDesc
)
18436 DNSServiceRef sdRef
= NULL
;
18437 ConnectionType type
;
18438 int32_t pid
= -1; // Initializing because the analyzer claims pid may be used uninitialized.
18439 uint8_t uuid
[ 16 ];
18441 if( strcasecmp( inString
, kConnectionArg_Normal
) == 0 )
18443 err
= DNSServiceCreateConnection( &sdRef
);
18444 require_noerr( err
, exit
);
18445 type
= kConnectionType_Normal
;
18447 else if( stricmp_prefix( inString
, kConnectionArgPrefix_PID
) == 0 )
18449 const char * const pidStr
= inString
+ sizeof_string( kConnectionArgPrefix_PID
);
18451 err
= StringToInt32( pidStr
, &pid
);
18454 FPrintF( stderr
, "Invalid delegate connection PID value: %s\n", pidStr
);
18459 memset( uuid
, 0, sizeof( uuid
) );
18460 err
= DNSServiceCreateDelegateConnection( &sdRef
, pid
, uuid
);
18463 FPrintF( stderr
, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err
, pid
);
18466 type
= kConnectionType_DelegatePID
;
18468 else if( stricmp_prefix( inString
, kConnectionArgPrefix_UUID
) == 0 )
18470 const char * const uuidStr
= inString
+ sizeof_string( kConnectionArgPrefix_UUID
);
18472 check_compile_time_code( sizeof( uuid
) == sizeof( uuid_t
) );
18474 err
= StringToUUID( uuidStr
, kSizeCString
, false, uuid
);
18477 FPrintF( stderr
, "Invalid delegate connection UUID value: %s\n", uuidStr
);
18482 err
= DNSServiceCreateDelegateConnection( &sdRef
, 0, uuid
);
18485 FPrintF( stderr
, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err
, uuid
);
18488 type
= kConnectionType_DelegateUUID
;
18492 FPrintF( stderr
, "Unrecognized connection string \"%s\".\n", inString
);
18497 err
= DNSServiceSetDispatchQueue( sdRef
, inQueue
);
18498 require_noerr( err
, exit
);
18503 outDesc
->type
= type
;
18504 if( type
== kConnectionType_DelegatePID
) outDesc
->delegate
.pid
= pid
;
18505 else if( type
== kConnectionType_DelegateUUID
) memcpy( outDesc
->delegate
.uuid
, uuid
, 16 );
18510 if( sdRef
) DNSServiceRefDeallocate( sdRef
);
18514 //===========================================================================================================================
18515 // InterfaceIndexFromArgString
18516 //===========================================================================================================================
18518 static OSStatus
InterfaceIndexFromArgString( const char *inString
, uint32_t *outIndex
)
18525 ifIndex
= if_nametoindex( inString
);
18528 err
= StringToUInt32( inString
, &ifIndex
);
18531 FPrintF( stderr
, "error: Invalid interface value: %s\n", inString
);
18542 *outIndex
= ifIndex
;
18549 //===========================================================================================================================
18550 // RecordDataFromArgString
18551 //===========================================================================================================================
18553 static OSStatus
RecordDataFromArgString( const char *inString
, uint8_t **outDataPtr
, size_t *outDataLen
)
18556 uint8_t * dataPtr
= NULL
;
18563 else if( stricmp_prefix( inString
, kRDataArgPrefix_Domain
) == 0 )
18565 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_Domain
);
18567 err
= StringToDomainName( str
, &dataPtr
, &dataLen
);
18568 require_noerr_quiet( err
, exit
);
18573 else if( stricmp_prefix( inString
, kRDataArgPrefix_File
) == 0 )
18575 const char * const path
= inString
+ sizeof_string( kRDataArgPrefix_File
);
18577 err
= CopyFileDataByPath( path
, (char **) &dataPtr
, &dataLen
);
18578 require_noerr( err
, exit
);
18579 require_action( dataLen
<= kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
18582 // Hexadecimal string
18584 else if( stricmp_prefix( inString
, kRDataArgPrefix_HexString
) == 0 )
18586 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_HexString
);
18588 err
= HexToDataCopy( str
, kSizeCString
, kHexToData_DefaultFlags
, &dataPtr
, &dataLen
, NULL
);
18589 require_noerr( err
, exit
);
18590 require_action( dataLen
<= kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
18593 // IPv4 address string
18595 else if( stricmp_prefix( inString
, kRDataArgPrefix_IPv4
) == 0 )
18597 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_IPv4
);
18599 err
= StringToARecordData( str
, &dataPtr
, &dataLen
);
18600 require_noerr_quiet( err
, exit
);
18603 // IPv6 address string
18605 else if( stricmp_prefix( inString
, kRDataArgPrefix_IPv6
) == 0 )
18607 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_IPv6
);
18609 err
= StringToAAAARecordData( str
, &dataPtr
, &dataLen
);
18610 require_noerr_quiet( err
, exit
);
18615 else if( stricmp_prefix( inString
, kRDataArgPrefix_SRV
) == 0 )
18617 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_SRV
);
18619 err
= CreateSRVRecordDataFromString( str
, &dataPtr
, &dataLen
);
18620 require_noerr( err
, exit
);
18623 // String with escaped hex and octal bytes
18625 else if( stricmp_prefix( inString
, kRDataArgPrefix_String
) == 0 )
18627 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_String
);
18628 const char * const end
= str
+ strlen( str
);
18635 success
= _ParseQuotedEscapedString( str
, end
, "", NULL
, 0, NULL
, &totalLen
, NULL
);
18636 require_action( success
, exit
, err
= kParamErr
);
18637 require_action( totalLen
<= kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
18639 dataLen
= totalLen
;
18640 dataPtr
= (uint8_t *) malloc( dataLen
);
18641 require_action( dataPtr
, exit
, err
= kNoMemoryErr
);
18643 success
= _ParseQuotedEscapedString( str
, end
, "", (char *) dataPtr
, dataLen
, &copiedLen
, NULL
, NULL
);
18644 require_action( success
, exit
, err
= kParamErr
);
18645 check( copiedLen
== dataLen
);
18656 else if( stricmp_prefix( inString
, kRDataArgPrefix_TXT
) == 0 )
18658 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_TXT
);
18660 err
= CreateTXTRecordDataFromString( str
, ',', &dataPtr
, &dataLen
);
18661 require_noerr( err
, exit
);
18664 // Unrecognized format
18668 FPrintF( stderr
, "Unrecognized record data string \"%s\".\n", inString
);
18674 *outDataLen
= dataLen
;
18675 *outDataPtr
= dataPtr
;
18679 FreeNullSafe( dataPtr
);
18683 //===========================================================================================================================
18684 // RecordTypeFromArgString
18685 //===========================================================================================================================
18689 uint16_t value
; // Record type's numeric value.
18690 const char * name
; // Record type's name as a string (e.g., "A", "PTR", "SRV").
18694 static const RecordType kRecordTypes
[] =
18698 { kDNSServiceType_A
, "A" },
18699 { kDNSServiceType_AAAA
, "AAAA" },
18700 { kDNSServiceType_PTR
, "PTR" },
18701 { kDNSServiceType_SRV
, "SRV" },
18702 { kDNSServiceType_TXT
, "TXT" },
18703 { kDNSServiceType_CNAME
, "CNAME" },
18704 { kDNSServiceType_SOA
, "SOA" },
18705 { kDNSServiceType_NSEC
, "NSEC" },
18706 { kDNSServiceType_NS
, "NS" },
18707 { kDNSServiceType_MX
, "MX" },
18708 { kDNSServiceType_ANY
, "ANY" },
18709 { kDNSServiceType_OPT
, "OPT" },
18711 // Less common types.
18713 { kDNSServiceType_MD
, "MD" },
18714 { kDNSServiceType_NS
, "NS" },
18715 { kDNSServiceType_MD
, "MD" },
18716 { kDNSServiceType_MF
, "MF" },
18717 { kDNSServiceType_MB
, "MB" },
18718 { kDNSServiceType_MG
, "MG" },
18719 { kDNSServiceType_MR
, "MR" },
18720 { kDNSServiceType_NULL
, "NULL" },
18721 { kDNSServiceType_WKS
, "WKS" },
18722 { kDNSServiceType_HINFO
, "HINFO" },
18723 { kDNSServiceType_MINFO
, "MINFO" },
18724 { kDNSServiceType_RP
, "RP" },
18725 { kDNSServiceType_AFSDB
, "AFSDB" },
18726 { kDNSServiceType_X25
, "X25" },
18727 { kDNSServiceType_ISDN
, "ISDN" },
18728 { kDNSServiceType_RT
, "RT" },
18729 { kDNSServiceType_NSAP
, "NSAP" },
18730 { kDNSServiceType_NSAP_PTR
, "NSAP_PTR" },
18731 { kDNSServiceType_SIG
, "SIG" },
18732 { kDNSServiceType_KEY
, "KEY" },
18733 { kDNSServiceType_PX
, "PX" },
18734 { kDNSServiceType_GPOS
, "GPOS" },
18735 { kDNSServiceType_LOC
, "LOC" },
18736 { kDNSServiceType_NXT
, "NXT" },
18737 { kDNSServiceType_EID
, "EID" },
18738 { kDNSServiceType_NIMLOC
, "NIMLOC" },
18739 { kDNSServiceType_ATMA
, "ATMA" },
18740 { kDNSServiceType_NAPTR
, "NAPTR" },
18741 { kDNSServiceType_KX
, "KX" },
18742 { kDNSServiceType_CERT
, "CERT" },
18743 { kDNSServiceType_A6
, "A6" },
18744 { kDNSServiceType_DNAME
, "DNAME" },
18745 { kDNSServiceType_SINK
, "SINK" },
18746 { kDNSServiceType_APL
, "APL" },
18747 { kDNSServiceType_DS
, "DS" },
18748 { kDNSServiceType_SSHFP
, "SSHFP" },
18749 { kDNSServiceType_IPSECKEY
, "IPSECKEY" },
18750 { kDNSServiceType_RRSIG
, "RRSIG" },
18751 { kDNSServiceType_DNSKEY
, "DNSKEY" },
18752 { kDNSServiceType_DHCID
, "DHCID" },
18753 { kDNSServiceType_NSEC3
, "NSEC3" },
18754 { kDNSServiceType_NSEC3PARAM
, "NSEC3PARAM" },
18755 { kDNSServiceType_HIP
, "HIP" },
18756 { kDNSServiceType_SPF
, "SPF" },
18757 { kDNSServiceType_UINFO
, "UINFO" },
18758 { kDNSServiceType_UID
, "UID" },
18759 { kDNSServiceType_GID
, "GID" },
18760 { kDNSServiceType_UNSPEC
, "UNSPEC" },
18761 { kDNSServiceType_TKEY
, "TKEY" },
18762 { kDNSServiceType_TSIG
, "TSIG" },
18763 { kDNSServiceType_IXFR
, "IXFR" },
18764 { kDNSServiceType_AXFR
, "AXFR" },
18765 { kDNSServiceType_MAILB
, "MAILB" },
18766 { kDNSServiceType_MAILA
, "MAILA" }
18769 static OSStatus
RecordTypeFromArgString( const char *inString
, uint16_t *outValue
)
18773 const RecordType
* type
;
18774 const RecordType
* const end
= kRecordTypes
+ countof( kRecordTypes
);
18776 for( type
= kRecordTypes
; type
< end
; ++type
)
18778 if( strcasecmp( type
->name
, inString
) == 0 )
18780 *outValue
= type
->value
;
18785 err
= StringToInt32( inString
, &i32
);
18786 require_noerr_quiet( err
, exit
);
18787 require_action_quiet( ( i32
>= 0 ) && ( i32
<= UINT16_MAX
), exit
, err
= kParamErr
);
18789 *outValue
= (uint16_t) i32
;
18795 //===========================================================================================================================
18796 // RecordClassFromArgString
18797 //===========================================================================================================================
18799 static OSStatus
RecordClassFromArgString( const char *inString
, uint16_t *outValue
)
18804 if( strcasecmp( inString
, "IN" ) == 0 )
18806 *outValue
= kDNSServiceClass_IN
;
18811 err
= StringToInt32( inString
, &i32
);
18812 require_noerr_quiet( err
, exit
);
18813 require_action_quiet( ( i32
>= 0 ) && ( i32
<= UINT16_MAX
), exit
, err
= kParamErr
);
18815 *outValue
= (uint16_t) i32
;
18821 //===========================================================================================================================
18822 // InterfaceIndexToName
18823 //===========================================================================================================================
18825 static char * InterfaceIndexToName( uint32_t inIfIndex
, char inNameBuf
[ kInterfaceNameBufLen
] )
18827 switch( inIfIndex
)
18829 case kDNSServiceInterfaceIndexAny
:
18830 strlcpy( inNameBuf
, "Any", kInterfaceNameBufLen
);
18833 case kDNSServiceInterfaceIndexLocalOnly
:
18834 strlcpy( inNameBuf
, "LocalOnly", kInterfaceNameBufLen
);
18837 case kDNSServiceInterfaceIndexUnicast
:
18838 strlcpy( inNameBuf
, "Unicast", kInterfaceNameBufLen
);
18841 case kDNSServiceInterfaceIndexP2P
:
18842 strlcpy( inNameBuf
, "P2P", kInterfaceNameBufLen
);
18845 #if( defined( kDNSServiceInterfaceIndexBLE ) )
18846 case kDNSServiceInterfaceIndexBLE
:
18847 strlcpy( inNameBuf
, "BLE", kInterfaceNameBufLen
);
18855 name
= if_indextoname( inIfIndex
, inNameBuf
);
18856 if( !name
) strlcpy( inNameBuf
, "NO NAME", kInterfaceNameBufLen
);
18861 return( inNameBuf
);
18864 //===========================================================================================================================
18865 // RecordTypeToString
18866 //===========================================================================================================================
18868 static const char * RecordTypeToString( unsigned int inValue
)
18870 const RecordType
* type
;
18871 const RecordType
* const end
= kRecordTypes
+ countof( kRecordTypes
);
18873 for( type
= kRecordTypes
; type
< end
; ++type
)
18875 if( type
->value
== inValue
) return( type
->name
);
18880 //===========================================================================================================================
18881 // DNSRecordDataToString
18882 //===========================================================================================================================
18885 DNSRecordDataToString(
18886 const void * inRDataPtr
,
18888 unsigned int inRDataType
,
18889 const void * inMsgPtr
,
18891 char ** outString
)
18894 const uint8_t * const rdataPtr
= (uint8_t *) inRDataPtr
;
18895 const uint8_t * const rdataEnd
= rdataPtr
+ inRDataLen
;
18897 const uint8_t * ptr
;
18899 char domainNameStr
[ kDNSServiceMaxDomainName
];
18905 if( inRDataType
== kDNSServiceType_A
)
18907 require_action_quiet( inRDataLen
== 4, exit
, err
= kMalformedErr
);
18909 ASPrintF( &rdataStr
, "%.4a", rdataPtr
);
18910 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
18915 else if( inRDataType
== kDNSServiceType_AAAA
)
18917 require_action_quiet( inRDataLen
== 16, exit
, err
= kMalformedErr
);
18919 ASPrintF( &rdataStr
, "%.16a", rdataPtr
);
18920 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
18923 // PTR, CNAME, or NS Record
18925 else if( ( inRDataType
== kDNSServiceType_PTR
) ||
18926 ( inRDataType
== kDNSServiceType_CNAME
) ||
18927 ( inRDataType
== kDNSServiceType_NS
) )
18931 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, NULL
);
18932 require_noerr( err
, exit
);
18936 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, NULL
);
18937 require_noerr( err
, exit
);
18940 rdataStr
= strdup( domainNameStr
);
18941 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
18946 else if( inRDataType
== kDNSServiceType_SRV
)
18948 const dns_fixed_fields_srv
* fields
;
18949 const uint8_t * target
;
18950 unsigned int priority
, weight
, port
;
18952 require_action_quiet( inRDataLen
> sizeof( dns_fixed_fields_srv
), exit
, err
= kMalformedErr
);
18954 fields
= (const dns_fixed_fields_srv
*) rdataPtr
;
18955 priority
= dns_fixed_fields_srv_get_priority( fields
);
18956 weight
= dns_fixed_fields_srv_get_weight( fields
);
18957 port
= dns_fixed_fields_srv_get_port( fields
);
18958 target
= (const uint8_t *) &fields
[ 1 ];
18962 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, target
, domainNameStr
, NULL
);
18963 require_noerr( err
, exit
);
18967 err
= DomainNameToString( target
, rdataEnd
, domainNameStr
, NULL
);
18968 require_noerr( err
, exit
);
18971 ASPrintF( &rdataStr
, "%u %u %u %s", priority
, weight
, port
, domainNameStr
);
18972 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
18977 else if( inRDataType
== kDNSServiceType_TXT
)
18979 require_action_quiet( inRDataLen
> 0, exit
, err
= kMalformedErr
);
18981 if( inRDataLen
== 1 )
18983 ASPrintF( &rdataStr
, "%#H", rdataPtr
, (int) inRDataLen
, INT_MAX
);
18984 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
18988 ASPrintF( &rdataStr
, "%#{txt}", rdataPtr
, inRDataLen
);
18989 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
18995 else if( inRDataType
== kDNSServiceType_SOA
)
18997 const dns_fixed_fields_soa
* fields
;
18998 uint32_t serial
, refresh
, retry
, expire
, minimum
;
19002 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
19003 require_noerr( err
, exit
);
19005 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
19007 rdataStr
= strdup( domainNameStr
);
19008 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19010 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, ptr
, domainNameStr
, &ptr
);
19011 require_noerr( err
, exit
);
19015 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
19016 require_noerr( err
, exit
);
19018 rdataStr
= strdup( domainNameStr
);
19019 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19021 err
= DomainNameToString( ptr
, rdataEnd
, domainNameStr
, &ptr
);
19022 require_noerr( err
, exit
);
19025 require_action_quiet( ( rdataEnd
- ptr
) == sizeof( dns_fixed_fields_soa
), exit
, err
= kMalformedErr
);
19027 fields
= (const dns_fixed_fields_soa
*) ptr
;
19028 serial
= dns_fixed_fields_soa_get_serial( fields
);
19029 refresh
= dns_fixed_fields_soa_get_refresh( fields
);
19030 retry
= dns_fixed_fields_soa_get_retry( fields
);
19031 expire
= dns_fixed_fields_soa_get_expire( fields
);
19032 minimum
= dns_fixed_fields_soa_get_minimum( fields
);
19034 n
= AppendPrintF( &rdataStr
, " %s %u %u %u %u %u\n", domainNameStr
, serial
, refresh
, retry
, expire
, minimum
);
19035 require_action( n
> 0, exit
, err
= kUnknownErr
);
19040 else if( inRDataType
== kDNSServiceType_NSEC
)
19042 unsigned int windowBlock
, bitmapLen
, i
, recordType
;
19043 const uint8_t * bitmapPtr
;
19047 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
19048 require_noerr( err
, exit
);
19052 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
19053 require_noerr( err
, exit
);
19056 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
19058 rdataStr
= strdup( domainNameStr
);
19059 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19061 for( ; ptr
< rdataEnd
; ptr
+= ( 2 + bitmapLen
) )
19063 require_action_quiet( ( ptr
+ 2 ) < rdataEnd
, exit
, err
= kMalformedErr
);
19065 windowBlock
= ptr
[ 0 ];
19066 bitmapLen
= ptr
[ 1 ];
19067 bitmapPtr
= &ptr
[ 2 ];
19069 require_action_quiet( ( bitmapLen
>= 1 ) && ( bitmapLen
<= 32 ) , exit
, err
= kMalformedErr
);
19070 require_action_quiet( ( bitmapPtr
+ bitmapLen
) <= rdataEnd
, exit
, err
= kMalformedErr
);
19072 for( i
= 0; i
< BitArray_MaxBits( bitmapLen
); ++i
)
19074 if( BitArray_GetBit( bitmapPtr
, bitmapLen
, i
) )
19076 recordType
= ( windowBlock
* 256 ) + i
;
19077 n
= AppendPrintF( &rdataStr
, " %s", RecordTypeToString( recordType
) );
19078 require_action( n
> 0, exit
, err
= kUnknownErr
);
19086 else if( inRDataType
== kDNSServiceType_MX
)
19088 uint16_t preference
;
19089 const uint8_t * exchange
;
19091 require_action_quiet( ( rdataPtr
+ 2 ) < rdataEnd
, exit
, err
= kMalformedErr
);
19093 preference
= ReadBig16( rdataPtr
);
19094 exchange
= &rdataPtr
[ 2 ];
19098 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, exchange
, domainNameStr
, NULL
);
19099 require_noerr( err
, exit
);
19103 err
= DomainNameToString( exchange
, rdataEnd
, domainNameStr
, NULL
);
19104 require_noerr( err
, exit
);
19107 n
= ASPrintF( &rdataStr
, "%u %s", preference
, domainNameStr
);
19108 require_action( n
> 0, exit
, err
= kUnknownErr
);
19111 // Unhandled record type
19115 err
= kNotHandledErr
;
19120 *outString
= rdataStr
;
19125 FreeNullSafe( rdataStr
);
19129 //===========================================================================================================================
19130 // DNSMessageToText
19131 //===========================================================================================================================
19133 #define DNSFlagsOpCodeToString( X ) ( \
19134 ( (X) == kDNSOpCode_Query ) ? "Query" : \
19135 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \
19136 ( (X) == kDNSOpCode_Status ) ? "Status" : \
19137 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \
19138 ( (X) == kDNSOpCode_Update ) ? "Update" : \
19141 #define DNSFlagsRCodeToString( X ) ( \
19142 ( (X) == kDNSRCode_NoError ) ? "NoError" : \
19143 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \
19144 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \
19145 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \
19146 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \
19147 ( (X) == kDNSRCode_Refused ) ? "Refused" : \
19152 const uint8_t * inMsgPtr
,
19154 const Boolean inMDNS
,
19155 const Boolean inPrintRaw
,
19159 DataBuffer dataBuf
;
19161 const DNSHeader
* hdr
;
19162 const uint8_t * ptr
;
19163 unsigned int id
, flags
, opcode
, rcode
;
19164 unsigned int questionCount
, answerCount
, authorityCount
, additionalCount
, i
, totalRRCount
;
19165 uint8_t name
[ kDomainNameLengthMax
];
19166 char nameStr
[ kDNSServiceMaxDomainName
];
19168 DataBuffer_Init( &dataBuf
, NULL
, 0, SIZE_MAX
);
19169 #define _Append( ... ) do { err = DataBuffer_AppendF( &dataBuf, __VA_ARGS__ ); require_noerr( err, exit ); } while( 0 )
19171 require_action_quiet( inMsgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
19173 hdr
= (DNSHeader
*) inMsgPtr
;
19174 id
= DNSHeaderGetID( hdr
);
19175 flags
= DNSHeaderGetFlags( hdr
);
19176 questionCount
= DNSHeaderGetQuestionCount( hdr
);
19177 answerCount
= DNSHeaderGetAnswerCount( hdr
);
19178 authorityCount
= DNSHeaderGetAuthorityCount( hdr
);
19179 additionalCount
= DNSHeaderGetAdditionalCount( hdr
);
19180 opcode
= DNSFlagsGetOpCode( flags
);
19181 rcode
= DNSFlagsGetRCode( flags
);
19183 _Append( "ID: 0x%04X (%u)\n", id
, id
);
19184 _Append( "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA%?s%?s %s\n",
19186 ( flags
& kDNSHeaderFlag_Response
) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode
),
19187 ( flags
& kDNSHeaderFlag_AuthAnswer
) ? ' ' : '!',
19188 ( flags
& kDNSHeaderFlag_Truncation
) ? ' ' : '!',
19189 ( flags
& kDNSHeaderFlag_RecursionDesired
) ? ' ' : '!',
19190 ( flags
& kDNSHeaderFlag_RecursionAvailable
) ? ' ' : '!',
19191 !inMDNS
, ( flags
& kDNSHeaderFlag_AuthenticData
) ? " AD" : "!AD",
19192 !inMDNS
, ( flags
& kDNSHeaderFlag_CheckingDisabled
) ? " CD" : "!CD",
19193 DNSFlagsRCodeToString( rcode
) );
19194 _Append( "Question count: %u\n", questionCount
);
19195 _Append( "Answer count: %u\n", answerCount
);
19196 _Append( "Authority count: %u\n", authorityCount
);
19197 _Append( "Additional count: %u\n", additionalCount
);
19199 ptr
= (const uint8_t *) &hdr
[ 1 ];
19200 for( i
= 0; i
< questionCount
; ++i
)
19202 uint16_t qtype
, qclass
;
19205 err
= DNSMessageExtractQuestion( inMsgPtr
, inMsgLen
, ptr
, name
, &qtype
, &qclass
, &ptr
);
19206 require_noerr( err
, exit
);
19208 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
19209 require_noerr( err
, exit
);
19211 isQU
= ( inMDNS
&& ( qclass
& kQClassUnicastResponseBit
) ) ? true : false;
19212 if( inMDNS
) qclass
&= ~kQClassUnicastResponseBit
;
19214 if( i
== 0 ) _Append( "\nQUESTION SECTION\n" );
19216 _Append( "%-30s %2s %?2s%?2u %-5s\n",
19217 nameStr
, inMDNS
? ( isQU
? "QU" : "QM" ) : "",
19218 ( qclass
== kDNSServiceClass_IN
), "IN", ( qclass
!= kDNSServiceClass_IN
), qclass
, RecordTypeToString( qtype
) );
19221 totalRRCount
= answerCount
+ authorityCount
+ additionalCount
;
19222 for( i
= 0; i
< totalRRCount
; ++i
)
19227 const uint8_t * rdataPtr
;
19230 Boolean cacheFlush
;
19232 err
= DNSMessageExtractRecord( inMsgPtr
, inMsgLen
, ptr
, name
, &type
, &class, &ttl
, &rdataPtr
, &rdataLen
, &ptr
);
19233 require_noerr( err
, exit
);
19235 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
19236 require_noerr( err
, exit
);
19238 cacheFlush
= ( inMDNS
&& ( class & kRRClassCacheFlushBit
) ) ? true : false;
19239 if( inMDNS
) class &= ~kRRClassCacheFlushBit
;
19242 if( !inPrintRaw
) DNSRecordDataToString( rdataPtr
, rdataLen
, type
, inMsgPtr
, inMsgLen
, &rdataStr
);
19245 ASPrintF( &rdataStr
, "%#H", rdataPtr
, (int) rdataLen
, (int) rdataLen
);
19246 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19249 if( answerCount
&& ( i
== 0 ) ) _Append( "\nANSWER SECTION\n" );
19250 else if( authorityCount
&& ( i
== answerCount
) ) _Append( "\nAUTHORITY SECTION\n" );
19251 else if( additionalCount
&& ( i
== ( answerCount
+ authorityCount
) ) ) _Append( "\nADDITIONAL SECTION\n" );
19253 _Append( "%-42s %6u %2s %?2s%?2u %-5s %s\n",
19254 nameStr
, ttl
, cacheFlush
? "CF" : "",
19255 ( class == kDNSServiceClass_IN
), "IN", ( class != kDNSServiceClass_IN
), class,
19256 RecordTypeToString( type
), rdataStr
);
19261 err
= DataBuffer_Append( &dataBuf
, "", 1 );
19262 require_noerr( err
, exit
);
19264 err
= DataBuffer_Detach( &dataBuf
, (uint8_t **) outText
, &len
);
19265 require_noerr( err
, exit
);
19268 DataBuffer_Free( &dataBuf
);
19272 //===========================================================================================================================
19273 // WriteDNSQueryMessage
19274 //===========================================================================================================================
19277 WriteDNSQueryMessage(
19278 uint8_t inMsg
[ kDNSQueryMessageMaxLen
],
19281 const char * inQName
,
19284 size_t * outMsgLen
)
19287 uint8_t qname
[ kDomainNameLengthMax
];
19289 err
= DomainNameFromString( qname
, inQName
, NULL
);
19290 require_noerr_quiet( err
, exit
);
19292 err
= DNSMessageWriteQuery( inMsgID
, inFlags
, qname
, inQType
, inQClass
, inMsg
, outMsgLen
);
19293 require_noerr_quiet( err
, exit
);
19299 //===========================================================================================================================
19300 // DispatchSignalSourceCreate
19301 //===========================================================================================================================
19304 DispatchSignalSourceCreate(
19306 DispatchHandler inEventHandler
,
19308 dispatch_source_t
* outSource
)
19311 dispatch_source_t source
;
19313 source
= dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL
, (uintptr_t) inSignal
, 0, dispatch_get_main_queue() );
19314 require_action( source
, exit
, err
= kUnknownErr
);
19316 dispatch_set_context( source
, inContext
);
19317 dispatch_source_set_event_handler_f( source
, inEventHandler
);
19319 *outSource
= source
;
19326 //===========================================================================================================================
19327 // DispatchSocketSourceCreate
19328 //===========================================================================================================================
19331 DispatchSocketSourceCreate(
19333 dispatch_source_type_t inType
,
19334 dispatch_queue_t inQueue
,
19335 DispatchHandler inEventHandler
,
19336 DispatchHandler inCancelHandler
,
19338 dispatch_source_t
* outSource
)
19341 dispatch_source_t source
;
19343 source
= dispatch_source_create( inType
, (uintptr_t) inSock
, 0, inQueue
? inQueue
: dispatch_get_main_queue() );
19344 require_action( source
, exit
, err
= kUnknownErr
);
19346 dispatch_set_context( source
, inContext
);
19347 dispatch_source_set_event_handler_f( source
, inEventHandler
);
19348 dispatch_source_set_cancel_handler_f( source
, inCancelHandler
);
19350 *outSource
= source
;
19357 //===========================================================================================================================
19358 // DispatchTimerCreate
19359 //===========================================================================================================================
19362 DispatchTimerCreate(
19363 dispatch_time_t inStart
,
19364 uint64_t inIntervalNs
,
19365 uint64_t inLeewayNs
,
19366 dispatch_queue_t inQueue
,
19367 DispatchHandler inEventHandler
,
19368 DispatchHandler inCancelHandler
,
19370 dispatch_source_t
* outTimer
)
19373 dispatch_source_t timer
;
19375 timer
= dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, inQueue
? inQueue
: dispatch_get_main_queue() );
19376 require_action( timer
, exit
, err
= kUnknownErr
);
19378 dispatch_source_set_timer( timer
, inStart
, inIntervalNs
, inLeewayNs
);
19379 dispatch_set_context( timer
, inContext
);
19380 dispatch_source_set_event_handler_f( timer
, inEventHandler
);
19381 dispatch_source_set_cancel_handler_f( timer
, inCancelHandler
);
19390 //===========================================================================================================================
19391 // DispatchProcessMonitorCreate
19392 //===========================================================================================================================
19395 DispatchProcessMonitorCreate(
19397 unsigned long inFlags
,
19398 dispatch_queue_t inQueue
,
19399 DispatchHandler inEventHandler
,
19400 DispatchHandler inCancelHandler
,
19402 dispatch_source_t
* outMonitor
)
19405 dispatch_source_t monitor
;
19407 monitor
= dispatch_source_create( DISPATCH_SOURCE_TYPE_PROC
, (uintptr_t) inPID
, inFlags
,
19408 inQueue
? inQueue
: dispatch_get_main_queue() );
19409 require_action( monitor
, exit
, err
= kUnknownErr
);
19411 dispatch_set_context( monitor
, inContext
);
19412 dispatch_source_set_event_handler_f( monitor
, inEventHandler
);
19413 dispatch_source_set_cancel_handler_f( monitor
, inCancelHandler
);
19415 *outMonitor
= monitor
;
19422 //===========================================================================================================================
19423 // ServiceTypeDescription
19424 //===========================================================================================================================
19428 const char * name
; // Name of the service type in two-label "_service._proto" format.
19429 const char * description
; // Description of the service type.
19433 // A Non-comprehensive table of DNS-SD service types
19435 static const ServiceType kServiceTypes
[] =
19437 { "_acp-sync._tcp", "AirPort Base Station Sync" },
19438 { "_adisk._tcp", "Automatic Disk Discovery" },
19439 { "_afpovertcp._tcp", "Apple File Sharing" },
19440 { "_airdrop._tcp", "AirDrop" },
19441 { "_airplay._tcp", "AirPlay" },
19442 { "_airport._tcp", "AirPort Base Station" },
19443 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
19444 { "_eppc._tcp", "Remote AppleEvents" },
19445 { "_ftp._tcp", "File Transfer Protocol" },
19446 { "_home-sharing._tcp", "Home Sharing" },
19447 { "_homekit._tcp", "HomeKit" },
19448 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
19449 { "_https._tcp", "HTTP over SSL/TLS" },
19450 { "_ipp._tcp", "Internet Printing Protocol" },
19451 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
19452 { "_mediaremotetv._tcp", "Media Remote" },
19453 { "_net-assistant._tcp", "Apple Remote Desktop" },
19454 { "_od-master._tcp", "OpenDirectory Master" },
19455 { "_nfs._tcp", "Network File System" },
19456 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
19457 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
19458 { "_raop._tcp", "Remote Audio Output Protocol" },
19459 { "_rfb._tcp", "Remote Frame Buffer" },
19460 { "_scanner._tcp", "Bonjour Scanning" },
19461 { "_smb._tcp", "Server Message Block over TCP/IP" },
19462 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
19463 { "_sleep-proxy._udp", "Sleep Proxy Server" },
19464 { "_ssh._tcp", "SSH Remote Login Protocol" },
19465 { "_teleport._tcp", "teleport" },
19466 { "_tftp._tcp", "Trivial File Transfer Protocol" },
19467 { "_workstation._tcp", "Workgroup Manager" },
19468 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
19469 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
19472 static const char * ServiceTypeDescription( const char *inName
)
19474 const ServiceType
* serviceType
;
19475 const ServiceType
* const end
= kServiceTypes
+ countof( kServiceTypes
);
19477 for( serviceType
= kServiceTypes
; serviceType
< end
; ++serviceType
)
19479 if( ( stricmp_prefix( inName
, serviceType
->name
) == 0 ) )
19481 const char * const ptr
= &inName
[ strlen( serviceType
->name
) ];
19483 if( ( ptr
[ 0 ] == '\0' ) || ( ( ptr
[ 0 ] == '.' ) && ( ptr
[ 1 ] == '\0' ) ) )
19485 return( serviceType
->description
);
19492 //===========================================================================================================================
19493 // SocketContextCreate
19494 //===========================================================================================================================
19496 static OSStatus
SocketContextCreate( SocketRef inSock
, void * inUserContext
, SocketContext
**outContext
)
19499 SocketContext
* context
;
19501 context
= (SocketContext
*) calloc( 1, sizeof( *context
) );
19502 require_action( context
, exit
, err
= kNoMemoryErr
);
19504 context
->refCount
= 1;
19505 context
->sock
= inSock
;
19506 context
->userContext
= inUserContext
;
19508 *outContext
= context
;
19515 //===========================================================================================================================
19516 // SocketContextRetain
19517 //===========================================================================================================================
19519 static SocketContext
* SocketContextRetain( SocketContext
*inContext
)
19521 ++inContext
->refCount
;
19522 return( inContext
);
19525 //===========================================================================================================================
19526 // SocketContextRelease
19527 //===========================================================================================================================
19529 static void SocketContextRelease( SocketContext
*inContext
)
19531 if( --inContext
->refCount
== 0 )
19533 ForgetSocket( &inContext
->sock
);
19538 //===========================================================================================================================
19539 // SocketContextCancelHandler
19540 //===========================================================================================================================
19542 static void SocketContextCancelHandler( void *inContext
)
19544 SocketContextRelease( (SocketContext
*) inContext
);
19547 //===========================================================================================================================
19549 //===========================================================================================================================
19551 static OSStatus
StringToInt32( const char *inString
, int32_t *outValue
)
19557 value
= strtol( inString
, &endPtr
, 0 );
19558 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
19559 require_action_quiet( ( value
>= INT32_MIN
) && ( value
<= INT32_MAX
), exit
, err
= kRangeErr
);
19561 *outValue
= (int32_t) value
;
19568 //===========================================================================================================================
19570 //===========================================================================================================================
19572 static OSStatus
StringToUInt32( const char *inString
, uint32_t *outValue
)
19578 value
= (uint32_t) strtol( inString
, &endPtr
, 0 );
19579 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
19588 #if( TARGET_OS_DARWIN )
19589 //===========================================================================================================================
19591 //===========================================================================================================================
19593 static int64_t _StringToInt64( const char *inString
, OSStatus
*outError
)
19600 set_errno_compat( 0 );
19601 val
= strtoll( inString
, &end
, 0 );
19602 errnoVal
= errno_compat();
19604 require_action_quiet( ( *end
== '\0' ) && ( end
!= inString
), exit
, err
= kMalformedErr
);
19605 require_action_quiet( ( ( val
!= LLONG_MIN
) && ( val
!= LLONG_MAX
) ) || ( errnoVal
!= ERANGE
), exit
, err
= kRangeErr
);
19606 require_action_quiet( ( val
>= INT64_MIN
) && ( val
<= INT64_MAX
), exit
, err
= kRangeErr
);
19610 if( outError
) *outError
= err
;
19611 return( (int64_t)val
);
19614 //===========================================================================================================================
19616 //===========================================================================================================================
19618 static uint64_t _StringToUInt64( const char *inString
, OSStatus
*outError
)
19621 unsigned long long val
;
19625 set_errno_compat( 0 );
19626 val
= strtoull( inString
, &end
, 0 );
19627 errnoVal
= errno_compat();
19629 require_action_quiet( ( *end
== '\0' ) && ( end
!= inString
), exit
, err
= kMalformedErr
);
19630 require_action_quiet( ( val
!= ULLONG_MAX
) || ( errnoVal
!= ERANGE
), exit
, err
= kRangeErr
);
19631 require_action_quiet( val
<= UINT64_MAX
, exit
, err
= kRangeErr
);
19635 if( outError
) *outError
= err
;
19636 return( (uint64_t)val
);
19639 //===========================================================================================================================
19641 //===========================================================================================================================
19643 static pid_t
_StringToPID( const char *inString
, OSStatus
*outError
)
19648 val
= _StringToInt64( inString
, &err
);
19649 require_noerr_quiet( err
, exit
);
19650 require_action_quiet( val
== (pid_t
) val
, exit
, err
= kRangeErr
);
19654 if( outError
) *outError
= err
;
19655 return( (pid_t
) val
);
19658 //===========================================================================================================================
19659 // _ParseEscapedString
19661 // Note: Similar to ParseEscapedString() from CoreUtils except that _ParseEscapedString() takes an optional C string
19662 // containing delimiter characters instead of being limited to one delimiter character. Also, when the function returns
19663 // due to a delimiter, the output pointer is set to the delimiter character instead of the character after the delimiter.
19664 //===========================================================================================================================
19667 _ParseEscapedString(
19668 const char * inSrc
,
19669 const char * inEnd
,
19670 const char * inDelimiters
,
19673 size_t * outCopiedLen
,
19674 size_t * outActualLen
,
19675 const char ** outPtr
)
19679 char * dst
= inBufPtr
;
19680 const char * const lim
= ( inBufLen
> 0 ) ? &inBufPtr
[ inBufLen
- 1 ] : inBufPtr
;
19685 if( !inDelimiters
) inDelimiters
= "";
19686 while( ptr
< inEnd
)
19692 for( del
= inDelimiters
; ( *del
!= '\0' ) && ( c
!= *del
); ++del
) {}
19693 if( *del
!= '\0' ) break;
19697 require_action_quiet( ptr
< inEnd
, exit
, err
= kUnderrunErr
);
19701 if( dst
< lim
) *dst
++ = (char) c
;
19703 if( inBufLen
> 0 ) *dst
= '\0';
19705 if( outCopiedLen
) *outCopiedLen
= (size_t)( dst
- inBufPtr
);
19706 if( outActualLen
) *outActualLen
= len
;
19707 if( outPtr
) *outPtr
= ptr
;
19715 //===========================================================================================================================
19716 // StringToARecordData
19717 //===========================================================================================================================
19719 static OSStatus
StringToARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
19722 uint32_t * addrPtr
;
19723 const size_t addrLen
= sizeof( *addrPtr
);
19726 addrPtr
= (uint32_t *) malloc( addrLen
);
19727 require_action( addrPtr
, exit
, err
= kNoMemoryErr
);
19729 err
= _StringToIPv4Address( inString
, kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
, addrPtr
,
19730 NULL
, NULL
, NULL
, &end
);
19731 if( !err
&& ( *end
!= '\0' ) ) err
= kMalformedErr
;
19732 require_noerr_quiet( err
, exit
);
19734 *addrPtr
= HostToBig32( *addrPtr
);
19736 *outPtr
= (uint8_t *) addrPtr
;
19741 FreeNullSafe( addrPtr
);
19745 //===========================================================================================================================
19746 // StringToAAAARecordData
19747 //===========================================================================================================================
19749 static OSStatus
StringToAAAARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
19753 const size_t addrLen
= 16;
19756 addrPtr
= (uint8_t *) malloc( addrLen
);
19757 require_action( addrPtr
, exit
, err
= kNoMemoryErr
);
19759 err
= _StringToIPv6Address( inString
,
19760 kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
| kStringToIPAddressFlagsNoScope
,
19761 addrPtr
, NULL
, NULL
, NULL
, &end
);
19762 if( !err
&& ( *end
!= '\0' ) ) err
= kMalformedErr
;
19763 require_noerr_quiet( err
, exit
);
19770 FreeNullSafe( addrPtr
);
19774 //===========================================================================================================================
19775 // StringToDomainName
19776 //===========================================================================================================================
19778 static OSStatus
StringToDomainName( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
19784 uint8_t nameBuf
[ kDomainNameLengthMax
];
19786 err
= DomainNameFromString( nameBuf
, inString
, &end
);
19787 require_noerr_quiet( err
, exit
);
19789 nameLen
= (size_t)( end
- nameBuf
);
19790 namePtr
= _memdup( nameBuf
, nameLen
);
19791 require_action( namePtr
, exit
, err
= kNoMemoryErr
);
19795 if( outLen
) *outLen
= nameLen
;
19801 #if( TARGET_OS_DARWIN )
19802 //===========================================================================================================================
19803 // GetDefaultDNSServer
19804 //===========================================================================================================================
19806 static OSStatus
GetDefaultDNSServer( sockaddr_ip
*outAddr
)
19809 dns_config_t
* config
;
19810 struct sockaddr
* addr
;
19813 config
= dns_configuration_copy();
19814 require_action( config
, exit
, err
= kUnknownErr
);
19817 for( i
= 0; i
< config
->n_resolver
; ++i
)
19819 const dns_resolver_t
* const resolver
= config
->resolver
[ i
];
19821 if( !resolver
->domain
&& ( resolver
->n_nameserver
> 0 ) )
19823 addr
= resolver
->nameserver
[ 0 ];
19827 require_action_quiet( addr
, exit
, err
= kNotFoundErr
);
19829 SockAddrCopy( addr
, outAddr
);
19833 if( config
) dns_configuration_free( config
);
19838 //===========================================================================================================================
19839 // GetMDNSMulticastAddrV4
19840 //===========================================================================================================================
19842 static void _MDNSMulticastAddrV4Init( void *inContext
);
19844 static const struct sockaddr
* GetMDNSMulticastAddrV4( void )
19846 static struct sockaddr_in sMDNSMulticastAddrV4
;
19847 static dispatch_once_t sMDNSMulticastAddrV4InitOnce
= 0;
19849 dispatch_once_f( &sMDNSMulticastAddrV4InitOnce
, &sMDNSMulticastAddrV4
, _MDNSMulticastAddrV4Init
);
19850 return( (const struct sockaddr
*) &sMDNSMulticastAddrV4
);
19853 static void _MDNSMulticastAddrV4Init( void *inContext
)
19855 struct sockaddr_in
* const addr
= (struct sockaddr_in
*) inContext
;
19857 memset( addr
, 0, sizeof( *addr
) );
19858 SIN_LEN_SET( addr
);
19859 addr
->sin_family
= AF_INET
;
19860 addr
->sin_port
= htons( kMDNSPort
);
19861 addr
->sin_addr
.s_addr
= htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251
19864 //===========================================================================================================================
19865 // GetMDNSMulticastAddrV6
19866 //===========================================================================================================================
19868 static void _MDNSMulticastAddrV6Init( void *inContext
);
19870 static const struct sockaddr
* GetMDNSMulticastAddrV6( void )
19872 static struct sockaddr_in6 sMDNSMulticastAddrV6
;
19873 static dispatch_once_t sMDNSMulticastAddrV6InitOnce
= 0;
19875 dispatch_once_f( &sMDNSMulticastAddrV6InitOnce
, &sMDNSMulticastAddrV6
, _MDNSMulticastAddrV6Init
);
19876 return( (const struct sockaddr
*) &sMDNSMulticastAddrV6
);
19879 static void _MDNSMulticastAddrV6Init( void *inContext
)
19881 struct sockaddr_in6
* const addr
= (struct sockaddr_in6
*) inContext
;
19883 memset( addr
, 0, sizeof( *addr
) );
19884 SIN6_LEN_SET( addr
);
19885 addr
->sin6_family
= AF_INET6
;
19886 addr
->sin6_port
= htons( kMDNSPort
);
19887 addr
->sin6_addr
.s6_addr
[ 0 ] = 0xFF; // The mDNS IPv6 multicast address is FF02::FB.
19888 addr
->sin6_addr
.s6_addr
[ 1 ] = 0x02;
19889 addr
->sin6_addr
.s6_addr
[ 15 ] = 0xFB;
19892 //===========================================================================================================================
19893 // CreateMulticastSocket
19894 //===========================================================================================================================
19897 CreateMulticastSocket(
19898 const struct sockaddr
* inAddr
,
19900 const char * inIfName
,
19901 uint32_t inIfIndex
,
19904 SocketRef
* outSock
)
19907 SocketRef sock
= kInvalidSocketRef
;
19908 const int family
= inAddr
->sa_family
;
19911 require_action_quiet( ( family
== AF_INET
) ||( family
== AF_INET6
), exit
, err
= kUnsupportedErr
);
19913 err
= ServerSocketOpen( family
, SOCK_DGRAM
, IPPROTO_UDP
, inPort
, &port
, kSocketBufferSize_DontSet
, &sock
);
19914 require_noerr_quiet( err
, exit
);
19916 err
= SocketSetMulticastInterface( sock
, inIfName
, inIfIndex
);
19917 require_noerr_quiet( err
, exit
);
19919 if( family
== AF_INET
)
19921 err
= setsockopt( sock
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *) &(uint8_t){ 1 }, (socklen_t
) sizeof( uint8_t ) );
19922 err
= map_socket_noerr_errno( sock
, err
);
19923 require_noerr_quiet( err
, exit
);
19927 err
= setsockopt( sock
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *) &(int){ 1 }, (socklen_t
) sizeof( int ) );
19928 err
= map_socket_noerr_errno( sock
, err
);
19929 require_noerr_quiet( err
, exit
);
19934 err
= SocketJoinMulticast( sock
, inAddr
, inIfName
, inIfIndex
);
19935 require_noerr_quiet( err
, exit
);
19938 if( outPort
) *outPort
= port
;
19940 sock
= kInvalidSocketRef
;
19943 ForgetSocket( &sock
);
19947 //===========================================================================================================================
19948 // DecimalTextToUInt32
19949 //===========================================================================================================================
19951 static OSStatus
DecimalTextToUInt32( const char *inSrc
, const char *inEnd
, uint32_t *outValue
, const char **outPtr
)
19955 const char * ptr
= inSrc
;
19957 require_action_quiet( ( ptr
< inEnd
) && isdigit_safe( *ptr
), exit
, err
= kMalformedErr
);
19959 value
= (uint64_t)( *ptr
++ - '0' );
19962 if( ( ptr
< inEnd
) && isdigit_safe( *ptr
) )
19964 err
= kMalformedErr
;
19970 while( ( ptr
< inEnd
) && isdigit_safe( *ptr
) )
19972 value
= ( value
* 10 ) + (uint64_t)( *ptr
++ - '0' );
19973 require_action_quiet( value
<= UINT32_MAX
, exit
, err
= kRangeErr
);
19977 *outValue
= (uint32_t) value
;
19978 if( outPtr
) *outPtr
= ptr
;
19985 //===========================================================================================================================
19986 // CheckIntegerArgument
19987 //===========================================================================================================================
19989 static OSStatus
CheckIntegerArgument( int inArgValue
, const char *inArgName
, int inMin
, int inMax
)
19991 if( ( inArgValue
>= inMin
) && ( inArgValue
<= inMax
) ) return( kNoErr
);
19993 FPrintF( stderr
, "error: Invalid %s: %d. Valid range is [%d, %d].\n", inArgName
, inArgValue
, inMin
, inMax
);
19994 return( kRangeErr
);
19997 //===========================================================================================================================
19998 // CheckDoubleArgument
19999 //===========================================================================================================================
20001 static OSStatus
CheckDoubleArgument( double inArgValue
, const char *inArgName
, double inMin
, double inMax
)
20003 if( ( inArgValue
>= inMin
) && ( inArgValue
<= inMax
) ) return( kNoErr
);
20005 FPrintF( stderr
, "error: Invalid %s: %.1f. Valid range is [%.1f, %.1f].\n", inArgName
, inArgValue
, inMin
, inMax
);
20006 return( kRangeErr
);
20009 //===========================================================================================================================
20011 //===========================================================================================================================
20013 static OSStatus
CheckRootUser( void )
20015 if( geteuid() == 0 ) return( kNoErr
);
20017 FPrintF( stderr
, "error: This command must to be run as root.\n" );
20018 return( kPermissionErr
);
20021 //===========================================================================================================================
20024 // Note: Based on systemf() from CoreUtils framework.
20025 //===========================================================================================================================
20027 extern char ** environ
;
20029 static OSStatus
SpawnCommand( pid_t
*outPID
, const char *inFormat
, ... )
20038 va_start( args
, inFormat
);
20039 VASPrintF( &command
, inFormat
, args
);
20041 require_action( command
, exit
, err
= kUnknownErr
);
20043 argv
[ 0 ] = "/bin/sh";
20045 argv
[ 2 ] = command
;
20047 err
= posix_spawn( &pid
, argv
[ 0 ], NULL
, NULL
, argv
, environ
);
20049 require_noerr_quiet( err
, exit
);
20051 if( outPID
) *outPID
= pid
;
20057 //===========================================================================================================================
20058 // OutputFormatFromArgString
20059 //===========================================================================================================================
20061 static OSStatus
OutputFormatFromArgString( const char *inArgString
, OutputFormatType
*outFormat
)
20064 OutputFormatType format
;
20066 format
= (OutputFormatType
) CLIArgToValue( "format", inArgString
, &err
,
20067 kOutputFormatStr_JSON
, kOutputFormatType_JSON
,
20068 kOutputFormatStr_XML
, kOutputFormatType_XML
,
20069 kOutputFormatStr_Binary
, kOutputFormatType_Binary
,
20071 if( outFormat
) *outFormat
= format
;
20075 //===========================================================================================================================
20076 // OutputPropertyList
20077 //===========================================================================================================================
20079 static OSStatus
OutputPropertyList( CFPropertyListRef inPList
, OutputFormatType inType
, const char *inOutputFilePath
)
20082 CFDataRef results
= NULL
;
20083 FILE * file
= NULL
;
20085 // Convert plist to a specific format.
20089 case kOutputFormatType_JSON
:
20090 results
= CFCreateJSONData( inPList
, kJSONFlags_None
, NULL
);
20091 require_action( results
, exit
, err
= kUnknownErr
);
20094 case kOutputFormatType_XML
:
20095 results
= CFPropertyListCreateData( NULL
, inPList
, kCFPropertyListXMLFormat_v1_0
, 0, NULL
);
20096 require_action( results
, exit
, err
= kUnknownErr
);
20099 case kOutputFormatType_Binary
:
20100 results
= CFPropertyListCreateData( NULL
, inPList
, kCFPropertyListBinaryFormat_v1_0
, 0, NULL
);
20101 require_action( results
, exit
, err
= kUnknownErr
);
20109 // Write formatted results to file or stdout.
20111 if( inOutputFilePath
)
20113 file
= fopen( inOutputFilePath
, "wb" );
20114 err
= map_global_value_errno( file
, file
);
20115 require_noerr( err
, exit
);
20122 err
= WriteANSIFile( file
, CFDataGetBytePtr( results
), (size_t) CFDataGetLength( results
) );
20123 require_noerr_quiet( err
, exit
);
20125 // Write a trailing newline for JSON-formatted results.
20127 if( inType
== kOutputFormatType_JSON
)
20129 err
= WriteANSIFile( file
, "\n", 1 );
20130 require_noerr_quiet( err
, exit
);
20134 if( file
&& ( file
!= stdout
) ) fclose( file
);
20135 CFReleaseNullSafe( results
);
20139 //===========================================================================================================================
20140 // CreateSRVRecordDataFromString
20141 //===========================================================================================================================
20143 static OSStatus
CreateSRVRecordDataFromString( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
20146 DataBuffer dataBuf
;
20150 uint8_t target
[ kDomainNameLengthMax
];
20152 DataBuffer_Init( &dataBuf
, NULL
, 0, ( 3 * 2 ) + kDomainNameLengthMax
);
20154 // Parse and set the priority, weight, and port values (all three are unsigned 16-bit values).
20157 for( i
= 0; i
< 3; ++i
)
20163 value
= strtol( ptr
, &next
, 0 );
20164 require_action_quiet( ( next
!= ptr
) && ( *next
== ',' ), exit
, err
= kMalformedErr
);
20165 require_action_quiet( ( value
>= 0 ) && ( value
<= UINT16_MAX
), exit
, err
= kRangeErr
);
20168 WriteBig16( buf
, value
);
20170 err
= DataBuffer_Append( &dataBuf
, buf
, sizeof( buf
) );
20171 require_noerr( err
, exit
);
20174 // Set the target domain name.
20176 err
= DomainNameFromString( target
, ptr
, &end
);
20177 require_noerr_quiet( err
, exit
);
20179 err
= DataBuffer_Append( &dataBuf
, target
, (size_t)( end
- target
) );
20180 require_noerr( err
, exit
);
20182 err
= DataBuffer_Detach( &dataBuf
, outPtr
, outLen
);
20183 require_noerr( err
, exit
);
20186 DataBuffer_Free( &dataBuf
);
20190 //===========================================================================================================================
20191 // CreateTXTRecordDataFromString
20192 //===========================================================================================================================
20194 static OSStatus
CreateTXTRecordDataFromString(const char *inString
, int inDelimiter
, uint8_t **outPtr
, size_t *outLen
)
20197 DataBuffer dataBuf
;
20199 uint8_t txtStr
[ 256 ]; // Buffer for single TXT string: 1 length byte + up to 255 bytes of data.
20201 DataBuffer_Init( &dataBuf
, NULL
, 0, kDNSRecordDataLengthMax
);
20206 uint8_t * dst
= &txtStr
[ 1 ];
20207 const uint8_t * const lim
= &txtStr
[ 256 ];
20210 while( *src
&& ( *src
!= inDelimiter
) )
20212 if( ( c
= *src
++ ) == '\\' )
20214 require_action_quiet( *src
!= '\0', exit
, err
= kUnderrunErr
);
20217 require_action_quiet( dst
< lim
, exit
, err
= kOverrunErr
);
20218 *dst
++ = (uint8_t) c
;
20220 txtStr
[ 0 ] = (uint8_t)( dst
- &txtStr
[ 1 ] );
20221 err
= DataBuffer_Append( &dataBuf
, txtStr
, 1 + txtStr
[ 0 ] );
20222 require_noerr( err
, exit
);
20224 if( *src
== '\0' ) break;
20228 err
= DataBuffer_Detach( &dataBuf
, outPtr
, outLen
);
20229 require_noerr( err
, exit
);
20232 DataBuffer_Free( &dataBuf
);
20236 //===========================================================================================================================
20237 // CreateNSECRecordData
20238 //===========================================================================================================================
20240 DECLARE_QSORT_NUMERIC_COMPARATOR( _QSortCmpUnsigned
);
20241 DEFINE_QSORT_NUMERIC_COMPARATOR( unsigned int, _QSortCmpUnsigned
)
20243 #define kNSECBitmapMaxLength 32 // 32 bytes (256 bits). See <https://tools.ietf.org/html/rfc4034#section-4.1.2>.
20246 CreateNSECRecordData(
20247 const uint8_t * inNextDomainName
,
20250 unsigned int inTypeCount
,
20255 DataBuffer rdataDB
;
20256 unsigned int * array
= NULL
;
20257 unsigned int i
, type
, maxBit
, currBlock
, bitmapLen
;
20258 uint8_t fields
[ 2 + kNSECBitmapMaxLength
];
20259 uint8_t * const bitmap
= &fields
[ 2 ];
20261 va_start( args
, inTypeCount
);
20262 DataBuffer_Init( &rdataDB
, NULL
, 0, kDNSRecordDataLengthMax
);
20264 // Append Next Domain Name.
20266 err
= DataBuffer_Append( &rdataDB
, inNextDomainName
, DomainNameLength( inNextDomainName
) );
20267 require_noerr( err
, exit
);
20269 // Append Type Bit Maps.
20272 memset( bitmap
, 0, kNSECBitmapMaxLength
);
20273 if( inTypeCount
> 0 )
20275 array
= (unsigned int *) malloc( inTypeCount
* sizeof_element( array
) );
20276 require_action( array
, exit
, err
= kNoMemoryErr
);
20278 for( i
= 0; i
< inTypeCount
; ++i
)
20280 type
= va_arg( args
, unsigned int );
20281 require_action_quiet( type
<= UINT16_MAX
, exit
, err
= kRangeErr
);
20284 qsort( array
, inTypeCount
, sizeof_element( array
), _QSortCmpUnsigned
);
20286 currBlock
= array
[ 0 ] / 256;
20287 for( i
= 0; i
< inTypeCount
; ++i
)
20289 const unsigned int block
= array
[ i
] / 256;
20290 const unsigned int bit
= array
[ i
] % 256;
20292 if( block
!= currBlock
)
20294 bitmapLen
= BitArray_MaxBytes( maxBit
+ 1 );
20295 fields
[ 0 ] = (uint8_t) currBlock
;
20296 fields
[ 1 ] = (uint8_t) bitmapLen
;
20298 err
= DataBuffer_Append( &rdataDB
, fields
, 2 + bitmapLen
);
20299 require_noerr( err
, exit
);
20303 memset( bitmap
, 0, bitmapLen
);
20305 BitArray_SetBit( bitmap
, bit
);
20306 if( bit
> maxBit
) maxBit
= bit
;
20314 bitmapLen
= BitArray_MaxBytes( maxBit
+ 1 );
20315 fields
[ 0 ] = (uint8_t) currBlock
;
20316 fields
[ 1 ] = (uint8_t) bitmapLen
;
20318 err
= DataBuffer_Append( &rdataDB
, fields
, 2 + bitmapLen
);
20319 require_noerr( err
, exit
);
20321 err
= DataBuffer_Detach( &rdataDB
, outPtr
, outLen
);
20322 require_noerr( err
, exit
);
20326 DataBuffer_Free( &rdataDB
);
20327 FreeNullSafe( array
);
20331 //===========================================================================================================================
20333 //===========================================================================================================================
20336 _AppendSOARecordData(
20338 const uint8_t * inMName
,
20339 const uint8_t * inRName
,
20341 uint32_t inRefresh
,
20344 uint32_t inMinimumTTL
,
20350 const uint8_t * inNamePtr
,
20355 const uint8_t * inMName
,
20356 const uint8_t * inRName
,
20358 uint32_t inRefresh
,
20361 uint32_t inMinimumTTL
,
20366 size_t rdlengthOffset
= 0;
20367 uint8_t * rdlengthPtr
;
20371 err
= _DataBuffer_AppendDNSRecord( inDB
, inNamePtr
, inNameLen
, inType
, inClass
, inTTL
, NULL
, 0 );
20372 require_noerr( err
, exit
);
20374 rdlengthOffset
= DataBuffer_GetLen( inDB
) - 2;
20377 err
= _AppendSOARecordData( inDB
, inMName
, inRName
, inSerial
, inRefresh
, inRetry
, inExpire
, inMinimumTTL
, &rdataLen
);
20378 require_noerr( err
, exit
);
20382 rdlengthPtr
= DataBuffer_GetPtr( inDB
) + rdlengthOffset
;
20383 WriteBig16( rdlengthPtr
, rdataLen
);
20386 if( outLen
) *outLen
= inNameLen
+ sizeof( dns_fixed_fields_record
) + rdataLen
;
20394 _AppendSOARecordData(
20396 const uint8_t * inMName
,
20397 const uint8_t * inRName
,
20399 uint32_t inRefresh
,
20402 uint32_t inMinimumTTL
,
20406 dns_fixed_fields_soa fields
;
20407 const size_t mnameLen
= DomainNameLength( inMName
);
20408 const size_t rnameLen
= DomainNameLength( inRName
);
20412 err
= DataBuffer_Append( inDB
, inMName
, mnameLen
);
20413 require_noerr( err
, exit
);
20415 err
= DataBuffer_Append( inDB
, inRName
, rnameLen
);
20416 require_noerr( err
, exit
);
20418 dns_fixed_fields_soa_init( &fields
, inSerial
, inRefresh
, inRetry
, inExpire
, inMinimumTTL
);
20419 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
20420 require_noerr( err
, exit
);
20422 if( outLen
) *outLen
= mnameLen
+ rnameLen
+ sizeof( fields
);
20429 //===========================================================================================================================
20430 // CreateSOARecordData
20431 //===========================================================================================================================
20434 CreateSOARecordData(
20435 const uint8_t * inMName
,
20436 const uint8_t * inRName
,
20438 uint32_t inRefresh
,
20441 uint32_t inMinimumTTL
,
20446 DataBuffer rdataDB
;
20448 DataBuffer_Init( &rdataDB
, NULL
, 0, kDNSRecordDataLengthMax
);
20450 err
= _AppendSOARecordData( &rdataDB
, inMName
, inRName
, inSerial
, inRefresh
, inRetry
, inExpire
, inMinimumTTL
, NULL
);
20451 require_noerr( err
, exit
);
20453 err
= DataBuffer_Detach( &rdataDB
, outPtr
, outLen
);
20454 require_noerr( err
, exit
);
20457 DataBuffer_Free( &rdataDB
);
20461 //===========================================================================================================================
20462 // _DataBuffer_AppendDNSQuestion
20463 //===========================================================================================================================
20466 _DataBuffer_AppendDNSQuestion(
20468 const uint8_t * inNamePtr
,
20474 dns_fixed_fields_question fields
;
20476 err
= DataBuffer_Append( inDB
, inNamePtr
, inNameLen
);
20477 require_noerr( err
, exit
);
20479 dns_fixed_fields_question_init( &fields
, inType
, inClass
);
20480 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
20481 require_noerr( err
, exit
);
20487 //===========================================================================================================================
20488 // _DataBuffer_AppendDNSRecord
20489 //===========================================================================================================================
20492 _DataBuffer_AppendDNSRecord(
20494 const uint8_t * inNamePtr
,
20499 const uint8_t * inRDataPtr
,
20500 size_t inRDataLen
)
20503 dns_fixed_fields_record fields
;
20505 require_action_quiet( inRDataLen
< kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
20507 err
= DataBuffer_Append( inDB
, inNamePtr
, inNameLen
);
20508 require_noerr( err
, exit
);
20510 dns_fixed_fields_record_init( &fields
, inType
, inClass
, inTTL
, (uint16_t) inRDataLen
);
20511 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
20512 require_noerr( err
, exit
);
20516 err
= DataBuffer_Append( inDB
, inRDataPtr
, inRDataLen
);
20517 require_noerr( err
, exit
);
20524 //===========================================================================================================================
20525 // _NanoTime64ToTimestamp
20526 //===========================================================================================================================
20528 static char * _NanoTime64ToTimestamp( NanoTime64 inTime
, char *inBuf
, size_t inMaxLen
)
20532 NanoTimeToTimeVal( inTime
, &tv
);
20533 return( MakeFractionalDateString( &tv
, inBuf
, inMaxLen
) );
20536 //===========================================================================================================================
20537 // _MDNSInterfaceListCreate
20538 //===========================================================================================================================
20540 static Boolean
_MDNSInterfaceIsBlacklisted( SocketRef inInfoSock
, const char *inIfName
);
20542 static OSStatus
_MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset
, size_t inItemSize
, MDNSInterfaceItem
**outList
)
20545 struct ifaddrs
* ifaList
;
20546 const struct ifaddrs
* ifa
;
20547 MDNSInterfaceItem
* interfaceList
;
20548 MDNSInterfaceItem
** ptr
;
20549 SocketRef infoSock
;
20552 interfaceList
= NULL
;
20553 infoSock
= kInvalidSocketRef
;
20554 if( inItemSize
== 0 ) inItemSize
= sizeof( MDNSInterfaceItem
);
20555 require_action_quiet( inItemSize
>= sizeof( MDNSInterfaceItem
), exit
, err
= kSizeErr
);
20557 infoSock
= socket( AF_INET
, SOCK_DGRAM
, 0 );
20558 err
= map_socket_creation_errno( infoSock
);
20559 require_noerr( err
, exit
);
20561 err
= getifaddrs( &ifaList
);
20562 err
= map_global_noerr_errno( err
);
20563 require_noerr( err
, exit
);
20565 ptr
= &interfaceList
;
20566 for( ifa
= ifaList
; ifa
; ifa
= ifa
->ifa_next
)
20568 MDNSInterfaceItem
* item
;
20570 const unsigned int flagsMask
= IFF_UP
| IFF_MULTICAST
| IFF_POINTOPOINT
;
20571 const unsigned int flagsNeeded
= IFF_UP
| IFF_MULTICAST
;
20573 if( ( ifa
->ifa_flags
& flagsMask
) != flagsNeeded
) continue;
20574 if( !ifa
->ifa_addr
|| !ifa
->ifa_name
) continue;
20575 family
= ifa
->ifa_addr
->sa_family
;
20576 if( ( family
!= AF_INET
) && ( family
!= AF_INET6
) ) continue;
20578 for( item
= interfaceList
; item
&& ( strcmp( item
->ifName
, ifa
->ifa_name
) != 0 ); item
= item
->next
) {}
20581 NetTransportType type
;
20583 const char * const ifName
= ifa
->ifa_name
;
20585 if( _MDNSInterfaceIsBlacklisted( infoSock
, ifName
) ) continue;
20586 err
= SocketGetInterfaceInfo( infoSock
, ifName
, NULL
, &ifIndex
, NULL
, NULL
, NULL
, NULL
, NULL
, &type
);
20587 require_noerr( err
, exit
);
20589 if( ifIndex
== 0 ) continue;
20590 if( type
== kNetTransportType_AWDL
)
20592 if( inSubset
== kMDNSInterfaceSubset_NonAWDL
) continue;
20596 if( inSubset
== kMDNSInterfaceSubset_AWDL
) continue;
20598 item
= (MDNSInterfaceItem
*) calloc( 1, inItemSize
);
20599 require_action( item
, exit
, err
= kNoMemoryErr
);
20604 item
->ifName
= strdup( ifName
);
20605 require_action( item
->ifName
, exit
, err
= kNoMemoryErr
);
20607 item
->ifIndex
= ifIndex
;
20608 if( type
== kNetTransportType_AWDL
) item
->isAWDL
= true;
20609 else if( type
== kNetTransportType_WiFi
) item
->isWiFi
= true;
20611 if( family
== AF_INET
) item
->hasIPv4
= true;
20612 else item
->hasIPv6
= true;
20614 require_action_quiet( interfaceList
, exit
, err
= kNotFoundErr
);
20618 *outList
= interfaceList
;
20619 interfaceList
= NULL
;
20623 if( ifaList
) freeifaddrs( ifaList
);
20624 _MDNSInterfaceListFree( interfaceList
);
20625 ForgetSocket( &infoSock
);
20629 static Boolean
_MDNSInterfaceIsBlacklisted( SocketRef inInfoSock
, const char *inIfName
)
20633 static const char * const kMDNSInterfacePrefixBlacklist
[] = { "llw" };
20636 // Check if the interface name's prefix matches the prefix blacklist.
20638 for( i
= 0; i
< (int) countof( kMDNSInterfacePrefixBlacklist
); ++i
)
20640 const char * const prefix
= kMDNSInterfacePrefixBlacklist
[ i
];
20642 if( strcmp_prefix( inIfName
, prefix
) == 0 )
20644 const char * ptr
= &inIfName
[ strlen( prefix
) ];
20646 while( isdigit_safe( *ptr
) ) ++ptr
;
20647 if( *ptr
== '\0' ) return( true );
20651 // Check if the interface is used for inter-(co)processor networking.
20653 memset( &ifr
, 0, sizeof( ifr
) );
20654 strlcpy( ifr
.ifr_name
, inIfName
, sizeof( ifr
.ifr_name
) );
20655 err
= ioctl( inInfoSock
, SIOCGIFFUNCTIONALTYPE
, &ifr
);
20656 err
= map_global_value_errno( err
!= -1, err
);
20657 if( !err
&& ( ifr
.ifr_functional_type
== IFRTYPE_FUNCTIONAL_INTCOPROC
) ) return( true );
20662 //===========================================================================================================================
20663 // _MDNSInterfaceListFree
20664 //===========================================================================================================================
20666 static void _MDNSInterfaceListFree( MDNSInterfaceItem
*inList
)
20668 MDNSInterfaceItem
* item
;
20670 while( ( item
= inList
) != NULL
)
20672 inList
= item
->next
;
20673 FreeNullSafe( item
->ifName
);
20678 //===========================================================================================================================
20679 // _MDNSInterfaceGetAny
20680 //===========================================================================================================================
20682 static OSStatus
_MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset
, char inNameBuf
[ IF_NAMESIZE
+ 1 ], uint32_t *outIndex
)
20685 MDNSInterfaceItem
* list
;
20686 const MDNSInterfaceItem
* item
;
20689 err
= _MDNSInterfaceListCreate( inSubset
, 0, &list
);
20690 require_noerr_quiet( err
, exit
);
20691 require_action_quiet( list
, exit
, err
= kNotFoundErr
);
20693 for( item
= list
; item
; item
= item
->next
)
20695 if( item
->hasIPv4
&& item
->hasIPv6
) break;
20697 if( !item
) item
= list
;
20698 if( inNameBuf
) strlcpy( inNameBuf
, item
->ifName
, IF_NAMESIZE
+ 1 );
20699 if( outIndex
) *outIndex
= item
->ifIndex
;
20702 _MDNSInterfaceListFree( list
);
20706 //===========================================================================================================================
20707 // _SetComputerName
20708 //===========================================================================================================================
20710 static OSStatus
_SetComputerName( CFStringRef inComputerName
, CFStringEncoding inEncoding
)
20713 SCPreferencesRef prefs
;
20716 prefs
= SCPreferencesCreateWithAuthorization( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
20717 err
= map_scerror( prefs
);
20718 require_noerr_quiet( err
, exit
);
20720 ok
= SCPreferencesSetComputerName( prefs
, inComputerName
, inEncoding
);
20721 err
= map_scerror( ok
);
20722 require_noerr_quiet( err
, exit
);
20724 ok
= SCPreferencesCommitChanges( prefs
);
20725 err
= map_scerror( ok
);
20726 require_noerr_quiet( err
, exit
);
20728 ok
= SCPreferencesApplyChanges( prefs
);
20729 err
= map_scerror( ok
);
20730 require_noerr_quiet( err
, exit
);
20733 CFReleaseNullSafe( prefs
);
20737 //===========================================================================================================================
20738 // _SetComputerNameWithUTF8CString
20739 //===========================================================================================================================
20741 static OSStatus
_SetComputerNameWithUTF8CString( const char *inComputerName
)
20744 CFStringRef computerName
;
20746 computerName
= CFStringCreateWithCString( NULL
, inComputerName
, kCFStringEncodingUTF8
);
20747 require_action( computerName
, exit
, err
= kNoMemoryErr
);
20749 err
= _SetComputerName( computerName
, kCFStringEncodingUTF8
);
20750 require_noerr_quiet( err
, exit
);
20753 CFReleaseNullSafe( computerName
);
20757 //===========================================================================================================================
20758 // _SetLocalHostName
20759 //===========================================================================================================================
20761 static OSStatus
_SetLocalHostName( CFStringRef inLocalHostName
)
20764 SCPreferencesRef prefs
;
20767 prefs
= SCPreferencesCreateWithAuthorization( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
20768 err
= map_scerror( prefs
);
20769 require_noerr_quiet( err
, exit
);
20771 ok
= SCPreferencesSetLocalHostName( prefs
, inLocalHostName
);
20772 err
= map_scerror( ok
);
20773 require_noerr_quiet( err
, exit
);
20775 ok
= SCPreferencesCommitChanges( prefs
);
20776 err
= map_scerror( ok
);
20777 require_noerr_quiet( err
, exit
);
20779 ok
= SCPreferencesApplyChanges( prefs
);
20780 err
= map_scerror( ok
);
20781 require_noerr_quiet( err
, exit
);
20784 CFReleaseNullSafe( prefs
);
20788 //===========================================================================================================================
20789 // _SetLocalHostNameWithUTF8CString
20790 //===========================================================================================================================
20792 static OSStatus
_SetLocalHostNameWithUTF8CString( const char *inLocalHostName
)
20795 CFStringRef localHostName
;
20797 localHostName
= CFStringCreateWithCString( NULL
, inLocalHostName
, kCFStringEncodingUTF8
);
20798 require_action( localHostName
, exit
, err
= kNoMemoryErr
);
20800 err
= _SetLocalHostName( localHostName
);
20801 require_noerr_quiet( err
, exit
);
20804 CFReleaseNullSafe( localHostName
);
20808 //===========================================================================================================================
20809 // MDNSColliderCreate
20810 //===========================================================================================================================
20814 kMDNSColliderOpCode_Invalid
= 0,
20815 kMDNSColliderOpCode_Send
= 1,
20816 kMDNSColliderOpCode_Wait
= 2,
20817 kMDNSColliderOpCode_SetProbeActions
= 3,
20818 kMDNSColliderOpCode_LoopPush
= 4,
20819 kMDNSColliderOpCode_LoopPop
= 5,
20820 kMDNSColliderOpCode_Exit
= 6
20822 } MDNSColliderOpCode
;
20826 MDNSColliderOpCode opcode
;
20829 } MDNSCInstruction
;
20831 #define kMaxLoopDepth 16
20833 struct MDNSColliderPrivate
20835 CFRuntimeBase base
; // CF object base.
20836 dispatch_queue_t queue
; // Queue for collider's events.
20837 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
20838 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
20839 SocketRef sockV4
; // IPv4 UDP socket for mDNS.
20840 SocketRef sockV6
; // IPv6 UDP socket for mDNS.
20841 uint8_t * target
; // Record name being targeted. (malloced)
20842 uint8_t * responsePtr
; // Response message pointer. (malloced)
20843 size_t responseLen
; // Response message length.
20844 uint8_t * probePtr
; // Probe query message pointer. (malloced)
20845 size_t probeLen
; // Probe query message length.
20846 unsigned int probeCount
; // Count of probe queries received for collider's record.
20847 uint32_t probeActionMap
; // Bitmap of actions to take for
20848 MDNSCInstruction
* program
; // Program to execute.
20849 uint32_t pc
; // Program's program counter.
20850 uint32_t loopCounts
[ kMaxLoopDepth
]; // Stack of loop counters.
20851 uint32_t loopDepth
; // Current loop depth.
20852 dispatch_source_t waitTimer
; // Timer for program's wait commands.
20853 uint32_t interfaceIndex
; // Interface over which to send and receive mDNS msgs.
20854 MDNSColliderStopHandler_f stopHandler
; // User's stop handler.
20855 void * stopContext
; // User's stop handler context.
20856 MDNSColliderProtocols protocols
; // Protocols to use, i.e., IPv4, IPv6.
20857 Boolean stopped
; // True if the collider has been stopped.
20858 uint8_t msgBuf
[ kMDNSMessageSizeMax
]; // mDNS message buffer.
20861 static void _MDNSColliderStop( MDNSColliderRef inCollider
, OSStatus inError
);
20862 static void _MDNSColliderReadHandler( void *inContext
);
20863 static void _MDNSColliderExecuteProgram( void *inContext
);
20864 static OSStatus
_MDNSColliderSendResponse( MDNSColliderRef inCollider
, SocketRef inSock
, const struct sockaddr
*inDest
);
20865 static OSStatus
_MDNSColliderSendProbe( MDNSColliderRef inCollider
, SocketRef inSock
, const struct sockaddr
*inDest
);
20867 CF_CLASS_DEFINE( MDNSCollider
);
20869 ulog_define_ex( kDNSSDUtilIdentifier
, MDNSCollider
, kLogLevelInfo
, kLogFlags_None
, "MDNSCollider", NULL
);
20870 #define mc_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSCollider ), (LEVEL), __VA_ARGS__ )
20872 static OSStatus
MDNSColliderCreate( dispatch_queue_t inQueue
, MDNSColliderRef
*outCollider
)
20875 MDNSColliderRef obj
= NULL
;
20877 CF_OBJECT_CREATE( MDNSCollider
, obj
, err
, exit
);
20879 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
20880 obj
->sockV4
= kInvalidSocketRef
;
20881 obj
->sockV6
= kInvalidSocketRef
;
20883 *outCollider
= obj
;
20890 //===========================================================================================================================
20891 // _MDNSColliderFinalize
20892 //===========================================================================================================================
20894 static void _MDNSColliderFinalize( CFTypeRef inObj
)
20896 MDNSColliderRef
const me
= (MDNSColliderRef
) inObj
;
20898 check( !me
->waitTimer
);
20899 check( !me
->readSourceV4
);
20900 check( !me
->readSourceV6
);
20901 check( !IsValidSocket( me
->sockV4
) );
20902 check( !IsValidSocket( me
->sockV6
) );
20903 ForgetMem( &me
->target
);
20904 ForgetMem( &me
->responsePtr
);
20905 ForgetMem( &me
->probePtr
);
20906 ForgetMem( &me
->program
);
20907 dispatch_forget( &me
->queue
);
20910 //===========================================================================================================================
20911 // MDNSColliderStart
20912 //===========================================================================================================================
20914 static void _MDNSColliderStart( void *inContext
);
20916 static OSStatus
MDNSColliderStart( MDNSColliderRef me
)
20920 require_action_quiet( me
->target
, exit
, err
= kNotPreparedErr
);
20921 require_action_quiet( me
->responsePtr
, exit
, err
= kNotPreparedErr
);
20922 require_action_quiet( me
->probePtr
, exit
, err
= kNotPreparedErr
);
20923 require_action_quiet( me
->program
, exit
, err
= kNotPreparedErr
);
20924 require_action_quiet( me
->interfaceIndex
, exit
, err
= kNotPreparedErr
);
20925 require_action_quiet( me
->protocols
, exit
, err
= kNotPreparedErr
);
20928 dispatch_async_f( me
->queue
, me
, _MDNSColliderStart
);
20935 static void _MDNSColliderStart( void *inContext
)
20938 MDNSColliderRef
const me
= (MDNSColliderRef
) inContext
;
20939 SocketRef sock
= kInvalidSocketRef
;
20940 SocketContext
* sockCtx
= NULL
;
20942 if( me
->protocols
& kMDNSColliderProtocol_IPv4
)
20944 err
= CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort
, NULL
, me
->interfaceIndex
, true, NULL
, &sock
);
20945 require_noerr( err
, exit
);
20947 err
= SocketContextCreate( sock
, me
, &sockCtx
);
20948 require_noerr( err
, exit
);
20949 sock
= kInvalidSocketRef
;
20951 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _MDNSColliderReadHandler
, SocketContextCancelHandler
,
20952 sockCtx
, &me
->readSourceV4
);
20953 require_noerr( err
, exit
);
20954 me
->sockV4
= sockCtx
->sock
;
20957 dispatch_resume( me
->readSourceV4
);
20960 if( me
->protocols
& kMDNSColliderProtocol_IPv6
)
20962 err
= CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort
, NULL
, me
->interfaceIndex
, true, NULL
, &sock
);
20963 require_noerr( err
, exit
);
20965 err
= SocketContextCreate( sock
, me
, &sockCtx
);
20966 require_noerr( err
, exit
);
20967 sock
= kInvalidSocketRef
;
20969 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _MDNSColliderReadHandler
, SocketContextCancelHandler
,
20970 sockCtx
, &me
->readSourceV6
);
20971 require_noerr( err
, exit
);
20972 me
->sockV6
= sockCtx
->sock
;
20975 dispatch_resume( me
->readSourceV6
);
20978 _MDNSColliderExecuteProgram( me
);
20982 ForgetSocket( &sock
);
20983 ForgetSocketContext( &sockCtx
);
20984 if( err
) _MDNSColliderStop( me
, err
);
20987 //===========================================================================================================================
20988 // MDNSColliderStop
20989 //===========================================================================================================================
20991 static void _MDNSColliderUserStop( void *inContext
);
20993 static void MDNSColliderStop( MDNSColliderRef me
)
20996 dispatch_async_f( me
->queue
, me
, _MDNSColliderUserStop
);
20999 static void _MDNSColliderUserStop( void *inContext
)
21001 MDNSColliderRef
const me
= (MDNSColliderRef
) inContext
;
21003 _MDNSColliderStop( me
, kCanceledErr
);
21007 //===========================================================================================================================
21008 // MDNSColliderSetProtocols
21009 //===========================================================================================================================
21011 static void MDNSColliderSetProtocols( MDNSColliderRef me
, MDNSColliderProtocols inProtocols
)
21013 me
->protocols
= inProtocols
;
21016 //===========================================================================================================================
21017 // MDNSColliderSetInterfaceIndex
21018 //===========================================================================================================================
21020 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef me
, uint32_t inInterfaceIndex
)
21022 me
->interfaceIndex
= inInterfaceIndex
;
21025 //===========================================================================================================================
21026 // MDNSColliderSetProgram
21027 //===========================================================================================================================
21029 #define kMDNSColliderProgCmd_Done "done"
21030 #define kMDNSColliderProgCmd_Loop "loop"
21031 #define kMDNSColliderProgCmd_Send "send"
21032 #define kMDNSColliderProgCmd_Probes "probes"
21033 #define kMDNSColliderProgCmd_Wait "wait"
21035 typedef uint32_t MDNSColliderProbeAction
;
21037 #define kMDNSColliderProbeAction_None 0
21038 #define kMDNSColliderProbeAction_Respond 1
21039 #define kMDNSColliderProbeAction_RespondUnicast 2
21040 #define kMDNSColliderProbeAction_RespondMulticast 3
21041 #define kMDNSColliderProbeAction_Probe 4
21042 #define kMDNSColliderProbeAction_MaxValue kMDNSColliderProbeAction_Probe
21044 #define kMDNSColliderProbeActionBits_Count 3
21045 #define kMDNSColliderProbeActionBits_Mask ( ( 1U << kMDNSColliderProbeActionBits_Count ) - 1 )
21046 #define kMDNSColliderProbeActionMaxProbeCount ( 32 / kMDNSColliderProbeActionBits_Count )
21048 check_compile_time( kMDNSColliderProbeAction_MaxValue
<= kMDNSColliderProbeActionBits_Mask
);
21050 static OSStatus
_MDNSColliderParseProbeActionString( const char *inString
, size_t inLen
, uint32_t *outBitmap
);
21052 static OSStatus
MDNSColliderSetProgram( MDNSColliderRef me
, const char *inProgramStr
)
21056 unsigned int loopDepth
;
21060 MDNSCInstruction
* program
= NULL
;
21061 uint32_t loopStart
[ kMaxLoopDepth
];
21064 for( cmd
= inProgramStr
; *cmd
; cmd
= next
)
21066 for( end
= cmd
; *end
&& ( *end
!= ';' ); ++end
) {}
21067 require_action_quiet( end
!= cmd
, exit
, err
= kMalformedErr
);
21068 next
= ( *end
== ';' ) ? ( end
+ 1 ) : end
;
21072 program
= (MDNSCInstruction
*) calloc( insCount
+ 1, sizeof( *program
) );
21073 require_action( program
, exit
, err
= kNoMemoryErr
);
21077 for( cmd
= inProgramStr
; *cmd
; cmd
= next
)
21084 MDNSCInstruction
* const ins
= &program
[ insCount
];
21086 while( isspace_safe( *cmd
) ) ++cmd
;
21087 for( end
= cmd
; *end
&& ( *end
!= ';' ); ++end
) {}
21088 next
= ( *end
== ';' ) ? ( end
+ 1 ) : end
;
21090 for( ptr
= cmd
; ( ptr
< end
) && !isspace_safe( *ptr
); ++ptr
) {}
21091 cmdLen
= (size_t)( ptr
- cmd
);
21095 if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Done
) == 0 )
21097 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
21098 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
21100 require_action_quiet( loopDepth
> 0, exit
, err
= kMalformedErr
);
21102 ins
->opcode
= kMDNSColliderOpCode_LoopPop
;
21103 ins
->operand
= loopStart
[ --loopDepth
];
21108 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Loop
) == 0 )
21110 for( arg
= ptr
; ( arg
< end
) && isspace_safe( *arg
); ++arg
) {}
21111 err
= DecimalTextToUInt32( arg
, end
, &value
, &ptr
);
21112 require_noerr_quiet( err
, exit
);
21113 require_action_quiet( value
> 0, exit
, err
= kValueErr
);
21115 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
21116 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
21118 ins
->opcode
= kMDNSColliderOpCode_LoopPush
;
21119 ins
->operand
= value
;
21121 require_action_quiet( loopDepth
< kMaxLoopDepth
, exit
, err
= kNoSpaceErr
);
21122 loopStart
[ loopDepth
++ ] = insCount
+ 1;
21127 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Probes
) == 0 )
21129 for( arg
= ptr
; ( arg
< end
) && isspace_safe( *arg
); ++arg
) {}
21130 for( ptr
= arg
; ( ptr
< end
) && !isspace_safe( *ptr
); ++ptr
) {}
21131 argLen
= (size_t)( ptr
- arg
);
21134 err
= _MDNSColliderParseProbeActionString( arg
, argLen
, &value
);
21135 require_noerr_quiet( err
, exit
);
21142 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
21143 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
21145 ins
->opcode
= kMDNSColliderOpCode_SetProbeActions
;
21146 ins
->operand
= value
;
21151 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Send
) == 0 )
21153 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
21154 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
21156 ins
->opcode
= kMDNSColliderOpCode_Send
;
21161 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Wait
) == 0 )
21163 for( arg
= ptr
; ( arg
< end
) && isspace_safe( *arg
); ++arg
) {}
21164 err
= DecimalTextToUInt32( arg
, end
, &value
, &ptr
);
21165 require_noerr_quiet( err
, exit
);
21167 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
21168 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
21170 ins
->opcode
= kMDNSColliderOpCode_Wait
;
21171 ins
->operand
= value
;
21174 // Unrecognized command
21183 require_action_quiet( loopDepth
== 0, exit
, err
= kMalformedErr
);
21185 program
[ insCount
].opcode
= kMDNSColliderOpCode_Exit
;
21187 FreeNullSafe( me
->program
);
21188 me
->program
= program
;
21193 FreeNullSafe( program
);
21197 static OSStatus
_MDNSColliderParseProbeActionString( const char *inString
, size_t inLen
, uint32_t *outBitmap
)
21201 const char * const end
= &inString
[ inLen
];
21211 MDNSColliderProbeAction action
;
21214 if( isdigit_safe( c
) )
21219 count
= ( count
* 10 ) + ( c
- '0' );
21220 require_action_quiet( count
<= ( kMDNSColliderProbeActionMaxProbeCount
- index
), exit
, err
= kCountErr
);
21221 require_action_quiet( ptr
< end
, exit
, err
= kUnderrunErr
);
21224 } while( isdigit_safe( c
) );
21225 require_action_quiet( count
> 0, exit
, err
= kCountErr
);
21229 require_action_quiet( index
< kMDNSColliderProbeActionMaxProbeCount
, exit
, err
= kMalformedErr
);
21235 case 'n': action
= kMDNSColliderProbeAction_None
; break;
21236 case 'r': action
= kMDNSColliderProbeAction_Respond
; break;
21237 case 'u': action
= kMDNSColliderProbeAction_RespondUnicast
; break;
21238 case 'm': action
= kMDNSColliderProbeAction_RespondMulticast
; break;
21239 case 'p': action
= kMDNSColliderProbeAction_Probe
; break;
21240 default: err
= kMalformedErr
; goto exit
;
21245 require_action_quiet( ( c
== '-' ) && ( ptr
< end
), exit
, err
= kMalformedErr
);
21247 while( count
-- > 0 )
21249 bitmap
|= ( action
<< ( index
* kMDNSColliderProbeActionBits_Count
) );
21254 *outBitmap
= bitmap
;
21261 //===========================================================================================================================
21262 // MDNSColliderSetStopHandler
21263 //===========================================================================================================================
21265 static void MDNSColliderSetStopHandler( MDNSColliderRef me
, MDNSColliderStopHandler_f inStopHandler
, void *inStopContext
)
21267 me
->stopHandler
= inStopHandler
;
21268 me
->stopContext
= inStopContext
;
21271 //===========================================================================================================================
21272 // MDNSColliderSetRecord
21273 //===========================================================================================================================
21275 #define kMDNSColliderDummyStr "\x16" "mdnscollider-sent-this" "\x05" "local"
21276 #define kMDNSColliderDummyName ( (const uint8_t *) kMDNSColliderDummyStr )
21277 #define kMDNSColliderDummyNameLen sizeof( kMDNSColliderDummyStr )
21280 MDNSColliderSetRecord(
21281 MDNSColliderRef me
,
21282 const uint8_t * inName
,
21284 const void * inRDataPtr
,
21285 size_t inRDataLen
)
21290 uint8_t * targetPtr
= NULL
;
21292 uint8_t * responsePtr
= NULL
;
21293 size_t responseLen
;
21294 uint8_t * probePtr
= NULL
;
21297 DataBuffer_Init( &msgDB
, NULL
, 0, kMDNSMessageSizeMax
);
21299 err
= DomainNameDup( inName
, &targetPtr
, &targetLen
);
21300 require_noerr_quiet( err
, exit
);
21302 // Create response message.
21304 memset( &header
, 0, sizeof( header
) );
21305 DNSHeaderSetFlags( &header
, kDNSHeaderFlag_Response
| kDNSHeaderFlag_AuthAnswer
);
21306 DNSHeaderSetAnswerCount( &header
, 1 );
21308 err
= DataBuffer_Append( &msgDB
, &header
, sizeof( header
) );
21309 require_noerr( err
, exit
);
21311 err
= _DataBuffer_AppendDNSRecord( &msgDB
, targetPtr
, targetLen
, inType
, kDNSServiceClass_IN
| kRRClassCacheFlushBit
,
21312 1976, inRDataPtr
, inRDataLen
);
21313 require_noerr( err
, exit
);
21315 err
= DataBuffer_Detach( &msgDB
, &responsePtr
, &responseLen
);
21316 require_noerr( err
, exit
);
21318 // Create probe message.
21320 memset( &header
, 0, sizeof( header
) );
21321 DNSHeaderSetQuestionCount( &header
, 2 );
21322 DNSHeaderSetAuthorityCount( &header
, 1 );
21324 err
= DataBuffer_Append( &msgDB
, &header
, sizeof( header
) );
21325 require_noerr( err
, exit
);
21327 err
= _DataBuffer_AppendDNSQuestion( &msgDB
, targetPtr
, targetLen
, kDNSServiceType_ANY
, kDNSServiceClass_IN
);
21328 require_noerr( err
, exit
);
21330 err
= _DataBuffer_AppendDNSQuestion( &msgDB
, kMDNSColliderDummyName
, kMDNSColliderDummyNameLen
,
21331 kDNSServiceType_NULL
, kDNSServiceClass_IN
);
21332 require_noerr( err
, exit
);
21334 err
= _DataBuffer_AppendDNSRecord( &msgDB
, targetPtr
, targetLen
, inType
, kDNSServiceClass_IN
,
21335 1976, inRDataPtr
, inRDataLen
);
21336 require_noerr( err
, exit
);
21338 err
= DataBuffer_Detach( &msgDB
, &probePtr
, &probeLen
);
21339 require_noerr( err
, exit
);
21341 FreeNullSafe( me
->target
);
21342 me
->target
= targetPtr
;
21345 FreeNullSafe( me
->responsePtr
);
21346 me
->responsePtr
= responsePtr
;
21347 me
->responseLen
= responseLen
;
21348 responsePtr
= NULL
;
21350 FreeNullSafe( me
->probePtr
);
21351 me
->probePtr
= probePtr
;
21352 me
->probeLen
= probeLen
;
21356 DataBuffer_Free( &msgDB
);
21357 FreeNullSafe( targetPtr
);
21358 FreeNullSafe( responsePtr
);
21359 FreeNullSafe( probePtr
);
21363 //===========================================================================================================================
21364 // _MDNSColliderStop
21365 //===========================================================================================================================
21367 static void _MDNSColliderStop( MDNSColliderRef me
, OSStatus inError
)
21369 dispatch_source_forget( &me
->waitTimer
);
21370 dispatch_source_forget( &me
->readSourceV4
);
21371 dispatch_source_forget( &me
->readSourceV6
);
21372 me
->sockV4
= kInvalidSocketRef
;
21373 me
->sockV6
= kInvalidSocketRef
;
21377 me
->stopped
= true;
21378 if( me
->stopHandler
) me
->stopHandler( me
->stopContext
, inError
);
21383 //===========================================================================================================================
21384 // _MDNSColliderReadHandler
21385 //===========================================================================================================================
21387 static MDNSColliderProbeAction
_MDNSColliderGetProbeAction( uint32_t inBitmap
, unsigned int inProbeNumber
);
21388 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction
);
21390 static void _MDNSColliderReadHandler( void *inContext
)
21393 struct timeval now
;
21394 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
21395 MDNSColliderRef
const me
= (MDNSColliderRef
) sockCtx
->userContext
;
21397 sockaddr_ip sender
;
21398 const DNSHeader
* hdr
;
21399 const uint8_t * ptr
;
21400 const struct sockaddr
* dest
;
21401 int probeFound
, probeIsQU
;
21402 unsigned int qCount
, i
;
21403 MDNSColliderProbeAction action
;
21405 gettimeofday( &now
, NULL
);
21407 err
= SocketRecvFrom( sockCtx
->sock
, me
->msgBuf
, sizeof( me
->msgBuf
), &msgLen
, &sender
, sizeof( sender
),
21408 NULL
, NULL
, NULL
, NULL
);
21409 require_noerr( err
, exit
);
21411 require_quiet( msgLen
>= kDNSHeaderLength
, exit
);
21412 hdr
= (const DNSHeader
*) me
->msgBuf
;
21414 probeFound
= false;
21416 qCount
= DNSHeaderGetQuestionCount( hdr
);
21417 ptr
= (const uint8_t *) &hdr
[ 1 ];
21418 for( i
= 0; i
< qCount
; ++i
)
21420 uint16_t qtype
, qclass
;
21421 uint8_t qname
[ kDomainNameLengthMax
];
21423 err
= DNSMessageExtractQuestion( me
->msgBuf
, msgLen
, ptr
, qname
, &qtype
, &qclass
, &ptr
);
21424 require_noerr_quiet( err
, exit
);
21426 if( ( qtype
== kDNSServiceType_NULL
) && ( qclass
== kDNSServiceClass_IN
) &&
21427 DomainNameEqual( qname
, kMDNSColliderDummyName
) )
21429 probeFound
= false;
21433 if( qtype
!= kDNSServiceType_ANY
) continue;
21434 if( ( qclass
& ~kQClassUnicastResponseBit
) != kDNSServiceClass_IN
) continue;
21435 if( !DomainNameEqual( qname
, me
->target
) ) continue;
21440 probeIsQU
= ( qclass
& kQClassUnicastResponseBit
) ? true : false;
21443 require_quiet( probeFound
, exit
);
21446 action
= _MDNSColliderGetProbeAction( me
->probeActionMap
, me
->probeCount
);
21448 mc_ulog( kLogLevelInfo
, "Received probe from %##a at %{du:time} (action: %s):\n\n%#1{du:dnsmsg}",
21449 &sender
, &now
, _MDNSColliderProbeActionToString( action
), me
->msgBuf
, msgLen
);
21451 if( ( action
== kMDNSColliderProbeAction_Respond
) ||
21452 ( action
== kMDNSColliderProbeAction_RespondUnicast
) ||
21453 ( action
== kMDNSColliderProbeAction_RespondMulticast
) )
21455 if( ( ( action
== kMDNSColliderProbeAction_Respond
) && probeIsQU
) ||
21456 ( action
== kMDNSColliderProbeAction_RespondUnicast
) )
21460 else if( ( ( action
== kMDNSColliderProbeAction_Respond
) && !probeIsQU
) ||
21461 ( action
== kMDNSColliderProbeAction_RespondMulticast
) )
21463 dest
= ( sender
.sa
.sa_family
== AF_INET
) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
21466 err
= _MDNSColliderSendResponse( me
, sockCtx
->sock
, dest
);
21467 require_noerr( err
, exit
);
21469 else if( action
== kMDNSColliderProbeAction_Probe
)
21471 dest
= ( sender
.sa
.sa_family
== AF_INET
) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
21473 err
= _MDNSColliderSendProbe( me
, sockCtx
->sock
, dest
);
21474 require_noerr( err
, exit
);
21481 static MDNSColliderProbeAction
_MDNSColliderGetProbeAction( uint32_t inBitmap
, unsigned int inProbeNumber
)
21483 MDNSColliderProbeAction action
;
21485 if( ( inProbeNumber
>= 1 ) && ( inProbeNumber
<= kMDNSColliderProbeActionMaxProbeCount
) )
21487 action
= ( inBitmap
>> ( ( inProbeNumber
- 1 ) * kMDNSColliderProbeActionBits_Count
) ) &
21488 kMDNSColliderProbeActionBits_Mask
;
21492 action
= kMDNSColliderProbeAction_None
;
21497 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction
)
21501 case kMDNSColliderProbeAction_None
: return( "None" );
21502 case kMDNSColliderProbeAction_Respond
: return( "Respond" );
21503 case kMDNSColliderProbeAction_RespondUnicast
: return( "Respond (unicast)" );
21504 case kMDNSColliderProbeAction_RespondMulticast
: return( "Respond (multicast)" );
21505 case kMDNSColliderProbeAction_Probe
: return( "Probe" );
21506 default: return( "???" );
21510 //===========================================================================================================================
21511 // _MDNSColliderExecuteProgram
21512 //===========================================================================================================================
21514 static void _MDNSColliderExecuteProgram( void *inContext
)
21517 MDNSColliderRef
const me
= (MDNSColliderRef
) inContext
;
21520 dispatch_forget( &me
->waitTimer
);
21525 const MDNSCInstruction
* const ins
= &me
->program
[ me
->pc
++ ];
21528 switch( ins
->opcode
)
21530 case kMDNSColliderOpCode_Send
:
21531 if( IsValidSocket( me
->sockV4
) )
21533 err
= _MDNSColliderSendResponse( me
, me
->sockV4
, GetMDNSMulticastAddrV4() );
21534 require_noerr( err
, exit
);
21536 if( IsValidSocket( me
->sockV6
) )
21538 err
= _MDNSColliderSendResponse( me
, me
->sockV6
, GetMDNSMulticastAddrV6() );
21539 require_noerr( err
, exit
);
21543 case kMDNSColliderOpCode_Wait
:
21544 waitMs
= ins
->operand
;
21547 err
= DispatchTimerOneShotCreate( dispatch_time_milliseconds( waitMs
), 1, me
->queue
,
21548 _MDNSColliderExecuteProgram
, me
, &me
->waitTimer
);
21549 require_noerr( err
, exit
);
21550 dispatch_resume( me
->waitTimer
);
21555 case kMDNSColliderOpCode_SetProbeActions
:
21556 me
->probeCount
= 0;
21557 me
->probeActionMap
= ins
->operand
;
21560 case kMDNSColliderOpCode_LoopPush
:
21561 check( me
->loopDepth
< kMaxLoopDepth
);
21562 me
->loopCounts
[ me
->loopDepth
++ ] = ins
->operand
;
21565 case kMDNSColliderOpCode_LoopPop
:
21566 check( me
->loopDepth
> 0 );
21567 if( --me
->loopCounts
[ me
->loopDepth
- 1 ] > 0 )
21569 me
->pc
= ins
->operand
;
21577 case kMDNSColliderOpCode_Exit
:
21583 dlogassert( "Unhandled opcode %u\n", ins
->opcode
);
21590 if( err
|| stop
) _MDNSColliderStop( me
, err
);
21593 //===========================================================================================================================
21594 // _MDNSColliderSendResponse
21595 //===========================================================================================================================
21597 static OSStatus
_MDNSColliderSendResponse( MDNSColliderRef me
, SocketRef inSock
, const struct sockaddr
*inDest
)
21602 n
= sendto( inSock
, (char *) me
->responsePtr
, me
->responseLen
, 0, inDest
, SockAddrGetSize( inDest
) );
21603 err
= map_socket_value_errno( inSock
, n
== (ssize_t
) me
->responseLen
, n
);
21607 //===========================================================================================================================
21608 // _MDNSColliderSendProbe
21609 //===========================================================================================================================
21611 static OSStatus
_MDNSColliderSendProbe( MDNSColliderRef me
, SocketRef inSock
, const struct sockaddr
*inDest
)
21616 n
= sendto( inSock
, (char *) me
->probePtr
, me
->probeLen
, 0, inDest
, SockAddrGetSize( inDest
) );
21617 err
= map_socket_value_errno( inSock
, n
== (ssize_t
) me
->probeLen
, n
);
21621 //===========================================================================================================================
21622 // ServiceBrowserCreate
21623 //===========================================================================================================================
21625 typedef struct SBDomain SBDomain
;
21626 typedef struct SBServiceType SBServiceType
;
21627 typedef struct SBServiceBrowse SBServiceBrowse
;
21628 typedef struct SBServiceInstance SBServiceInstance
;
21629 typedef struct SBIPAddress SBIPAddress
;
21631 struct ServiceBrowserPrivate
21633 CFRuntimeBase base
; // CF object base.
21634 dispatch_queue_t queue
; // Queue for service browser's events.
21635 DNSServiceRef connection
; // Shared connection for DNS-SD ops.
21636 DNSServiceRef domainsQuery
; // Query for recommended browsing domains.
21637 char * domain
; // If non-null, then browsing is limited to this domain.
21638 StringListItem
* serviceTypeList
; // If non-null, then browsing is limited to these service types.
21639 ServiceBrowserCallback_f userCallback
; // User's callback. Called when browsing stops.
21640 void * userContext
; // User's callback context.
21641 SBDomain
* domainList
; // List of domains and their browse results.
21642 dispatch_source_t stopTimer
; // Timer to stop browsing after browseTimeSecs.
21643 uint32_t ifIndex
; // If non-zero, then browsing is limited to this interface.
21644 unsigned int browseTimeSecs
; // Amount of time to spend browsing in seconds.
21645 Boolean includeAWDL
; // True if the IncludeAWDL flag should be used for DNS-SD ops that
21646 // use the "any" interface.
21651 SBDomain
* next
; // Next domain object in list.
21652 ServiceBrowserRef browser
; // Pointer to parent service browser.
21653 char * name
; // Name of the domain.
21654 DNSServiceRef servicesQuery
; // Query for services (_services._dns-sd._udp.<domain> PTR record) in domain.
21655 SBServiceType
* typeList
; // List of service types to browse for in this domain.
21658 struct SBServiceType
21660 SBServiceType
* next
; // Next service type object in list.
21661 char * name
; // Name of the service type.
21662 SBServiceBrowse
* browseList
; // List of browses for this service type.
21665 struct SBServiceBrowse
21667 SBServiceBrowse
* next
; // Next browse object in list.
21668 ServiceBrowserRef browser
; // Pointer to parent service browser.
21669 DNSServiceRef browse
; // Reference to DNSServiceBrowse op.
21670 SBServiceInstance
* instanceList
; // List of service instances that were discovered by this browse.
21671 uint64_t startTicks
; // Value of UpTicks() when the browse op began.
21672 uint32_t ifIndex
; // If non-zero, then the browse is limited to this interface.
21675 struct SBServiceInstance
21677 SBServiceInstance
* next
; // Next service instance object in list.
21678 ServiceBrowserRef browser
; // Pointer to parent service browser.
21679 char * name
; // Name of the service instance.
21680 char * fqdn
; // Fully qualified domain name of service instance (for logging/debugging).
21681 uint32_t ifIndex
; // Index of interface over which this service instance was discovered.
21682 uint64_t discoverTimeUs
; // Time it took to discover this service instance in microseconds.
21683 DNSServiceRef resolve
; // Reference to DNSServiceResolve op for this service instance.
21684 uint64_t resolveStartTicks
; // Value of UpTicks() when the DNSServiceResolve op began.
21685 uint64_t resolveTimeUs
; // Time it took to resolve this service instance.
21686 char * hostname
; // Service instance's hostname. Result of DNSServiceResolve.
21687 uint16_t port
; // Service instance's port number. Result of DNSServiceResolve.
21688 uint8_t * txtPtr
; // Service instance's TXT record data. Result of DNSServiceResolve.
21689 size_t txtLen
; // Length of service instance's TXT record data.
21690 DNSServiceRef getAddrInfo
; // Reference to DNSServiceGetAddrInfo op for service instance's hostname.
21691 uint64_t gaiStartTicks
; // Value of UpTicks() when the DNSServiceGetAddrInfo op began.
21692 SBIPAddress
* ipaddrList
; // List of IP addresses that the hostname resolved to.
21697 SBIPAddress
* next
; // Next IP address object in list.
21698 sockaddr_ip sip
; // IPv4 or IPv6 address.
21699 uint64_t resolveTimeUs
; // Time it took to resolve this IP address in microseconds.
21704 SBRDomain
* domainList
; // List of domains in which services were found.
21705 int32_t refCount
; // This object's reference count.
21707 } ServiceBrowserResultsPrivate
;
21709 static void _ServiceBrowserStop( ServiceBrowserRef me
, OSStatus inError
);
21710 static OSStatus
_ServiceBrowserAddDomain( ServiceBrowserRef inBrowser
, const char *inDomain
);
21711 static OSStatus
_ServiceBrowserRemoveDomain( ServiceBrowserRef inBrowser
, const char *inName
);
21712 static void _ServiceBrowserTimerHandler( void *inContext
);
21713 static void DNSSD_API
21714 _ServiceBrowserDomainsQueryCallback(
21715 DNSServiceRef inSDRef
,
21716 DNSServiceFlags inFlags
,
21717 uint32_t inInterfaceIndex
,
21718 DNSServiceErrorType inError
,
21719 const char * inFullName
,
21722 uint16_t inRDataLen
,
21723 const void * inRDataPtr
,
21725 void * inContext
);
21726 static void DNSSD_API
21727 _ServiceBrowserServicesQueryCallback(
21728 DNSServiceRef inSDRef
,
21729 DNSServiceFlags inFlags
,
21730 uint32_t inInterfaceIndex
,
21731 DNSServiceErrorType inError
,
21732 const char * inFullName
,
21735 uint16_t inRDataLen
,
21736 const void * inRDataPtr
,
21738 void * inContext
);
21739 static void DNSSD_API
21740 _ServiceBrowserBrowseCallback(
21741 DNSServiceRef inSDRef
,
21742 DNSServiceFlags inFlags
,
21743 uint32_t inInterfaceIndex
,
21744 DNSServiceErrorType inError
,
21745 const char * inName
,
21746 const char * inRegType
,
21747 const char * inDomain
,
21748 void * inContext
);
21749 static void DNSSD_API
21750 _ServiceBrowserResolveCallback(
21751 DNSServiceRef inSDRef
,
21752 DNSServiceFlags inFlags
,
21753 uint32_t inInterfaceIndex
,
21754 DNSServiceErrorType inError
,
21755 const char * inFullName
,
21756 const char * inHostname
,
21759 const unsigned char * inTXTPtr
,
21760 void * inContext
);
21761 static void DNSSD_API
21762 _ServiceBrowserGAICallback(
21763 DNSServiceRef inSDRef
,
21764 DNSServiceFlags inFlags
,
21765 uint32_t inInterfaceIndex
,
21766 DNSServiceErrorType inError
,
21767 const char * inHostname
,
21768 const struct sockaddr
* inSockAddr
,
21770 void * inContext
);
21772 _ServiceBrowserAddServiceType(
21773 ServiceBrowserRef inBrowser
,
21774 SBDomain
* inDomain
,
21775 const char * inName
,
21776 uint32_t inIfIndex
);
21778 _ServiceBrowserRemoveServiceType(
21779 ServiceBrowserRef inBrowser
,
21780 SBDomain
* inDomain
,
21781 const char * inName
,
21782 uint32_t inIfIndex
);
21784 _ServiceBrowserAddServiceInstance(
21785 ServiceBrowserRef inBrowser
,
21786 SBServiceBrowse
* inBrowse
,
21787 uint32_t inIfIndex
,
21788 const char * inName
,
21789 const char * inRegType
,
21790 const char * inDomain
,
21791 uint64_t inDiscoverTimeUs
);
21793 _ServiceBrowserRemoveServiceInstance(
21794 ServiceBrowserRef inBrowser
,
21795 SBServiceBrowse
* inBrowse
,
21796 const char * inName
,
21797 uint32_t inIfIndex
);
21799 _ServiceBrowserAddIPAddress(
21800 ServiceBrowserRef inBrowser
,
21801 SBServiceInstance
* inInstance
,
21802 const struct sockaddr
* inSockAddr
,
21803 uint64_t inResolveTimeUs
);
21805 _ServiceBrowserRemoveIPAddress(
21806 ServiceBrowserRef inBrowser
,
21807 SBServiceInstance
* inInstance
,
21808 const struct sockaddr
* inSockAddr
);
21809 static OSStatus
_ServiceBrowserCreateResults( ServiceBrowserRef me
, ServiceBrowserResults
**outResults
);
21810 static OSStatus
_SBDomainCreate( const char *inName
, ServiceBrowserRef inBrowser
, SBDomain
**outDomain
);
21811 static void _SBDomainFree( SBDomain
*inDomain
);
21812 static OSStatus
_SBServiceTypeCreate( const char *inName
, SBServiceType
**outType
);
21813 static void _SBServiceTypeFree( SBServiceType
*inType
);
21814 static OSStatus
_SBServiceBrowseCreate( uint32_t inIfIndex
, ServiceBrowserRef inBrowser
, SBServiceBrowse
**outBrowse
);
21815 static void _SBServiceBrowseFree( SBServiceBrowse
*inBrowse
);
21817 _SBServiceInstanceCreate(
21818 const char * inName
,
21819 const char * inType
,
21820 const char * inDomain
,
21821 uint32_t inIfIndex
,
21822 uint64_t inDiscoverTimeUs
,
21823 ServiceBrowserRef inBrowser
,
21824 SBServiceInstance
** outInstance
);
21825 static void _SBServiceInstanceFree( SBServiceInstance
*inInstance
);
21827 _SBIPAddressCreate(
21828 const struct sockaddr
* inSockAddr
,
21829 uint64_t inResolveTimeUs
,
21830 SBIPAddress
** outIPAddress
);
21831 static void _SBIPAddressFree( SBIPAddress
*inIPAddress
);
21832 static void _SBIPAddressFreeList( SBIPAddress
*inList
);
21833 static OSStatus
_SBRDomainCreate( const char *inName
, SBRDomain
**outDomain
);
21834 static void _SBRDomainFree( SBRDomain
*inDomain
);
21835 static OSStatus
_SBRServiceTypeCreate( const char *inName
, SBRServiceType
**outType
);
21836 static void _SBRServiceTypeFree( SBRServiceType
*inType
);
21838 _SBRServiceInstanceCreate(
21839 const char * inName
,
21840 uint32_t inInterfaceIndex
,
21841 const char * inHostname
,
21843 const uint8_t * inTXTPtr
,
21845 uint64_t inDiscoverTimeUs
,
21846 uint64_t inResolveTimeUs
,
21847 SBRServiceInstance
** outInstance
);
21848 static void _SBRServiceInstanceFree( SBRServiceInstance
*inInstance
);
21850 _SBRIPAddressCreate(
21851 const struct sockaddr
* inSockAddr
,
21852 uint64_t inResolveTimeUs
,
21853 SBRIPAddress
** outIPAddress
);
21854 static void _SBRIPAddressFree( SBRIPAddress
*inIPAddress
);
21856 #define ForgetSBIPAddressList( X ) ForgetCustom( X, _SBIPAddressFreeList )
21858 CF_CLASS_DEFINE( ServiceBrowser
);
21860 ulog_define_ex( kDNSSDUtilIdentifier
, ServiceBrowser
, kLogLevelTrace
, kLogFlags_None
, "ServiceBrowser", NULL
);
21861 #define sb_ulog( LEVEL, ... ) ulog( &log_category_from_name( ServiceBrowser ), (LEVEL), __VA_ARGS__ )
21864 ServiceBrowserCreate(
21865 dispatch_queue_t inQueue
,
21866 uint32_t inInterfaceIndex
,
21867 const char * inDomain
,
21868 unsigned int inBrowseTimeSecs
,
21869 Boolean inIncludeAWDL
,
21870 ServiceBrowserRef
* outBrowser
)
21873 ServiceBrowserRef obj
;
21875 CF_OBJECT_CREATE( ServiceBrowser
, obj
, err
, exit
);
21877 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
21878 obj
->ifIndex
= inInterfaceIndex
;
21881 obj
->domain
= strdup( inDomain
);
21882 require_action( obj
->domain
, exit
, err
= kNoMemoryErr
);
21884 obj
->browseTimeSecs
= inBrowseTimeSecs
;
21885 obj
->includeAWDL
= inIncludeAWDL
;
21892 CFReleaseNullSafe( obj
);
21896 //===========================================================================================================================
21897 // _ServiceBrowserFinalize
21898 //===========================================================================================================================
21900 static void _ServiceBrowserFinalize( CFTypeRef inObj
)
21902 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inObj
;
21903 StringListItem
* serviceType
;
21905 dispatch_forget( &me
->queue
);
21906 check( !me
->connection
);
21907 check( !me
->domainsQuery
);
21908 ForgetMem( &me
->domain
);
21909 while( ( serviceType
= me
->serviceTypeList
) != NULL
)
21911 me
->serviceTypeList
= serviceType
->next
;
21912 ForgetMem( &serviceType
->str
);
21913 free( serviceType
);
21915 check( !me
->domainList
);
21916 check( !me
->stopTimer
);
21919 //===========================================================================================================================
21920 // ServiceBrowserStart
21921 //===========================================================================================================================
21923 static void _ServiceBrowserStart( void *inContext
);
21925 static void ServiceBrowserStart( ServiceBrowserRef me
)
21928 dispatch_async_f( me
->queue
, me
, _ServiceBrowserStart
);
21931 static void _ServiceBrowserStart( void *inContext
)
21934 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inContext
;
21936 err
= DNSServiceCreateConnection( &me
->connection
);
21937 require_noerr( err
, exit
);
21939 err
= DNSServiceSetDispatchQueue( me
->connection
, me
->queue
);
21940 require_noerr( err
, exit
);
21944 err
= _ServiceBrowserAddDomain( me
, me
->domain
);
21945 require_noerr( err
, exit
);
21949 DNSServiceRef sdRef
;
21950 const char * const recordName
= "b._dns-sd._udp.local.";
21951 const uint32_t ifIndex
= kDNSServiceInterfaceIndexLocalOnly
;
21953 // Perform PTR meta-query for "b._dns-sd._udp.local." to enumerate recommended browsing domains.
21954 // See <https://tools.ietf.org/html/rfc6763#section-11>.
21956 sb_ulog( kLogLevelTrace
, "Starting PTR QueryRecord on interface %d for %s", (int32_t) ifIndex
, recordName
);
21958 sdRef
= me
->connection
;
21959 err
= DNSServiceQueryRecord( &sdRef
, kDNSServiceFlagsShareConnection
, ifIndex
, recordName
,
21960 kDNSServiceType_PTR
, kDNSServiceClass_IN
, _ServiceBrowserDomainsQueryCallback
, me
);
21961 require_noerr( err
, exit
);
21963 me
->domainsQuery
= sdRef
;
21966 err
= DispatchTimerCreate( dispatch_time_seconds( me
->browseTimeSecs
), DISPATCH_TIME_FOREVER
,
21967 100 * kNanosecondsPerMillisecond
, me
->queue
, _ServiceBrowserTimerHandler
, NULL
, me
, &me
->stopTimer
);
21968 require_noerr( err
, exit
);
21969 dispatch_resume( me
->stopTimer
);
21972 if( err
) _ServiceBrowserStop( me
, err
);
21975 //===========================================================================================================================
21976 // ServiceBrowserAddServiceType
21977 //===========================================================================================================================
21979 static OSStatus
ServiceBrowserAddServiceType( ServiceBrowserRef me
, const char *inServiceType
)
21982 StringListItem
* item
;
21983 StringListItem
** itemPtr
;
21984 StringListItem
* newItem
= NULL
;
21986 for( itemPtr
= &me
->serviceTypeList
; ( item
= *itemPtr
) != NULL
; itemPtr
= &item
->next
)
21988 if( strcmp( item
->str
, inServiceType
) == 0 ) break;
21992 newItem
= (StringListItem
*) calloc( 1, sizeof( *newItem
) );
21993 require_action( newItem
, exit
, err
= kNoMemoryErr
);
21995 newItem
->str
= strdup( inServiceType
);
21996 require_action( newItem
->str
, exit
, err
= kNoMemoryErr
);
21998 *itemPtr
= newItem
;
22004 FreeNullSafe( newItem
);
22008 //===========================================================================================================================
22009 // ServiceBrowserSetCallback
22010 //===========================================================================================================================
22012 static void ServiceBrowserSetCallback( ServiceBrowserRef me
, ServiceBrowserCallback_f inCallback
, void *inContext
)
22014 me
->userCallback
= inCallback
;
22015 me
->userContext
= inContext
;
22018 //===========================================================================================================================
22019 // ServiceBrowserResultsRetain
22020 //===========================================================================================================================
22022 static void ServiceBrowserResultsRetain( ServiceBrowserResults
*inResults
)
22024 ServiceBrowserResultsPrivate
* const results
= (ServiceBrowserResultsPrivate
*) inResults
;
22026 atomic_add_32( &results
->refCount
, 1 );
22029 //===========================================================================================================================
22030 // ServiceBrowserResultsRelease
22031 //===========================================================================================================================
22033 static void ServiceBrowserResultsRelease( ServiceBrowserResults
*inResults
)
22035 ServiceBrowserResultsPrivate
* const results
= (ServiceBrowserResultsPrivate
*) inResults
;
22036 SBRDomain
* domain
;
22038 if( atomic_add_and_fetch_32( &results
->refCount
, -1 ) == 0 )
22040 while( ( domain
= inResults
->domainList
) != NULL
)
22042 inResults
->domainList
= domain
->next
;
22043 _SBRDomainFree( domain
);
22049 //===========================================================================================================================
22050 // _ServiceBrowserStop
22051 //===========================================================================================================================
22053 static void _ServiceBrowserStop( ServiceBrowserRef me
, OSStatus inError
)
22058 SBServiceBrowse
* b
;
22059 SBServiceInstance
* i
;
22061 dispatch_source_forget( &me
->stopTimer
);
22062 DNSServiceForget( &me
->domainsQuery
);
22063 for( d
= me
->domainList
; d
; d
= d
->next
)
22065 DNSServiceForget( &d
->servicesQuery
);
22066 for( t
= d
->typeList
; t
; t
= t
->next
)
22068 for( b
= t
->browseList
; b
; b
= b
->next
)
22070 DNSServiceForget( &b
->browse
);
22071 for( i
= b
->instanceList
; i
; i
= i
->next
)
22073 DNSServiceForget( &i
->resolve
);
22074 DNSServiceForget( &i
->getAddrInfo
);
22079 DNSServiceForget( &me
->connection
);
22081 if( me
->userCallback
)
22083 ServiceBrowserResults
* results
= NULL
;
22085 err
= _ServiceBrowserCreateResults( me
, &results
);
22086 if( !err
) err
= inError
;
22088 me
->userCallback( results
, err
, me
->userContext
);
22089 me
->userCallback
= NULL
;
22090 me
->userContext
= NULL
;
22091 if( results
) ServiceBrowserResultsRelease( results
);
22094 while( ( d
= me
->domainList
) != NULL
)
22096 me
->domainList
= d
->next
;
22097 _SBDomainFree( d
);
22102 //===========================================================================================================================
22103 // _ServiceBrowserAddDomain
22104 //===========================================================================================================================
22106 static OSStatus
_ServiceBrowserAddDomain( ServiceBrowserRef me
, const char *inDomain
)
22110 SBDomain
** domainPtr
;
22111 SBDomain
* newDomain
= NULL
;
22113 for( domainPtr
= &me
->domainList
; ( domain
= *domainPtr
) != NULL
; domainPtr
= &domain
->next
)
22115 if( strcasecmp( domain
->name
, inDomain
) == 0 ) break;
22117 require_action_quiet( !domain
, exit
, err
= kDuplicateErr
);
22119 err
= _SBDomainCreate( inDomain
, me
, &newDomain
);
22120 require_noerr_quiet( err
, exit
);
22122 if( me
->serviceTypeList
)
22124 const StringListItem
* item
;
22126 for( item
= me
->serviceTypeList
; item
; item
= item
->next
)
22128 err
= _ServiceBrowserAddServiceType( me
, newDomain
, item
->str
, me
->ifIndex
);
22129 if( err
== kDuplicateErr
) err
= kNoErr
;
22130 require_noerr( err
, exit
);
22136 DNSServiceRef sdRef
;
22137 DNSServiceFlags flags
;
22139 // Perform PTR meta-query for _services._dns-sd._udp.<domain> to enumerate service types in domain.
22140 // See <https://tools.ietf.org/html/rfc6763#section-9>.
22142 ASPrintF( &recordName
, "_services._dns-sd._udp.%s", newDomain
->name
);
22143 require_action( recordName
, exit
, err
= kNoMemoryErr
);
22145 flags
= kDNSServiceFlagsShareConnection
;
22146 if( ( me
->ifIndex
== kDNSServiceInterfaceIndexAny
) && me
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
22148 sb_ulog( kLogLevelTrace
, "Starting PTR QueryRecord on interface %d for %s", (int32_t) me
->ifIndex
, recordName
);
22150 sdRef
= newDomain
->browser
->connection
;
22151 err
= DNSServiceQueryRecord( &sdRef
, flags
, me
->ifIndex
, recordName
, kDNSServiceType_PTR
, kDNSServiceClass_IN
,
22152 _ServiceBrowserServicesQueryCallback
, newDomain
);
22153 free( recordName
);
22154 require_noerr( err
, exit
);
22156 newDomain
->servicesQuery
= sdRef
;
22159 *domainPtr
= newDomain
;
22164 if( newDomain
) _SBDomainFree( newDomain
);
22168 //===========================================================================================================================
22169 // _ServiceBrowserRemoveDomain
22170 //===========================================================================================================================
22172 static OSStatus
_ServiceBrowserRemoveDomain( ServiceBrowserRef me
, const char *inName
)
22176 SBDomain
** domainPtr
;
22178 for( domainPtr
= &me
->domainList
; ( domain
= *domainPtr
) != NULL
; domainPtr
= &domain
->next
)
22180 if( strcasecmp( domain
->name
, inName
) == 0 ) break;
22185 *domainPtr
= domain
->next
;
22186 _SBDomainFree( domain
);
22191 err
= kNotFoundErr
;
22197 //===========================================================================================================================
22198 // _ServiceBrowserTimerHandler
22199 //===========================================================================================================================
22201 static void _ServiceBrowserTimerHandler( void *inContext
)
22203 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inContext
;
22205 _ServiceBrowserStop( me
, kNoErr
);
22208 //===========================================================================================================================
22209 // _ServiceBrowserDomainsQueryCallback
22210 //===========================================================================================================================
22212 static void DNSSD_API
22213 _ServiceBrowserDomainsQueryCallback(
22214 DNSServiceRef inSDRef
,
22215 DNSServiceFlags inFlags
,
22216 uint32_t inInterfaceIndex
,
22217 DNSServiceErrorType inError
,
22218 const char * inFullName
,
22221 uint16_t inRDataLen
,
22222 const void * inRDataPtr
,
22226 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inContext
;
22228 char domainStr
[ kDNSServiceMaxDomainName
];
22234 sb_ulog( kLogLevelTrace
, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
22235 DNSServiceFlagsToAddRmvStr( inFlags
), (int32_t) inInterfaceIndex
, inFullName
, inType
, inRDataPtr
, inRDataLen
,
22236 !inError
, inError
);
22238 require_noerr( inError
, exit
);
22240 err
= DomainNameToString( inRDataPtr
, ( (const uint8_t *) inRDataPtr
) + inRDataLen
, domainStr
, NULL
);
22241 require_noerr( err
, exit
);
22243 if( inFlags
& kDNSServiceFlagsAdd
)
22245 err
= _ServiceBrowserAddDomain( me
, domainStr
);
22246 if( err
== kDuplicateErr
) err
= kNoErr
;
22247 require_noerr( err
, exit
);
22251 err
= _ServiceBrowserRemoveDomain( me
, domainStr
);
22252 if( err
== kNotFoundErr
) err
= kNoErr
;
22253 require_noerr( err
, exit
);
22260 //===========================================================================================================================
22261 // _ServiceBrowserServicesQueryCallback
22262 //===========================================================================================================================
22264 static void DNSSD_API
22265 _ServiceBrowserServicesQueryCallback(
22266 DNSServiceRef inSDRef
,
22267 DNSServiceFlags inFlags
,
22268 uint32_t inInterfaceIndex
,
22269 DNSServiceErrorType inError
,
22270 const char * inFullName
,
22273 uint16_t inRDataLen
,
22274 const void * inRDataPtr
,
22279 SBDomain
* const domain
= (SBDomain
*) inContext
;
22280 ServiceBrowserRef
const me
= domain
->browser
;
22281 const uint8_t * src
;
22282 const uint8_t * end
;
22285 uint8_t serviceType
[ 2 * ( 1 + kDomainLabelLengthMax
) + 1 ];
22286 char serviceTypeStr
[ kDNSServiceMaxDomainName
];
22292 sb_ulog( kLogLevelTrace
, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
22293 DNSServiceFlagsToAddRmvStr( inFlags
), (int32_t) inInterfaceIndex
, inFullName
, inType
, inRDataPtr
, inRDataLen
,
22294 !inError
, inError
);
22296 require_noerr( inError
, exit
);
22298 check( inType
== kDNSServiceType_PTR
);
22299 check( inClass
== kDNSServiceClass_IN
);
22301 // The first two labels of the domain name in the RDATA describe a service type.
22302 // See <https://tools.ietf.org/html/rfc6763#section-9>.
22304 src
= (const uint8_t *) inRDataPtr
;
22305 end
= src
+ inRDataLen
;
22307 for( i
= 0; i
< 2; ++i
)
22311 require_action_quiet( ( end
- src
) > 0, exit
, err
= kUnderrunErr
);
22314 require_action_quiet( ( labelLen
> 0 ) && ( labelLen
<= kDomainLabelLengthMax
), exit
, err
= kMalformedErr
);
22315 require_action_quiet( ( (size_t)( end
- src
) ) >= ( 1 + labelLen
), exit
, err
= kUnderrunErr
);
22317 memcpy( dst
, src
, 1 + labelLen
);
22318 src
+= 1 + labelLen
;
22319 dst
+= 1 + labelLen
;
22323 err
= DomainNameToString( serviceType
, NULL
, serviceTypeStr
, NULL
);
22324 require_noerr( err
, exit
);
22326 if( inFlags
& kDNSServiceFlagsAdd
)
22328 err
= _ServiceBrowserAddServiceType( me
, domain
, serviceTypeStr
, inInterfaceIndex
);
22329 if( err
== kDuplicateErr
) err
= kNoErr
;
22330 require_noerr( err
, exit
);
22334 err
= _ServiceBrowserRemoveServiceType( me
, domain
, serviceTypeStr
, inInterfaceIndex
);
22335 if( err
== kNotFoundErr
) err
= kNoErr
;
22336 require_noerr( err
, exit
);
22343 //===========================================================================================================================
22344 // _ServiceBrowserBrowseCallback
22345 //===========================================================================================================================
22347 static void DNSSD_API
22348 _ServiceBrowserBrowseCallback(
22349 DNSServiceRef inSDRef
,
22350 DNSServiceFlags inFlags
,
22351 uint32_t inInterfaceIndex
,
22352 DNSServiceErrorType inError
,
22353 const char * inName
,
22354 const char * inRegType
,
22355 const char * inDomain
,
22359 const uint64_t nowTicks
= UpTicks();
22360 SBServiceBrowse
* const browse
= (SBServiceBrowse
*) inContext
;
22361 ServiceBrowserRef
const me
= (ServiceBrowserRef
) browse
->browser
;
22365 sb_ulog( kLogLevelTrace
, "Browse result: %s on interface %d for %s.%s%s%?{end} (error: %#m)",
22366 DNSServiceFlagsToAddRmvStr( inFlags
), (int32_t) inInterfaceIndex
, inName
, inRegType
, inDomain
, !inError
, inError
);
22368 require_noerr( inError
, exit
);
22370 if( inFlags
& kDNSServiceFlagsAdd
)
22372 err
= _ServiceBrowserAddServiceInstance( me
, browse
, inInterfaceIndex
, inName
, inRegType
, inDomain
,
22373 UpTicksToMicroseconds( nowTicks
- browse
->startTicks
) );
22374 if( err
== kDuplicateErr
) err
= kNoErr
;
22375 require_noerr( err
, exit
);
22379 err
= _ServiceBrowserRemoveServiceInstance( me
, browse
, inName
, inInterfaceIndex
);
22380 if( err
== kNotFoundErr
) err
= kNoErr
;
22381 require_noerr( err
, exit
);
22388 //===========================================================================================================================
22389 // _ServiceBrowserResolveCallback
22390 //===========================================================================================================================
22392 static void DNSSD_API
22393 _ServiceBrowserResolveCallback(
22394 DNSServiceRef inSDRef
,
22395 DNSServiceFlags inFlags
,
22396 uint32_t inInterfaceIndex
,
22397 DNSServiceErrorType inError
,
22398 const char * inFullName
,
22399 const char * inHostname
,
22402 const unsigned char * inTXTPtr
,
22406 const uint64_t nowTicks
= UpTicks();
22407 SBServiceInstance
* const instance
= (SBServiceInstance
*) inContext
;
22408 ServiceBrowserRef
const me
= (ServiceBrowserRef
) instance
->browser
;
22413 sb_ulog( kLogLevelTrace
, "Resolve result on interface %d for %s -> %s:%u %#{txt}%?{end} (error %#m)",
22414 (int32_t) inInterfaceIndex
, inFullName
, inHostname
, inPort
, inTXTPtr
, (size_t) inTXTLen
, !inError
, inError
);
22416 require_noerr( inError
, exit
);
22418 if( !MemEqual( instance
->txtPtr
, instance
->txtLen
, inTXTPtr
, inTXTLen
) )
22420 FreeNullSafe( instance
->txtPtr
);
22421 instance
->txtPtr
= _memdup( inTXTPtr
, inTXTLen
);
22422 require_action( instance
->txtPtr
, exit
, err
= kNoMemoryErr
);
22424 instance
->txtLen
= inTXTLen
;
22427 instance
->port
= ntohs( inPort
);
22429 if( !instance
->hostname
|| ( strcasecmp( instance
->hostname
, inHostname
) != 0 ) )
22431 DNSServiceRef sdRef
;
22433 if( !instance
->hostname
) instance
->resolveTimeUs
= UpTicksToMicroseconds( nowTicks
- instance
->resolveStartTicks
);
22435 err
= ReplaceString( &instance
->hostname
, NULL
, inHostname
, kSizeCString
);
22436 require_noerr( err
, exit
);
22438 DNSServiceForget( &instance
->getAddrInfo
);
22439 ForgetSBIPAddressList( &instance
->ipaddrList
);
22441 sb_ulog( kLogLevelTrace
, "Starting GetAddrInfo on interface %d for %s",
22442 (int32_t) instance
->ifIndex
, instance
->hostname
);
22444 sdRef
= me
->connection
;
22445 instance
->gaiStartTicks
= UpTicks();
22446 err
= DNSServiceGetAddrInfo( &sdRef
, kDNSServiceFlagsShareConnection
, instance
->ifIndex
,
22447 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, instance
->hostname
, _ServiceBrowserGAICallback
, instance
);
22448 require_noerr( err
, exit
);
22450 instance
->getAddrInfo
= sdRef
;
22457 //===========================================================================================================================
22458 // _ServiceBrowserGAICallback
22459 //===========================================================================================================================
22461 static void DNSSD_API
22462 _ServiceBrowserGAICallback(
22463 DNSServiceRef inSDRef
,
22464 DNSServiceFlags inFlags
,
22465 uint32_t inInterfaceIndex
,
22466 DNSServiceErrorType inError
,
22467 const char * inHostname
,
22468 const struct sockaddr
* inSockAddr
,
22473 const uint64_t nowTicks
= UpTicks();
22474 SBServiceInstance
* const instance
= (SBServiceInstance
*) inContext
;
22475 ServiceBrowserRef
const me
= (ServiceBrowserRef
) instance
->browser
;
22480 sb_ulog( kLogLevelTrace
, "GetAddrInfo result: %s on interface %d for (%s ->) %s -> %##a%?{end} (error: %#m)",
22481 DNSServiceFlagsToAddRmvStr( inFlags
), (int32_t) inInterfaceIndex
, instance
->fqdn
, inHostname
, inSockAddr
,
22482 !inError
, inError
);
22484 require_noerr( inError
, exit
);
22486 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
22488 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
22492 if( inFlags
& kDNSServiceFlagsAdd
)
22494 err
= _ServiceBrowserAddIPAddress( me
, instance
, inSockAddr
,
22495 UpTicksToMicroseconds( nowTicks
- instance
->gaiStartTicks
) );
22496 if( err
== kDuplicateErr
) err
= kNoErr
;
22497 require_noerr( err
, exit
);
22501 err
= _ServiceBrowserRemoveIPAddress( me
, instance
, inSockAddr
);
22502 if( err
== kNotFoundErr
) err
= kNoErr
;
22503 require_noerr( err
, exit
);
22510 //===========================================================================================================================
22511 // _ServiceBrowserAddServiceType
22512 //===========================================================================================================================
22515 _ServiceBrowserAddServiceType(
22516 ServiceBrowserRef me
,
22517 SBDomain
* inDomain
,
22518 const char * inName
,
22519 uint32_t inIfIndex
)
22522 SBServiceType
* type
;
22523 SBServiceType
** typePtr
;
22524 SBServiceType
* newType
= NULL
;
22525 SBServiceBrowse
* browse
;
22526 SBServiceBrowse
** browsePtr
;
22527 SBServiceBrowse
* newBrowse
= NULL
;
22528 DNSServiceRef sdRef
;
22529 DNSServiceFlags flags
;
22531 for( typePtr
= &inDomain
->typeList
; ( type
= *typePtr
) != NULL
; typePtr
= &type
->next
)
22533 if( strcasecmp( type
->name
, inName
) == 0 ) break;
22537 err
= _SBServiceTypeCreate( inName
, &newType
);
22538 require_noerr_quiet( err
, exit
);
22543 for( browsePtr
= &type
->browseList
; ( browse
= *browsePtr
) != NULL
; browsePtr
= &browse
->next
)
22545 if( browse
->ifIndex
== inIfIndex
) break;
22547 require_action_quiet( !browse
, exit
, err
= kDuplicateErr
);
22549 err
= _SBServiceBrowseCreate( inIfIndex
, me
, &newBrowse
);
22550 require_noerr_quiet( err
, exit
);
22552 flags
= kDNSServiceFlagsShareConnection
;
22553 if( ( newBrowse
->ifIndex
== kDNSServiceInterfaceIndexAny
) && me
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
22555 sb_ulog( kLogLevelTrace
, "Starting Browse on interface %d for %s%s",
22556 (int32_t) newBrowse
->ifIndex
, type
->name
, inDomain
->name
);
22558 sdRef
= me
->connection
;
22559 newBrowse
->startTicks
= UpTicks();
22560 err
= DNSServiceBrowse( &sdRef
, flags
, newBrowse
->ifIndex
, type
->name
, inDomain
->name
, _ServiceBrowserBrowseCallback
,
22562 require_noerr( err
, exit
);
22564 newBrowse
->browse
= sdRef
;
22565 *browsePtr
= newBrowse
;
22570 *typePtr
= newType
;
22575 if( newBrowse
) _SBServiceBrowseFree( newBrowse
);
22576 if( newType
) _SBServiceTypeFree( newType
);
22580 //===========================================================================================================================
22581 // _ServiceBrowserRemoveServiceType
22582 //===========================================================================================================================
22585 _ServiceBrowserRemoveServiceType(
22586 ServiceBrowserRef me
,
22587 SBDomain
* inDomain
,
22588 const char * inName
,
22589 uint32_t inIfIndex
)
22592 SBServiceType
* type
;
22593 SBServiceType
** typePtr
;
22594 SBServiceBrowse
* browse
;
22595 SBServiceBrowse
** browsePtr
;
22599 for( typePtr
= &inDomain
->typeList
; ( type
= *typePtr
) != NULL
; typePtr
= &type
->next
)
22601 if( strcasecmp( type
->name
, inName
) == 0 ) break;
22603 require_action_quiet( type
, exit
, err
= kNotFoundErr
);
22605 for( browsePtr
= &type
->browseList
; ( browse
= *browsePtr
) != NULL
; browsePtr
= &browse
->next
)
22607 if( browse
->ifIndex
== inIfIndex
) break;
22609 require_action_quiet( browse
, exit
, err
= kNotFoundErr
);
22611 *browsePtr
= browse
->next
;
22612 _SBServiceBrowseFree( browse
);
22613 if( !type
->browseList
)
22615 *typePtr
= type
->next
;
22616 _SBServiceTypeFree( type
);
22624 //===========================================================================================================================
22625 // _ServiceBrowserAddServiceInstance
22626 //===========================================================================================================================
22629 _ServiceBrowserAddServiceInstance(
22630 ServiceBrowserRef me
,
22631 SBServiceBrowse
* inBrowse
,
22632 uint32_t inIfIndex
,
22633 const char * inName
,
22634 const char * inRegType
,
22635 const char * inDomain
,
22636 uint64_t inDiscoverTimeUs
)
22639 DNSServiceRef sdRef
;
22640 SBServiceInstance
* instance
;
22641 SBServiceInstance
** instancePtr
;
22642 SBServiceInstance
* newInstance
= NULL
;
22644 for( instancePtr
= &inBrowse
->instanceList
; ( instance
= *instancePtr
) != NULL
; instancePtr
= &instance
->next
)
22646 if( ( instance
->ifIndex
== inIfIndex
) && ( strcasecmp( instance
->name
, inName
) == 0 ) ) break;
22648 require_action_quiet( !instance
, exit
, err
= kDuplicateErr
);
22650 err
= _SBServiceInstanceCreate( inName
, inRegType
, inDomain
, inIfIndex
, inDiscoverTimeUs
, me
, &newInstance
);
22651 require_noerr_quiet( err
, exit
);
22653 sb_ulog( kLogLevelTrace
, "Starting Resolve on interface %d for %s.%s%s",
22654 (int32_t) newInstance
->ifIndex
, inName
, inRegType
, inDomain
);
22656 sdRef
= me
->connection
;
22657 newInstance
->resolveStartTicks
= UpTicks();
22658 err
= DNSServiceResolve( &sdRef
, kDNSServiceFlagsShareConnection
, newInstance
->ifIndex
, inName
, inRegType
, inDomain
,
22659 _ServiceBrowserResolveCallback
, newInstance
);
22660 require_noerr( err
, exit
);
22662 newInstance
->resolve
= sdRef
;
22663 *instancePtr
= newInstance
;
22664 newInstance
= NULL
;
22667 if( newInstance
) _SBServiceInstanceFree( newInstance
);
22671 //===========================================================================================================================
22672 // _ServiceBrowserRemoveServiceInstance
22673 //===========================================================================================================================
22676 _ServiceBrowserRemoveServiceInstance(
22677 ServiceBrowserRef me
,
22678 SBServiceBrowse
* inBrowse
,
22679 const char * inName
,
22680 uint32_t inIfIndex
)
22683 SBServiceInstance
* instance
;
22684 SBServiceInstance
** ptr
;
22688 for( ptr
= &inBrowse
->instanceList
; ( instance
= *ptr
) != NULL
; ptr
= &instance
->next
)
22690 if( ( instance
->ifIndex
== inIfIndex
) && ( strcasecmp( instance
->name
, inName
) == 0 ) ) break;
22692 require_action_quiet( instance
, exit
, err
= kNotFoundErr
);
22694 *ptr
= instance
->next
;
22695 _SBServiceInstanceFree( instance
);
22702 //===========================================================================================================================
22703 // _ServiceBrowserAddIPAddress
22704 //===========================================================================================================================
22707 _ServiceBrowserAddIPAddress(
22708 ServiceBrowserRef me
,
22709 SBServiceInstance
* inInstance
,
22710 const struct sockaddr
* inSockAddr
,
22711 uint64_t inResolveTimeUs
)
22714 SBIPAddress
* ipaddr
;
22715 SBIPAddress
** ipaddrPtr
;
22716 SBIPAddress
* newIPAddr
= NULL
;
22720 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
22722 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
22727 for( ipaddrPtr
= &inInstance
->ipaddrList
; ( ipaddr
= *ipaddrPtr
) != NULL
; ipaddrPtr
= &ipaddr
->next
)
22729 if( SockAddrCompareAddr( &ipaddr
->sip
, inSockAddr
) == 0 ) break;
22731 require_action_quiet( !ipaddr
, exit
, err
= kDuplicateErr
);
22733 err
= _SBIPAddressCreate( inSockAddr
, inResolveTimeUs
, &newIPAddr
);
22734 require_noerr_quiet( err
, exit
);
22736 *ipaddrPtr
= newIPAddr
;
22741 if( newIPAddr
) _SBIPAddressFree( newIPAddr
);
22745 //===========================================================================================================================
22746 // _ServiceBrowserRemoveIPAddress
22747 //===========================================================================================================================
22750 _ServiceBrowserRemoveIPAddress(
22751 ServiceBrowserRef me
,
22752 SBServiceInstance
* inInstance
,
22753 const struct sockaddr
* inSockAddr
)
22756 SBIPAddress
* ipaddr
;
22757 SBIPAddress
** ipaddrPtr
;
22761 for( ipaddrPtr
= &inInstance
->ipaddrList
; ( ipaddr
= *ipaddrPtr
) != NULL
; ipaddrPtr
= &ipaddr
->next
)
22763 if( SockAddrCompareAddr( &ipaddr
->sip
.sa
, inSockAddr
) == 0 ) break;
22765 require_action_quiet( ipaddr
, exit
, err
= kNotFoundErr
);
22767 *ipaddrPtr
= ipaddr
->next
;
22768 _SBIPAddressFree( ipaddr
);
22775 //===========================================================================================================================
22776 // _ServiceBrowserCreateResults
22777 //===========================================================================================================================
22779 static OSStatus
_ServiceBrowserCreateResults( ServiceBrowserRef me
, ServiceBrowserResults
**outResults
)
22784 SBServiceBrowse
* b
;
22785 SBServiceInstance
* i
;
22787 ServiceBrowserResultsPrivate
* results
;
22788 SBRDomain
** domainPtr
;
22790 results
= (ServiceBrowserResultsPrivate
*) calloc( 1, sizeof( *results
) );
22791 require_action( results
, exit
, err
= kNoMemoryErr
);
22793 results
->refCount
= 1;
22795 domainPtr
= &results
->domainList
;
22796 for( d
= me
->domainList
; d
; d
= d
->next
)
22798 SBRDomain
* domain
;
22799 SBRServiceType
** typePtr
;
22801 err
= _SBRDomainCreate( d
->name
, &domain
);
22802 require_noerr_quiet( err
, exit
);
22803 *domainPtr
= domain
;
22804 domainPtr
= &domain
->next
;
22806 typePtr
= &domain
->typeList
;
22807 for( t
= d
->typeList
; t
; t
= t
->next
)
22809 SBRServiceType
* type
;
22810 SBRServiceInstance
** instancePtr
;
22812 err
= _SBRServiceTypeCreate( t
->name
, &type
);
22813 require_noerr_quiet( err
, exit
);
22815 typePtr
= &type
->next
;
22817 instancePtr
= &type
->instanceList
;
22818 for( b
= t
->browseList
; b
; b
= b
->next
)
22820 for( i
= b
->instanceList
; i
; i
= i
->next
)
22822 SBRServiceInstance
* instance
;
22823 SBRIPAddress
** ipaddrPtr
;
22825 err
= _SBRServiceInstanceCreate( i
->name
, i
->ifIndex
, i
->hostname
, i
->port
, i
->txtPtr
, i
->txtLen
,
22826 i
->discoverTimeUs
, i
->resolveTimeUs
, &instance
);
22827 require_noerr_quiet( err
, exit
);
22828 *instancePtr
= instance
;
22829 instancePtr
= &instance
->next
;
22831 ipaddrPtr
= &instance
->ipaddrList
;
22832 for( a
= i
->ipaddrList
; a
; a
= a
->next
)
22834 SBRIPAddress
* ipaddr
;
22836 err
= _SBRIPAddressCreate( &a
->sip
.sa
, a
->resolveTimeUs
, &ipaddr
);
22837 require_noerr_quiet( err
, exit
);
22839 *ipaddrPtr
= ipaddr
;
22840 ipaddrPtr
= &ipaddr
->next
;
22847 *outResults
= (ServiceBrowserResults
*) results
;
22852 if( results
) ServiceBrowserResultsRelease( (ServiceBrowserResults
*) results
);
22856 //===========================================================================================================================
22858 //===========================================================================================================================
22860 static OSStatus
_SBDomainCreate( const char *inName
, ServiceBrowserRef inBrowser
, SBDomain
**outDomain
)
22865 obj
= (SBDomain
*) calloc( 1, sizeof( *obj
) );
22866 require_action( obj
, exit
, err
= kNoMemoryErr
);
22868 obj
->name
= strdup( inName
);
22869 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
22871 obj
->browser
= inBrowser
;
22878 if( obj
) _SBDomainFree( obj
);
22882 //===========================================================================================================================
22884 //===========================================================================================================================
22886 static void _SBDomainFree( SBDomain
*inDomain
)
22888 SBServiceType
* type
;
22890 ForgetMem( &inDomain
->name
);
22891 DNSServiceForget( &inDomain
->servicesQuery
);
22892 while( ( type
= inDomain
->typeList
) != NULL
)
22894 inDomain
->typeList
= type
->next
;
22895 _SBServiceTypeFree( type
);
22900 //===========================================================================================================================
22901 // _SBServiceTypeCreate
22902 //===========================================================================================================================
22904 static OSStatus
_SBServiceTypeCreate( const char *inName
, SBServiceType
**outType
)
22907 SBServiceType
* obj
;
22909 obj
= (SBServiceType
*) calloc( 1, sizeof( *obj
) );
22910 require_action( obj
, exit
, err
= kNoMemoryErr
);
22912 obj
->name
= strdup( inName
);
22913 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
22920 if( obj
) _SBServiceTypeFree( obj
);
22924 //===========================================================================================================================
22925 // _SBServiceTypeFree
22926 //===========================================================================================================================
22928 static void _SBServiceTypeFree( SBServiceType
*inType
)
22930 SBServiceBrowse
* browse
;
22932 ForgetMem( &inType
->name
);
22933 while( ( browse
= inType
->browseList
) != NULL
)
22935 inType
->browseList
= browse
->next
;
22936 _SBServiceBrowseFree( browse
);
22941 //===========================================================================================================================
22942 // _SBServiceBrowseCreate
22943 //===========================================================================================================================
22945 static OSStatus
_SBServiceBrowseCreate( uint32_t inIfIndex
, ServiceBrowserRef inBrowser
, SBServiceBrowse
**outBrowse
)
22948 SBServiceBrowse
* obj
;
22950 obj
= (SBServiceBrowse
*) calloc( 1, sizeof( *obj
) );
22951 require_action( obj
, exit
, err
= kNoMemoryErr
);
22953 obj
->ifIndex
= inIfIndex
;
22954 obj
->browser
= inBrowser
;
22962 //===========================================================================================================================
22963 // _SBServiceBrowseFree
22964 //===========================================================================================================================
22966 static void _SBServiceBrowseFree( SBServiceBrowse
*inBrowse
)
22968 SBServiceInstance
* instance
;
22970 DNSServiceForget( &inBrowse
->browse
);
22971 while( ( instance
= inBrowse
->instanceList
) != NULL
)
22973 inBrowse
->instanceList
= instance
->next
;
22974 _SBServiceInstanceFree( instance
);
22979 //===========================================================================================================================
22980 // _SBServiceInstanceCreate
22981 //===========================================================================================================================
22984 _SBServiceInstanceCreate(
22985 const char * inName
,
22986 const char * inType
,
22987 const char * inDomain
,
22988 uint32_t inIfIndex
,
22989 uint64_t inDiscoverTimeUs
,
22990 ServiceBrowserRef inBrowser
,
22991 SBServiceInstance
** outInstance
)
22994 SBServiceInstance
* obj
;
22996 obj
= (SBServiceInstance
*) calloc( 1, sizeof( *obj
) );
22997 require_action( obj
, exit
, err
= kNoMemoryErr
);
22999 obj
->name
= strdup( inName
);
23000 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
23002 ASPrintF( &obj
->fqdn
, "%s.%s%s", obj
->name
, inType
, inDomain
);
23003 require_action( obj
->fqdn
, exit
, err
= kNoMemoryErr
);
23005 obj
->ifIndex
= inIfIndex
;
23006 obj
->discoverTimeUs
= inDiscoverTimeUs
;
23007 obj
->browser
= inBrowser
;
23009 *outInstance
= obj
;
23014 if( obj
) _SBServiceInstanceFree( obj
);
23018 //===========================================================================================================================
23019 // _SBServiceInstanceFree
23020 //===========================================================================================================================
23022 static void _SBServiceInstanceFree( SBServiceInstance
*inInstance
)
23024 ForgetMem( &inInstance
->name
);
23025 ForgetMem( &inInstance
->fqdn
);
23026 DNSServiceForget( &inInstance
->resolve
);
23027 ForgetMem( &inInstance
->hostname
);
23028 ForgetMem( &inInstance
->txtPtr
);
23029 DNSServiceForget( &inInstance
->getAddrInfo
);
23030 ForgetSBIPAddressList( &inInstance
->ipaddrList
);
23031 free( inInstance
);
23034 //===========================================================================================================================
23035 // _SBIPAddressCreate
23036 //===========================================================================================================================
23038 static OSStatus
_SBIPAddressCreate( const struct sockaddr
*inSockAddr
, uint64_t inResolveTimeUs
, SBIPAddress
**outIPAddress
)
23043 obj
= (SBIPAddress
*) calloc( 1, sizeof( *obj
) );
23044 require_action( obj
, exit
, err
= kNoMemoryErr
);
23046 SockAddrCopy( inSockAddr
, &obj
->sip
);
23047 obj
->resolveTimeUs
= inResolveTimeUs
;
23049 *outIPAddress
= obj
;
23056 //===========================================================================================================================
23057 // _SBIPAddressFree
23058 //===========================================================================================================================
23060 static void _SBIPAddressFree( SBIPAddress
*inIPAddress
)
23062 free( inIPAddress
);
23065 //===========================================================================================================================
23066 // _SBIPAddressFreeList
23067 //===========================================================================================================================
23069 static void _SBIPAddressFreeList( SBIPAddress
*inList
)
23071 SBIPAddress
* ipaddr
;
23073 while( ( ipaddr
= inList
) != NULL
)
23075 inList
= ipaddr
->next
;
23076 _SBIPAddressFree( ipaddr
);
23080 //===========================================================================================================================
23081 // _SBRDomainCreate
23082 //===========================================================================================================================
23084 static OSStatus
_SBRDomainCreate( const char *inName
, SBRDomain
**outDomain
)
23089 obj
= (SBRDomain
*) calloc( 1, sizeof( *obj
) );
23090 require_action( obj
, exit
, err
= kNoMemoryErr
);
23092 obj
->name
= strdup( inName
);
23093 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
23100 if( obj
) _SBRDomainFree( obj
);
23104 //===========================================================================================================================
23106 //===========================================================================================================================
23108 static void _SBRDomainFree( SBRDomain
*inDomain
)
23110 SBRServiceType
* type
;
23112 ForgetMem( &inDomain
->name
);
23113 while( ( type
= inDomain
->typeList
) != NULL
)
23115 inDomain
->typeList
= type
->next
;
23116 _SBRServiceTypeFree( type
);
23121 //===========================================================================================================================
23122 // _SBRServiceTypeCreate
23123 //===========================================================================================================================
23125 static OSStatus
_SBRServiceTypeCreate( const char *inName
, SBRServiceType
**outType
)
23128 SBRServiceType
* obj
;
23130 obj
= (SBRServiceType
*) calloc( 1, sizeof( *obj
) );
23131 require_action( obj
, exit
, err
= kNoMemoryErr
);
23133 obj
->name
= strdup( inName
);
23134 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
23141 if( obj
) _SBRServiceTypeFree( obj
);
23145 //===========================================================================================================================
23146 // _SBRServiceTypeFree
23147 //===========================================================================================================================
23149 static void _SBRServiceTypeFree( SBRServiceType
*inType
)
23151 SBRServiceInstance
* instance
;
23153 ForgetMem( &inType
->name
);
23154 while( ( instance
= inType
->instanceList
) != NULL
)
23156 inType
->instanceList
= instance
->next
;
23157 _SBRServiceInstanceFree( instance
);
23162 //===========================================================================================================================
23163 // _SBRServiceInstanceCreate
23164 //===========================================================================================================================
23167 _SBRServiceInstanceCreate(
23168 const char * inName
,
23169 uint32_t inInterfaceIndex
,
23170 const char * inHostname
,
23172 const uint8_t * inTXTPtr
,
23174 uint64_t inDiscoverTimeUs
,
23175 uint64_t inResolveTimeUs
,
23176 SBRServiceInstance
** outInstance
)
23179 SBRServiceInstance
* obj
;
23181 obj
= (SBRServiceInstance
*) calloc( 1, sizeof( *obj
) );
23182 require_action( obj
, exit
, err
= kNoMemoryErr
);
23184 obj
->name
= strdup( inName
);
23185 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
23189 obj
->hostname
= strdup( inHostname
);
23190 require_action( obj
->hostname
, exit
, err
= kNoMemoryErr
);
23194 obj
->txtPtr
= (uint8_t *) _memdup( inTXTPtr
, inTXTLen
);
23195 require_action( obj
->txtPtr
, exit
, err
= kNoMemoryErr
);
23196 obj
->txtLen
= inTXTLen
;
23198 obj
->discoverTimeUs
= inDiscoverTimeUs
;
23199 obj
->resolveTimeUs
= inResolveTimeUs
;
23200 obj
->ifIndex
= inInterfaceIndex
;
23201 obj
->port
= inPort
;
23203 *outInstance
= obj
;
23208 if( obj
) _SBRServiceInstanceFree( obj
);
23212 //===========================================================================================================================
23213 // _SBRServiceInstanceFree
23214 //===========================================================================================================================
23216 static void _SBRServiceInstanceFree( SBRServiceInstance
*inInstance
)
23218 SBRIPAddress
* ipaddr
;
23220 ForgetMem( &inInstance
->name
);
23221 ForgetMem( &inInstance
->hostname
);
23222 ForgetMem( &inInstance
->txtPtr
);
23223 while( ( ipaddr
= inInstance
->ipaddrList
) != NULL
)
23225 inInstance
->ipaddrList
= ipaddr
->next
;
23226 _SBRIPAddressFree( ipaddr
);
23228 free( inInstance
);
23231 //===========================================================================================================================
23232 // _SBRIPAddressCreate
23233 //===========================================================================================================================
23236 _SBRIPAddressCreate(
23237 const struct sockaddr
* inSockAddr
,
23238 uint64_t inResolveTimeUs
,
23239 SBRIPAddress
** outIPAddress
)
23242 SBRIPAddress
* obj
;
23244 obj
= (SBRIPAddress
*) calloc( 1, sizeof( *obj
) );
23245 require_action( obj
, exit
, err
= kNoMemoryErr
);
23247 SockAddrCopy( inSockAddr
, &obj
->sip
);
23248 obj
->resolveTimeUs
= inResolveTimeUs
;
23250 *outIPAddress
= obj
;
23257 //===========================================================================================================================
23258 // _SBRIPAddressFree
23259 //===========================================================================================================================
23261 static void _SBRIPAddressFree( SBRIPAddress
*inIPAddress
)
23263 free( inIPAddress
);
23266 //===========================================================================================================================
23269 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
23270 //===========================================================================================================================
23272 static OSStatus
_SocketWriteAll( SocketRef inSock
, const void *inData
, size_t inSize
, int32_t inTimeoutSecs
)
23275 const uint8_t * src
;
23276 const uint8_t * end
;
23278 struct timeval timeout
;
23281 FD_ZERO( &writeSet
);
23282 src
= (const uint8_t *) inData
;
23283 end
= src
+ inSize
;
23286 FD_SET( inSock
, &writeSet
);
23287 timeout
.tv_sec
= inTimeoutSecs
;
23288 timeout
.tv_usec
= 0;
23289 n
= select( (int)( inSock
+ 1 ), NULL
, &writeSet
, NULL
, &timeout
);
23290 if( n
== 0 ) { err
= kTimeoutErr
; goto exit
; }
23291 err
= map_socket_value_errno( inSock
, n
> 0, n
);
23292 require_noerr( err
, exit
);
23294 n
= send( inSock
, (char *) src
, (size_t)( end
- src
), 0 );
23295 err
= map_socket_value_errno( inSock
, n
>= 0, n
);
23296 if( err
== EINTR
) continue;
23297 require_noerr( err
, exit
);
23307 //===========================================================================================================================
23308 // _ParseIPv4Address
23310 // Warning: "inBuffer" may be modified even in error cases.
23312 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
23313 //===========================================================================================================================
23315 static OSStatus
_ParseIPv4Address( const char *inStr
, uint8_t inBuffer
[ 4 ], const char **outStr
)
23331 for( ; ( c
= *inStr
) != '\0'; ++inStr
)
23333 if( isdigit_safe( c
) )
23335 v
= ( *dst
* 10 ) + ( c
- '0' );
23336 require_action_quiet( v
<= 255, exit
, err
= kRangeErr
);
23337 *dst
= (uint8_t) v
;
23341 require_action_quiet( segments
<= 4, exit
, err
= kOverrunErr
);
23345 else if( ( c
== '.' ) && sawDigit
)
23347 require_action_quiet( segments
< 4, exit
, err
= kMalformedErr
);
23356 require_action_quiet( segments
== 4, exit
, err
= kUnderrunErr
);
23365 //===========================================================================================================================
23366 // _StringToIPv4Address
23368 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
23369 //===========================================================================================================================
23372 _StringToIPv4Address(
23373 const char * inStr
,
23374 StringToIPAddressFlags inFlags
,
23377 uint32_t * outSubnet
,
23378 uint32_t * outRouter
,
23379 const char ** outStr
)
23389 uint32_t subnetMask
;
23392 require_action( inStr
, exit
, err
= kParamErr
);
23394 // Parse the address-only part of the address (e.g. "1.2.3.4").
23396 err
= _ParseIPv4Address( inStr
, buf
, &inStr
);
23397 require_noerr_quiet( err
, exit
);
23398 ip
= (uint32_t)( ( buf
[ 0 ] << 24 ) | ( buf
[ 1 ] << 16 ) | ( buf
[ 2 ] << 8 ) | buf
[ 3 ] );
23401 // Parse the port (if any).
23407 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPort
), exit
, err
= kUnexpectedErr
);
23408 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) port
= ( port
* 10 ) + ( c
- '0' );
23409 require_action_quiet( port
<= 65535, exit
, err
= kRangeErr
);
23413 // Parse the prefix length (if any).
23421 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPrefix
), exit
, err
= kUnexpectedErr
);
23422 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) prefix
= ( prefix
* 10 ) + ( c
- '0' );
23423 require_action_quiet( ( prefix
>= 0 ) && ( prefix
<= 32 ), exit
, err
= kRangeErr
);
23426 subnetMask
= ( prefix
> 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix
) ) : 0;
23427 router
= ( ip
& subnetMask
) | 1;
23430 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
23432 if( outIP
) *outIP
= ip
;
23433 if( outPort
&& hasPort
) *outPort
= port
;
23434 if( outSubnet
&& hasPrefix
) *outSubnet
= subnetMask
;
23435 if( outRouter
&& hasPrefix
) *outRouter
= router
;
23436 if( outStr
) *outStr
= inStr
;
23443 //===========================================================================================================================
23444 // _ParseIPv6Address
23446 // Note: Parsed according to the rules specified in RFC 3513.
23447 // Warning: "inBuffer" may be modified even in error cases.
23449 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23450 //===========================================================================================================================
23452 static OSStatus
_ParseIPv6Address( const char *inStr
, int inAllowV4Mapped
, uint8_t inBuffer
[ 16 ], const char **outStr
)
23454 // Table to map uppercase hex characters - '0' to their numeric values.
23455 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
23456 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 };
23461 uint8_t * colonPtr
;
23468 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
23470 for( i
= 0; i
< 16; ++i
) inBuffer
[ i
] = 0;
23472 // Special case leading :: (e.g. "::1") to simplify processing later.
23474 if( *inStr
== ':' )
23477 require_action_quiet( *inStr
== ':', exit
, err
= kMalformedErr
);
23480 // Parse the address.
23488 while( ( ( c
= *inStr
++ ) != '\0' ) && ( c
!= '%' ) && ( c
!= '/' ) && ( c
!= ']' ) )
23490 if( ( c
>= 'a' ) && ( c
<= 'f' ) ) c
-= ( 'a' - 'A' );
23491 if( ( ( c
>= '0' ) && ( c
<= '9' ) ) || ( ( c
>= 'A' ) && ( c
<= 'F' ) ) )
23494 check( c
< (int) countof( kASCIItoHexTable
) );
23495 v
= ( v
<< 4 ) | kASCIItoHexTable
[ c
];
23496 require_action_quiet( v
<= 0xFFFF, exit
, err
= kRangeErr
);
23505 require_action_quiet( !colonPtr
, exit
, err
= kMalformedErr
);
23509 require_action_quiet( *inStr
!= '\0', exit
, err
= kUnderrunErr
);
23510 require_action_quiet( ( dst
+ 2 ) <= lim
, exit
, err
= kOverrunErr
);
23511 *dst
++ = (uint8_t)( ( v
>> 8 ) & 0xFF );
23512 *dst
++ = (uint8_t)( v
& 0xFF );
23518 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
23520 if( inAllowV4Mapped
&& ( c
== '.' ) && ( ( dst
+ 4 ) <= lim
) )
23522 err
= _ParseIPv4Address( ptr
, dst
, &inStr
);
23523 require_noerr_quiet( err
, exit
);
23526 ++inStr
; // Increment because the code below expects the end to be at "inStr - 1".
23532 require_action_quiet( ( dst
+ 2 ) <= lim
, exit
, err
= kOverrunErr
);
23533 *dst
++ = (uint8_t)( ( v
>> 8 ) & 0xFF );
23534 *dst
++ = (uint8_t)( v
& 0xFF );
23536 check( dst
<= lim
);
23539 require_action_quiet( dst
< lim
, exit
, err
= kOverrunErr
);
23540 n
= (int)( dst
- colonPtr
);
23541 for( i
= 1; i
<= n
; ++i
)
23543 lim
[ -i
] = colonPtr
[ n
- i
];
23544 colonPtr
[ n
- i
] = 0;
23548 require_action_quiet( dst
== lim
, exit
, err
= kUnderrunErr
);
23550 *outStr
= inStr
- 1;
23557 //===========================================================================================================================
23560 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23561 //===========================================================================================================================
23563 static OSStatus
_ParseIPv6Scope( const char *inStr
, uint32_t *outScope
, const char **outStr
)
23565 #if( TARGET_OS_POSIX )
23567 char scopeStr
[ 64 ];
23574 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
23577 lim
= dst
+ ( countof( scopeStr
) - 1 );
23578 while( ( ( c
= *inStr
) != '\0' ) && ( c
!= ':' ) && ( c
!= '/' ) && ( c
!= ']' ) && ( dst
< lim
) )
23583 check( dst
<= lim
);
23585 // First try to map as a name and if that fails, treat it as a numeric scope.
23587 scope
= if_nametoindex( scopeStr
);
23590 for( ptr
= scopeStr
; ( ( c
= *ptr
) >= '0' ) && ( c
<= '9' ); ++ptr
)
23592 scope
= ( scope
* 10 ) + ( ( (uint8_t) c
) - '0' );
23594 require_action_quiet( c
== '\0', exit
, err
= kMalformedErr
);
23595 require_action_quiet( ( ptr
!= scopeStr
) && ( ( (int)( ptr
- scopeStr
) ) <= 10 ), exit
, err
= kMalformedErr
);
23607 const char * start
;
23611 for( start
= inStr
; ( ( c
= *inStr
) >= '0' ) && ( c
<= '9' ); ++inStr
)
23613 scope
= ( scope
* 10 ) + ( c
- '0' );
23615 require_action_quiet( ( inStr
!= start
) && ( ( (int)( inStr
- start
) ) <= 10 ), exit
, err
= kMalformedErr
);
23626 //===========================================================================================================================
23627 // _StringToIPv6Address
23629 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23630 //===========================================================================================================================
23633 _StringToIPv6Address(
23634 const char * inStr
,
23635 StringToIPAddressFlags inFlags
,
23636 uint8_t outIPv6
[ 16 ],
23637 uint32_t * outScope
,
23640 const char ** outStr
)
23643 uint8_t ipv6
[ 16 ];
23654 require_action( inStr
, exit
, err
= kParamErr
);
23656 if( *inStr
== '[' ) ++inStr
; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
23658 // Parse the address-only part of the address (e.g. "1::1").
23660 err
= _ParseIPv6Address( inStr
, !( inFlags
& kStringToIPAddressFlagsNoIPv4Mapped
), ipv6
, &inStr
);
23661 require_noerr_quiet( err
, exit
);
23664 // Parse the scope, port, or prefix length.
23675 if( c
== '%' ) // Scope (e.g. "%en0" or "%5")
23677 require_action_quiet( !hasScope
, exit
, err
= kMalformedErr
);
23678 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoScope
), exit
, err
= kUnexpectedErr
);
23680 err
= _ParseIPv6Scope( inStr
, &scope
, &inStr
);
23681 require_noerr_quiet( err
, exit
);
23685 else if( c
== ':' ) // Port (e.g. ":80")
23687 require_action_quiet( !hasPort
, exit
, err
= kMalformedErr
);
23688 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPort
), exit
, err
= kUnexpectedErr
);
23689 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) port
= ( port
* 10 ) + ( c
- '0' );
23690 require_action_quiet( port
<= 65535, exit
, err
= kRangeErr
);
23693 else if( c
== '/' ) // Prefix Length (e.g. "/64")
23695 require_action_quiet( !hasPrefix
, exit
, err
= kMalformedErr
);
23696 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPrefix
), exit
, err
= kUnexpectedErr
);
23697 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) prefix
= ( prefix
* 10 ) + ( c
- '0' );
23698 require_action_quiet( ( prefix
>= 0 ) && ( prefix
<= 128 ), exit
, err
= kRangeErr
);
23701 else if( c
== ']' )
23703 require_action_quiet( !hasBracket
, exit
, err
= kMalformedErr
);
23713 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
23715 if( outIPv6
) for( i
= 0; i
< 16; ++i
) outIPv6
[ i
] = ipv6
[ i
];
23716 if( outScope
&& hasScope
) *outScope
= scope
;
23717 if( outPort
&& hasPort
) *outPort
= port
;
23718 if( outPrefix
&& hasPrefix
) *outPrefix
= prefix
;
23719 if( outStr
) *outStr
= inStr
;
23726 //===========================================================================================================================
23727 // _StringArray_Free
23729 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
23730 //===========================================================================================================================
23732 static void _StringArray_Free( char **inArray
, size_t inCount
)
23736 for( i
= 0; i
< inCount
; ++i
)
23738 free( inArray
[ i
] );
23740 if( inCount
> 0 ) free( inArray
);
23743 //===========================================================================================================================
23744 // _ParseQuotedEscapedString
23746 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
23747 //===========================================================================================================================
23750 _ParseQuotedEscapedString(
23751 const char * inSrc
,
23752 const char * inEnd
,
23753 const char * inDelimiters
,
23756 size_t * outCopiedLen
,
23757 size_t * outTotalLen
,
23758 const char ** outSrc
)
23760 const unsigned char * src
;
23761 const unsigned char * end
;
23762 unsigned char * dst
;
23763 unsigned char * lim
;
23767 Boolean singleQuote
;
23768 Boolean doubleQuote
;
23770 if( inEnd
== NULL
) inEnd
= inSrc
+ strlen( inSrc
);
23771 src
= (const unsigned char *) inSrc
;
23772 end
= (const unsigned char *) inEnd
;
23773 dst
= (unsigned char *) inBuf
;
23774 lim
= dst
+ inMaxLen
;
23775 while( ( src
< end
) && isspace_safe( *src
) ) ++src
; // Skip leading spaces.
23776 if( src
>= end
) return( false );
23778 // Parse each argument from the string.
23780 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
23783 singleQuote
= false;
23784 doubleQuote
= false;
23790 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
23794 singleQuote
= false;
23798 else if( doubleQuote
)
23800 // Double quotes protect everything except double quotes and backslashes. A backslash can be
23801 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
23802 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
23803 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
23804 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
23808 doubleQuote
= false;
23811 else if( c
== '\\' )
23816 if( ( c2
== '"' ) || ( c2
== '\\' ) )
23821 else if( c2
== '\n' )
23826 else if( ( c2
== 'x' ) || ( c2
== 'X' ) )
23830 if( ( ( end
- src
) >= 2 ) && IsHexPair( src
) )
23832 c
= HexPairToByte( src
);
23836 else if( isoctal_safe( c2
) )
23838 if( ( ( end
- src
) >= 3 ) && IsOctalTriple( src
) )
23840 c
= OctalTripleToByte( src
);
23847 else if( strchr( inDelimiters
, c
) )
23851 else if( c
== '\\' )
23853 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
23854 // A backslash followed by a newline disappears completely.
23855 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
23856 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
23866 else if( ( c
== 'x' ) || ( c
== 'X' ) )
23869 if( ( ( end
- src
) >= 2 ) && IsHexPair( src
) )
23871 c
= HexPairToByte( src
);
23875 else if( isoctal_safe( c
) )
23877 if( ( ( end
- src
) >= 3 ) && IsOctalTriple( src
) )
23879 c
= OctalTripleToByte( src
);
23893 else if( c
== '\'' )
23895 singleQuote
= true;
23898 else if( c
== '"' )
23900 doubleQuote
= true;
23906 if( inBuf
) *dst
= c
;
23912 if( outCopiedLen
) *outCopiedLen
= (size_t)( dst
- ( (unsigned char *) inBuf
) );
23913 if( outTotalLen
) *outTotalLen
= totalLen
;
23914 if( outSrc
) *outSrc
= (const char *) src
;
23918 //===========================================================================================================================
23919 // _ServerSocketOpenEx2
23921 // Note: Based on ServerSocketOpenEx() from CoreUtils. Added parameter to not use SO_REUSEPORT.
23922 //===========================================================================================================================
23925 _ServerSocketOpenEx2(
23929 const void * inAddr
,
23933 Boolean inNoPortReuse
,
23934 SocketRef
* outSock
)
23944 port
= ( inPort
< 0 ) ? -inPort
: inPort
; // Negated port number means "try this port, but allow dynamic".
23946 sock
= socket( inFamily
, inType
, inProtocol
);
23947 err
= map_socket_creation_errno( sock
);
23948 require_noerr_quiet( err
, exit
);
23950 #if( defined( SO_NOSIGPIPE ) )
23951 setsockopt( sock
, SOL_SOCKET
, SO_NOSIGPIPE
, &(int){ 1 }, (socklen_t
) sizeof( int ) );
23954 err
= SocketMakeNonBlocking( sock
);
23955 require_noerr( err
, exit
);
23957 // Set receive buffer size. This has to be done on the listening socket *before* listen is called because
23958 // accept does not return until after the window scale option is exchanged during the 3-way handshake.
23959 // Since accept returns a new socket, the only way to use a larger window scale option is to set the buffer
23960 // size on the listening socket since SO_RCVBUF is inherited by the accepted socket. See UNPv1e3 Section 7.5.
23962 err
= SocketSetBufferSize( sock
, SO_RCVBUF
, inRcvBufSize
);
23963 check_noerr( err
);
23965 // Allow port or address reuse because we may bind separate IPv4 and IPv6 sockets to the same port.
23967 if( ( inType
!= SOCK_DGRAM
) || !inNoPortReuse
)
23970 name
= ( inType
== SOCK_DGRAM
) ? SO_REUSEPORT
: SO_REUSEADDR
;
23971 err
= setsockopt( sock
, SOL_SOCKET
, name
, (char *) &option
, (socklen_t
) sizeof( option
) );
23972 err
= map_socket_noerr_errno( sock
, err
);
23973 require_noerr( err
, exit
);
23976 if( inFamily
== AF_INET
)
23978 // Bind to the port. If it fails, retry with a dynamic port.
23980 memset( &sip
.v4
, 0, sizeof( sip
.v4
) );
23981 SIN_LEN_SET( &sip
.v4
);
23982 sip
.v4
.sin_family
= AF_INET
;
23983 sip
.v4
.sin_port
= htons( (uint16_t) port
);
23984 sip
.v4
.sin_addr
.s_addr
= inAddr
? *( (const uint32_t *) inAddr
) : htonl( INADDR_ANY
);
23985 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v4
) );
23986 err
= map_socket_noerr_errno( sock
, err
);
23987 if( err
&& ( inPort
< 0 ) )
23989 sip
.v4
.sin_port
= 0;
23990 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v4
) );
23991 err
= map_socket_noerr_errno( sock
, err
);
23993 require_noerr( err
, exit
);
23995 #if( defined( AF_INET6 ) )
23996 else if( inFamily
== AF_INET6
)
23998 // Restrict this socket to IPv6 only because we're going to use a separate socket for IPv4.
24001 err
= setsockopt( sock
, IPPROTO_IPV6
, IPV6_V6ONLY
, (char *) &option
, (socklen_t
) sizeof( option
) );
24002 err
= map_socket_noerr_errno( sock
, err
);
24003 require_noerr( err
, exit
);
24005 // Bind to the port. If it fails, retry with a dynamic port.
24007 memset( &sip
.v6
, 0, sizeof( sip
.v6
) );
24008 SIN6_LEN_SET( &sip
.v6
);
24009 sip
.v6
.sin6_family
= AF_INET6
;
24010 sip
.v6
.sin6_port
= htons( (uint16_t) port
);
24011 sip
.v6
.sin6_addr
= inAddr
? *( (const struct in6_addr
*) inAddr
) : in6addr_any
;
24012 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v6
) );
24013 err
= map_socket_noerr_errno( sock
, err
);
24014 if( err
&& ( inPort
< 0 ) )
24016 sip
.v6
.sin6_port
= 0;
24017 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v6
) );
24018 err
= map_socket_noerr_errno( sock
, err
);
24020 require_noerr( err
, exit
);
24025 dlogassert( "Unsupported family: %d", inFamily
);
24026 err
= kUnsupportedErr
;
24030 if( inType
== SOCK_STREAM
)
24032 err
= listen( sock
, SOMAXCONN
);
24033 err
= map_socket_noerr_errno( sock
, err
);
24036 err
= listen( sock
, 5 );
24037 err
= map_socket_noerr_errno( sock
, err
);
24038 require_noerr( err
, exit
);
24044 len
= (socklen_t
) sizeof( sip
);
24045 err
= getsockname( sock
, &sip
.sa
, &len
);
24046 err
= map_socket_noerr_errno( sock
, err
);
24047 require_noerr( err
, exit
);
24049 *outPort
= SockAddrGetPort( &sip
);
24052 sock
= kInvalidSocketRef
;
24055 ForgetSocket( &sock
);
24059 //===========================================================================================================================
24062 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24063 //===========================================================================================================================
24065 static void * _memdup( const void *inPtr
, size_t inLen
)
24069 mem
= malloc( ( inLen
> 0 ) ? inLen
: 1 ); // If inLen is 0, use 1 since malloc( 0 ) is not well defined.
24070 require( mem
, exit
);
24071 if( inLen
> 0 ) memcpy( mem
, inPtr
, inLen
);
24077 //===========================================================================================================================
24080 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24081 //===========================================================================================================================
24083 static int _memicmp( const void *inP1
, const void *inP2
, size_t inLen
)
24085 const unsigned char * p1
;
24086 const unsigned char * e1
;
24087 const unsigned char * p2
;
24091 p1
= (const unsigned char *) inP1
;
24093 p2
= (const unsigned char *) inP2
;
24098 c1
= tolower( c1
);
24099 c2
= tolower( c2
);
24100 if( c1
< c2
) return( -1 );
24101 if( c1
> c2
) return( 1 );
24106 //===========================================================================================================================
24109 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24110 //===========================================================================================================================
24112 static uint32_t _FNV1( const void *inData
, size_t inSize
)
24114 const uint8_t * src
= (const uint8_t *) inData
;
24115 const uint8_t * const end
= src
+ inSize
;
24118 hash
= 0x811c9dc5U
;
24119 while( src
!= end
)
24121 hash
*= 0x01000193;