2 Copyright (c) 2016-2019 Apple Inc. All rights reserved.
4 dnssdutil is a command-line utility for testing the DNS-SD API.
7 #include "DNSMessage.h"
9 #include <CoreUtils/CoreUtils.h>
11 #include <dns_sd_private.h>
13 #include CF_RUNTIME_HEADER
15 #if( TARGET_OS_DARWIN )
16 #include <CFNetwork/CFHost.h>
17 #include <CoreFoundation/CoreFoundation.h>
18 #include <SystemConfiguration/SCPrivate.h>
24 #include <sys/proc_info.h>
28 #if( TARGET_OS_POSIX )
29 #include <sys/resource.h>
32 #if( !defined( DNSSDUTIL_INCLUDE_DNSCRYPT ) )
33 #define DNSSDUTIL_INCLUDE_DNSCRYPT 0
36 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
37 #include "tweetnacl.h" // TweetNaCl from <https://tweetnacl.cr.yp.to/software.html>.
40 #if( !defined( MDNSRESPONDER_PROJECT ) )
41 #define MDNSRESPONDER_PROJECT 0
44 #if( MDNSRESPONDER_PROJECT )
45 #include <dns_services.h>
46 #include "mdns_private.h"
47 #include "TestUtils.h"
50 //===========================================================================================================================
52 //===========================================================================================================================
54 #define kDNSSDUtilNumVersion NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 )
56 #if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) )
57 #define DNSSDUTIL_SOURCE_VERSION "0.0.0"
60 #define kDNSSDUtilIdentifier "com.apple.dnssdutil"
62 //===========================================================================================================================
64 //===========================================================================================================================
66 // DNS-SD API flag descriptors
68 #define kDNSServiceFlagsDescriptors \
69 "\x00" "AutoTrigger\0" \
72 "\x03" "NoAutoRename\0" \
75 "\x06" "BrowseDomains\0" \
76 "\x07" "RegistrationDomains\0" \
77 "\x08" "LongLivedQuery\0" \
78 "\x09" "AllowRemoteQuery\0" \
79 "\x0A" "ForceMulticast\0" \
80 "\x0B" "KnownUnique\0" \
81 "\x0C" "ReturnIntermediates\0" \
82 "\x0D" "DenyConstrained\0" \
83 "\x0E" "ShareConnection\0" \
84 "\x0F" "SuppressUnusable\0" \
86 "\x11" "IncludeP2P\0" \
87 "\x12" "WakeOnResolve\0" \
88 "\x13" "BackgroundTrafficClass\0" \
89 "\x14" "IncludeAWDL\0" \
91 "\x16" "UnicastResponse\0" \
92 "\x17" "ValidateOptional\0" \
93 "\x18" "WakeOnlyService\0" \
94 "\x19" "ThresholdOne\0" \
95 "\x1A" "ThresholdFinder\0" \
96 "\x1B" "DenyCellular\0" \
97 "\x1C" "ServiceIndex\0" \
98 "\x1D" "DenyExpensive\0" \
99 "\x1E" "PathEvaluationDone\0" \
100 "\x1F" "AllowExpiredAnswers\0" \
103 #define DNSServiceFlagsToAddRmvStr( FLAGS ) ( ( (FLAGS) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" )
105 #define kDNSServiceProtocolDescriptors \
112 #define kBadDNSServiceRef ( (DNSServiceRef)(intptr_t) -1 )
114 //===========================================================================================================================
116 //===========================================================================================================================
119 #define kDNSMaxUDPMessageSize 512
120 #define kDNSMaxTCPMessageSize UINT16_MAX
122 #define kDNSRecordDataLengthMax UINT16_MAX
124 //===========================================================================================================================
126 //===========================================================================================================================
128 #define kMDNSPort 5353
130 #define kDefaultMDNSMessageID 0
131 #define kDefaultMDNSQueryFlags 0
133 #define kQClassUnicastResponseBit ( 1U << 15 )
134 #define kRRClassCacheFlushBit ( 1U << 15 )
136 // Recommended Resource Record TTL values. See <https://tools.ietf.org/html/rfc6762#section-10>.
138 #define kMDNSRecordTTL_Host 120 // TTL for resource records related to a host name, e.g., A, AAAA, SRV, etc.
139 #define kMDNSRecordTTL_Other 4500 // TTL for other resource records.
141 // Maximum mDNS Message Size. See <https://tools.ietf.org/html/rfc6762#section-17>.
143 #define kMDNSMessageSizeMax 8952 // 9000 B (Ethernet jumbo frame max size) - 40 B (IPv6 header) - 8 B (UDP header)
145 #define kLocalStr "\x05" "local"
146 #define kLocalLabel ( (const uint8_t *) kLocalStr )
147 #define kLocalName ( (const uint8_t *) kLocalStr )
148 #define kLocalNameLen sizeof( kLocalStr )
150 //===========================================================================================================================
151 // Test Address Blocks
152 //===========================================================================================================================
154 // IPv4 address block 203.0.113.0/24 (TEST-NET-3) is reserved for documentation. See <https://tools.ietf.org/html/rfc5737>.
156 #define kDNSServerBaseAddrV4 UINT32_C( 0xCB007100 ) // 203.0.113.0/24
158 // IPv6 address block 2001:db8::/32 is reserved for documentation. See <https://tools.ietf.org/html/rfc3849>.
160 static const uint8_t kDNSServerBaseAddrV6
[] =
162 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:1::/120
165 static const uint8_t kMDNSReplierBaseAddrV6
[] =
167 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:2::/96
170 check_compile_time( sizeof( kDNSServerBaseAddrV6
) == 16 );
171 check_compile_time( sizeof( kMDNSReplierBaseAddrV6
) == 16 );
173 // Bad IPv4 and IPv6 Address Blocks
174 // Used by the DNS server when it needs to respond with intentionally "bad" A/AAAA record data, i.e., IP addresses neither
175 // in 203.0.113.0/24 nor 2001:db8:1::/120.
177 #define kDNSServerBadBaseAddrV4 UINT32_C( 0x00000000 ) // 0.0.0.0/24
179 static const uint8_t kDNSServerBadBaseAddrV6
[] =
181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 // ::ffff:0:0/120
184 check_compile_time( sizeof( kDNSServerBadBaseAddrV6
) == 16 );
186 //===========================================================================================================================
188 //===========================================================================================================================
190 #define kLowerAlphaNumericCharSet "abcdefghijklmnopqrstuvwxyz0123456789"
191 #define kLowerAlphaNumericCharSetSize sizeof_string( kLowerAlphaNumericCharSet )
193 #if( !defined( kWhiteSpaceCharSet ) )
194 #define kWhiteSpaceCharSet "\t\n\v\f\r "
197 // Note: strcpy_literal() appears in CoreUtils code, but isn't currently defined in framework headers.
199 #if( !defined( strcpy_literal ) )
200 #define strcpy_literal( DST, SRC ) memcpy( DST, SRC, sizeof( SRC ) )
203 #define _RandomStringExact( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, OUT_STRING ) \
204 RandomString( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, CHAR_COUNT, OUT_STRING )
206 #define kNoSuchRecordStr "No Such Record"
207 #define kNoSuchRecordAStr "No Such Record (A)"
208 #define kNoSuchRecordAAAAStr "No Such Record (AAAA)"
210 #define kRootLabel ( (const uint8_t *) "" )
212 //===========================================================================================================================
213 // Gerneral Command Options
214 //===========================================================================================================================
216 // Command option macros
218 #define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON ) \
219 CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None, \
222 #define kRequiredOptionSuffix " [REQUIRED]"
224 #define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
225 CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, \
226 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
227 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
229 #define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
230 MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
232 #define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
233 CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
234 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
235 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
237 #define DoubleOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
238 CLI_OPTION_DOUBLE_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
239 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
240 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
242 #define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \
243 CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL )
245 #define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
246 CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
247 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
248 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
250 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
251 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
253 #define CFStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
254 CLI_OPTION_CFSTRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
255 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
256 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
258 // DNS-SD API flag options
260 static int gDNSSDFlags
= 0;
261 static int gDNSSDFlag_AllowExpiredAnswers
= false;
262 static int gDNSSDFlag_BrowseDomains
= false;
263 static int gDNSSDFlag_DenyCellular
= false;
264 static int gDNSSDFlag_DenyConstrained
= false;
265 static int gDNSSDFlag_DenyExpensive
= false;
266 static int gDNSSDFlag_ForceMulticast
= false;
267 static int gDNSSDFlag_IncludeAWDL
= false;
268 static int gDNSSDFlag_KnownUnique
= false;
269 static int gDNSSDFlag_NoAutoRename
= false;
270 static int gDNSSDFlag_PathEvaluationDone
= false;
271 static int gDNSSDFlag_RegistrationDomains
= false;
272 static int gDNSSDFlag_ReturnIntermediates
= false;
273 static int gDNSSDFlag_Shared
= false;
274 static int gDNSSDFlag_SuppressUnusable
= false;
275 static int gDNSSDFlag_Timeout
= false;
276 static int gDNSSDFlag_UnicastResponse
= false;
277 static int gDNSSDFlag_Unique
= false;
278 static int gDNSSDFlag_WakeOnResolve
= false;
280 #define DNSSDFlagsOption() \
281 IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \
282 "DNSServiceFlags as an integer. This value is bitwise ORed with other single flag options.", false )
284 #define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \
285 BooleanOption( SHORT_CHAR, # FLAG_NAME, &gDNSSDFlag_ ## FLAG_NAME, "Use kDNSServiceFlags" # FLAG_NAME "." )
287 #define DNSSDFlagsOption_AllowExpiredAnswers() DNSSDFlagOption( 'X', AllowExpiredAnswers )
288 #define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular )
289 #define DNSSDFlagsOption_DenyConstrained() DNSSDFlagOption( 'R', DenyConstrained)
290 #define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive )
291 #define DNSSDFlagsOption_ForceMulticast() DNSSDFlagOption( 'M', ForceMulticast )
292 #define DNSSDFlagsOption_IncludeAWDL() DNSSDFlagOption( 'A', IncludeAWDL )
293 #define DNSSDFlagsOption_KnownUnique() DNSSDFlagOption( 'K', KnownUnique )
294 #define DNSSDFlagsOption_NoAutoRename() DNSSDFlagOption( 'N', NoAutoRename )
295 #define DNSSDFlagsOption_PathEvalDone() DNSSDFlagOption( 'P', PathEvaluationDone )
296 #define DNSSDFlagsOption_ReturnIntermediates() DNSSDFlagOption( 'I', ReturnIntermediates )
297 #define DNSSDFlagsOption_Shared() DNSSDFlagOption( 'S', Shared )
298 #define DNSSDFlagsOption_SuppressUnusable() DNSSDFlagOption( 'S', SuppressUnusable )
299 #define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout )
300 #define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse )
301 #define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique )
302 #define DNSSDFlagsOption_WakeOnResolve() DNSSDFlagOption( 'W', WakeOnResolve )
306 static const char * gInterface
= NULL
;
308 #define InterfaceOption() \
309 StringOption( 'i', "interface", &gInterface, "interface", \
310 "Network interface by name or index. Use index -1 for local-only.", false )
312 // Connection options
314 #define kConnectionArg_Normal ""
315 #define kConnectionArgPrefix_PID "pid:"
316 #define kConnectionArgPrefix_UUID "uuid:"
318 static const char * gConnectionOpt
= kConnectionArg_Normal
;
320 #define ConnectionOptions() \
321 { kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type", \
322 kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL, \
323 "Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL }
325 #define kConnectionSection_Name "Connection Option"
326 #define kConnectionSection_Text \
327 "The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n" \
328 "the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n" \
329 "specifying the connection option without an argument, i.e.,\n" \
333 "To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n" \
335 " --connection=pid:<PID>\n" \
337 "to specify the delegator by PID, or use\n" \
339 " --connection=uuid:<UUID>\n" \
341 "to specify the delegator by UUID.\n" \
343 "To not use a main connection at all, but instead perform operations on their own implicit connections, use\n" \
347 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
349 // Help text for record data options
351 #define kRDataArgPrefix_Domain "domain:"
352 #define kRDataArgPrefix_File "file:"
353 #define kRDataArgPrefix_HexString "hex:"
354 #define kRDataArgPrefix_IPv4 "ipv4:"
355 #define kRDataArgPrefix_IPv6 "ipv6:"
356 #define kRDataArgPrefix_SRV "srv:"
357 #define kRDataArgPrefix_String "string:"
358 #define kRDataArgPrefix_TXT "txt:"
360 #define kRecordDataSection_Name "Record Data Arguments"
361 #define kRecordDataSection_Text \
362 "A record data argument is specified in one of the following formats:\n" \
364 "Format Syntax Example\n" \
365 "Domain name domain:<domain name> domain:demo._test._tcp.local\n" \
366 "File containing record data file:<file path> file:/path/to/binary-rdata-file\n" \
367 "Hexadecimal string hex:<hex string> hex:c0000201 or hex:'C0 00 02 01'\n" \
368 "IPv4 address ipv4:<IPv4 address> ipv4:192.0.2.1\n" \
369 "IPv6 address ipv6:<IPv6 address> ipv6:2001:db8::1\n" \
370 "SRV record srv:<priority>,<weight>,<port>,<target> srv:0,0,64206,example.local\n" \
371 "String string:<string> string:'\\x09color=red'\n" \
372 "TXT record strings txt:<comma-delimited strings> txt:'vers=1.0,lang=en\\,es\\,fr,passreq'\n" \
374 "Note: The string format converts each \\xHH escape sequence into the octet represented by the HH hex digit pair.\n"
376 #define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text )
378 //===========================================================================================================================
380 //===========================================================================================================================
382 #define kOutputFormatStr_JSON "json"
383 #define kOutputFormatStr_XML "xml"
384 #define kOutputFormatStr_Binary "binary"
388 kOutputFormatType_Invalid
= 0,
389 kOutputFormatType_JSON
= 1,
390 kOutputFormatType_XML
= 2,
391 kOutputFormatType_Binary
= 3
395 #define FormatOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP, IS_REQUIRED ) \
396 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, "format", SHORT_HELP, IS_REQUIRED, \
398 "Use '" kOutputFormatStr_JSON "' for JavaScript Object Notation (JSON).\n" \
399 "Use '" kOutputFormatStr_XML "' for property list XML version 1.0.\n" \
400 "Use '" kOutputFormatStr_Binary "' for property list binary version 1.0.\n" \
404 //===========================================================================================================================
405 // Browse Command Options
406 //===========================================================================================================================
408 static char ** gBrowse_ServiceTypes
= NULL
;
409 static size_t gBrowse_ServiceTypesCount
= 0;
410 static const char * gBrowse_Domain
= NULL
;
411 static int gBrowse_DoResolve
= false;
412 static int gBrowse_QueryTXT
= false;
413 static int gBrowse_TimeLimitSecs
= 0;
415 static CLIOption kBrowseOpts
[] =
418 MultiStringOption( 't', "type", &gBrowse_ServiceTypes
, &gBrowse_ServiceTypesCount
, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ),
419 StringOption( 'd', "domain", &gBrowse_Domain
, "domain", "Domain in which to browse for the service type(s).", false ),
421 CLI_OPTION_GROUP( "Flags" ),
423 DNSSDFlagsOption_IncludeAWDL(),
425 CLI_OPTION_GROUP( "Operation" ),
427 BooleanOption( 0 , "resolve", &gBrowse_DoResolve
, "Resolve service instances." ),
428 BooleanOption( 0 , "queryTXT", &gBrowse_QueryTXT
, "Query TXT records of service instances." ),
429 IntegerOption( 'l', "timeLimit", &gBrowse_TimeLimitSecs
, "seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ),
435 //===========================================================================================================================
436 // GetAddrInfo Command Options
437 //===========================================================================================================================
439 static const char * gGetAddrInfo_Name
= NULL
;
440 static int gGetAddrInfo_ProtocolIPv4
= false;
441 static int gGetAddrInfo_ProtocolIPv6
= false;
442 static int gGetAddrInfo_OneShot
= false;
443 static int gGetAddrInfo_TimeLimitSecs
= 0;
445 static CLIOption kGetAddrInfoOpts
[] =
448 StringOption( 'n', "name", &gGetAddrInfo_Name
, "domain name", "Domain name to resolve.", true ),
449 BooleanOption( 0 , "ipv4", &gGetAddrInfo_ProtocolIPv4
, "Use kDNSServiceProtocol_IPv4." ),
450 BooleanOption( 0 , "ipv6", &gGetAddrInfo_ProtocolIPv6
, "Use kDNSServiceProtocol_IPv6." ),
452 CLI_OPTION_GROUP( "Flags" ),
454 DNSSDFlagsOption_AllowExpiredAnswers(),
455 DNSSDFlagsOption_DenyCellular(),
456 DNSSDFlagsOption_DenyConstrained(),
457 DNSSDFlagsOption_DenyExpensive(),
458 DNSSDFlagsOption_PathEvalDone(),
459 DNSSDFlagsOption_ReturnIntermediates(),
460 DNSSDFlagsOption_SuppressUnusable(),
461 DNSSDFlagsOption_Timeout(),
463 CLI_OPTION_GROUP( "Operation" ),
465 BooleanOption( 'o', "oneshot", &gGetAddrInfo_OneShot
, "Finish after first set of results." ),
466 IntegerOption( 'l', "timeLimit", &gGetAddrInfo_TimeLimitSecs
, "seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ),
472 //===========================================================================================================================
473 // QueryRecord Command Options
474 //===========================================================================================================================
476 static const char * gQueryRecord_Name
= NULL
;
477 static const char * gQueryRecord_Type
= NULL
;
478 static int gQueryRecord_OneShot
= false;
479 static int gQueryRecord_TimeLimitSecs
= 0;
480 static int gQueryRecord_RawRData
= false;
482 static CLIOption kQueryRecordOpts
[] =
485 StringOption( 'n', "name", &gQueryRecord_Name
, "domain name", "Full domain name of record to query.", true ),
486 StringOption( 't', "type", &gQueryRecord_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
488 CLI_OPTION_GROUP( "Flags" ),
490 DNSSDFlagsOption_AllowExpiredAnswers(),
491 DNSSDFlagsOption_DenyCellular(),
492 DNSSDFlagsOption_DenyConstrained(),
493 DNSSDFlagsOption_DenyExpensive(),
494 DNSSDFlagsOption_ForceMulticast(),
495 DNSSDFlagsOption_IncludeAWDL(),
496 DNSSDFlagsOption_PathEvalDone(),
497 DNSSDFlagsOption_ReturnIntermediates(),
498 DNSSDFlagsOption_SuppressUnusable(),
499 DNSSDFlagsOption_Timeout(),
500 DNSSDFlagsOption_UnicastResponse(),
502 CLI_OPTION_GROUP( "Operation" ),
504 BooleanOption( 'o', "oneshot", &gQueryRecord_OneShot
, "Finish after first set of results." ),
505 IntegerOption( 'l', "timeLimit", &gQueryRecord_TimeLimitSecs
, "seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ),
506 BooleanOption( 0 , "raw", &gQueryRecord_RawRData
, "Show record data as a hexdump." ),
512 //===========================================================================================================================
513 // Register Command Options
514 //===========================================================================================================================
516 static const char * gRegister_Name
= NULL
;
517 static const char * gRegister_Type
= NULL
;
518 static const char * gRegister_Domain
= NULL
;
519 static int gRegister_Port
= 0;
520 static const char * gRegister_TXT
= NULL
;
521 static int gRegister_LifetimeMs
= -1;
522 static const char ** gAddRecord_Types
= NULL
;
523 static size_t gAddRecord_TypesCount
= 0;
524 static const char ** gAddRecord_Data
= NULL
;
525 static size_t gAddRecord_DataCount
= 0;
526 static const char ** gAddRecord_TTLs
= NULL
;
527 static size_t gAddRecord_TTLsCount
= 0;
528 static const char * gUpdateRecord_Data
= NULL
;
529 static int gUpdateRecord_DelayMs
= 0;
530 static int gUpdateRecord_TTL
= 0;
532 static CLIOption kRegisterOpts
[] =
535 StringOption( 'n', "name", &gRegister_Name
, "service name", "Name of service.", false ),
536 StringOption( 't', "type", &gRegister_Type
, "service type", "Service type, e.g., \"_ssh._tcp\".", true ),
537 StringOption( 'd', "domain", &gRegister_Domain
, "domain", "Domain in which to advertise the service.", false ),
538 IntegerOption( 'p', "port", &gRegister_Port
, "port number", "Service's port number.", true ),
539 StringOption( 0 , "txt", &gRegister_TXT
, "record data", "The TXT record data. See " kRecordDataSection_Name
" below.", false ),
541 CLI_OPTION_GROUP( "Flags" ),
543 DNSSDFlagsOption_IncludeAWDL(),
544 DNSSDFlagsOption_KnownUnique(),
545 DNSSDFlagsOption_NoAutoRename(),
547 CLI_OPTION_GROUP( "Operation" ),
548 IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs
, "ms", "Lifetime of the service registration in milliseconds.", false ),
550 CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ),
551 StringOption( 0 , "updateData", &gUpdateRecord_Data
, "record data", "Record data for the record update. See " kRecordDataSection_Name
" below.", false ),
552 IntegerOption( 0 , "updateDelay", &gUpdateRecord_DelayMs
, "ms", "Number of milliseconds after registration to wait before record update.", false ),
553 IntegerOption( 0 , "updateTTL", &gUpdateRecord_TTL
, "seconds", "Time-to-live of the updated record.", false ),
555 CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ),
556 MultiStringOption( 0 , "addType", &gAddRecord_Types
, &gAddRecord_TypesCount
, "record type", "Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ),
557 MultiStringOptionEx( 0 , "addData", &gAddRecord_Data
, &gAddRecord_DataCount
, "record data", "Additional record's data. See " kRecordDataSection_Name
" below.", false, NULL
),
558 MultiStringOption( 0 , "addTTL", &gAddRecord_TTLs
, &gAddRecord_TTLsCount
, "seconds", "Time-to-live of additional record in seconds. Use '0' for default.", false ),
564 //===========================================================================================================================
565 // RegisterRecord Command Options
566 //===========================================================================================================================
568 static const char * gRegisterRecord_Name
= NULL
;
569 static const char * gRegisterRecord_Type
= NULL
;
570 static const char * gRegisterRecord_Data
= NULL
;
571 static int gRegisterRecord_TTL
= 0;
572 static int gRegisterRecord_LifetimeMs
= -1;
573 static const char * gRegisterRecord_UpdateData
= NULL
;
574 static int gRegisterRecord_UpdateDelayMs
= 0;
575 static int gRegisterRecord_UpdateTTL
= 0;
577 static CLIOption kRegisterRecordOpts
[] =
580 StringOption( 'n', "name", &gRegisterRecord_Name
, "record name", "Fully qualified domain name of record.", true ),
581 StringOption( 't', "type", &gRegisterRecord_Type
, "record type", "Record type by name (e.g., TXT, PTR, A) or number.", true ),
582 StringOption( 'd', "data", &gRegisterRecord_Data
, "record data", "The record data. See " kRecordDataSection_Name
" below.", false ),
583 IntegerOption( 0 , "ttl", &gRegisterRecord_TTL
, "seconds", "Time-to-live in seconds. Use '0' for default.", false ),
585 CLI_OPTION_GROUP( "Flags" ),
587 DNSSDFlagsOption_IncludeAWDL(),
588 DNSSDFlagsOption_KnownUnique(),
589 DNSSDFlagsOption_Shared(),
590 DNSSDFlagsOption_Unique(),
592 CLI_OPTION_GROUP( "Operation" ),
593 IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs
, "ms", "Lifetime of the service registration in milliseconds.", false ),
595 CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ),
596 StringOption( 0 , "updateData", &gRegisterRecord_UpdateData
, "record data", "Record data for the record update.", false ),
597 IntegerOption( 0 , "updateDelay", &gRegisterRecord_UpdateDelayMs
, "ms", "Number of milliseconds after registration to wait before record update.", false ),
598 IntegerOption( 0 , "updateTTL", &gRegisterRecord_UpdateTTL
, "seconds", "Time-to-live of the updated record.", false ),
604 //===========================================================================================================================
605 // Resolve Command Options
606 //===========================================================================================================================
608 static char * gResolve_Name
= NULL
;
609 static char * gResolve_Type
= NULL
;
610 static char * gResolve_Domain
= NULL
;
611 static int gResolve_TimeLimitSecs
= 0;
613 static CLIOption kResolveOpts
[] =
616 StringOption( 'n', "name", &gResolve_Name
, "service name", "Name of the service instance to resolve.", true ),
617 StringOption( 't', "type", &gResolve_Type
, "service type", "Type of the service instance to resolve.", true ),
618 StringOption( 'd', "domain", &gResolve_Domain
, "domain", "Domain of the service instance to resolve.", true ),
620 CLI_OPTION_GROUP( "Flags" ),
622 DNSSDFlagsOption_ForceMulticast(),
623 DNSSDFlagsOption_IncludeAWDL(),
624 DNSSDFlagsOption_ReturnIntermediates(),
625 DNSSDFlagsOption_WakeOnResolve(),
627 CLI_OPTION_GROUP( "Operation" ),
629 IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs
, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ),
635 //===========================================================================================================================
636 // Reconfirm Command Options
637 //===========================================================================================================================
639 static const char * gReconfirmRecord_Name
= NULL
;
640 static const char * gReconfirmRecord_Type
= NULL
;
641 static const char * gReconfirmRecord_Class
= NULL
;
642 static const char * gReconfirmRecord_Data
= NULL
;
644 static CLIOption kReconfirmOpts
[] =
647 StringOption( 'n', "name", &gReconfirmRecord_Name
, "record name", "Full name of the record to reconfirm.", true ),
648 StringOption( 't', "type", &gReconfirmRecord_Type
, "record type", "Type of the record to reconfirm.", true ),
649 StringOption( 'c', "class", &gReconfirmRecord_Class
, "record class", "Class of the record to reconfirm. Default class is IN.", false ),
650 StringOption( 'd', "data", &gReconfirmRecord_Data
, "record data", "The record data. See " kRecordDataSection_Name
" below.", false ),
652 CLI_OPTION_GROUP( "Flags" ),
659 //===========================================================================================================================
660 // getaddrinfo-POSIX Command Options
661 //===========================================================================================================================
663 static const char * gGAIPOSIX_HostName
= NULL
;
664 static const char * gGAIPOSIX_ServName
= NULL
;
665 static const char * gGAIPOSIX_Family
= NULL
;
666 static int gGAIPOSIXFlag_AddrConfig
= false;
667 static int gGAIPOSIXFlag_All
= false;
668 static int gGAIPOSIXFlag_CanonName
= false;
669 static int gGAIPOSIXFlag_NumericHost
= false;
670 static int gGAIPOSIXFlag_NumericServ
= false;
671 static int gGAIPOSIXFlag_Passive
= false;
672 static int gGAIPOSIXFlag_V4Mapped
= false;
673 #if( defined( AI_V4MAPPED_CFG ) )
674 static int gGAIPOSIXFlag_V4MappedCFG
= false;
676 #if( defined( AI_DEFAULT ) )
677 static int gGAIPOSIXFlag_Default
= false;
679 #if( defined( AI_UNUSABLE ) )
680 static int gGAIPOSIXFlag_Unusable
= false;
683 static CLIOption kGetAddrInfoPOSIXOpts
[] =
685 StringOption( 'n', "hostname", &gGAIPOSIX_HostName
, "hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ),
686 StringOption( 's', "servname", &gGAIPOSIX_ServName
, "servname", "Port number in decimal or service name from services(5).", false ),
688 CLI_OPTION_GROUP( "Hints" ),
689 StringOptionEx( 'f', "family", &gGAIPOSIX_Family
, "address family", "Address family to use for hints ai_family field.", false,
691 "Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n"
692 "address family is specified, then AF_UNSPEC is used.\n"
694 BooleanOption( 0 , "flag-addrconfig", &gGAIPOSIXFlag_AddrConfig
, "In hints ai_flags field, set AI_ADDRCONFIG." ),
695 BooleanOption( 0 , "flag-all", &gGAIPOSIXFlag_All
, "In hints ai_flags field, set AI_ALL." ),
696 BooleanOption( 0 , "flag-canonname", &gGAIPOSIXFlag_CanonName
, "In hints ai_flags field, set AI_CANONNAME." ),
697 BooleanOption( 0 , "flag-numerichost", &gGAIPOSIXFlag_NumericHost
, "In hints ai_flags field, set AI_NUMERICHOST." ),
698 BooleanOption( 0 , "flag-numericserv", &gGAIPOSIXFlag_NumericServ
, "In hints ai_flags field, set AI_NUMERICSERV." ),
699 BooleanOption( 0 , "flag-passive", &gGAIPOSIXFlag_Passive
, "In hints ai_flags field, set AI_PASSIVE." ),
700 BooleanOption( 0 , "flag-v4mapped", &gGAIPOSIXFlag_V4Mapped
, "In hints ai_flags field, set AI_V4MAPPED." ),
701 #if( defined( AI_V4MAPPED_CFG ) )
702 BooleanOption( 0 , "flag-v4mappedcfg", &gGAIPOSIXFlag_V4MappedCFG
, "In hints ai_flags field, set AI_V4MAPPED_CFG." ),
704 #if( defined( AI_DEFAULT ) )
705 BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default
, "In hints ai_flags field, set AI_DEFAULT." ),
707 #if( defined( AI_UNUSABLE ) )
708 BooleanOption( 0 , "flag-unusable", &gGAIPOSIXFlag_Unusable
, "In hints ai_flags field, set AI_UNUSABLE." ),
711 CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ),
715 //===========================================================================================================================
716 // ReverseLookup Command Options
717 //===========================================================================================================================
719 static const char * gReverseLookup_IPAddr
= NULL
;
720 static int gReverseLookup_OneShot
= false;
721 static int gReverseLookup_TimeLimitSecs
= 0;
723 static CLIOption kReverseLookupOpts
[] =
726 StringOption( 'a', "address", &gReverseLookup_IPAddr
, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ),
728 CLI_OPTION_GROUP( "Flags" ),
730 DNSSDFlagsOption_ForceMulticast(),
731 DNSSDFlagsOption_ReturnIntermediates(),
732 DNSSDFlagsOption_SuppressUnusable(),
734 CLI_OPTION_GROUP( "Operation" ),
736 BooleanOption( 'o', "oneshot", &gReverseLookup_OneShot
, "Finish after first set of results." ),
737 IntegerOption( 'l', "timeLimit", &gReverseLookup_TimeLimitSecs
, "seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ),
743 //===========================================================================================================================
744 // PortMapping Command Options
745 //===========================================================================================================================
747 static int gPortMapping_ProtocolTCP
= false;
748 static int gPortMapping_ProtocolUDP
= false;
749 static int gPortMapping_InternalPort
= 0;
750 static int gPortMapping_ExternalPort
= 0;
751 static int gPortMapping_TTL
= 0;
753 static CLIOption kPortMappingOpts
[] =
756 BooleanOption( 0, "tcp", &gPortMapping_ProtocolTCP
, "Use kDNSServiceProtocol_TCP." ),
757 BooleanOption( 0, "udp", &gPortMapping_ProtocolUDP
, "Use kDNSServiceProtocol_UDP." ),
758 IntegerOption( 0, "internalPort", &gPortMapping_InternalPort
, "port number", "Internal port.", false ),
759 IntegerOption( 0, "externalPort", &gPortMapping_ExternalPort
, "port number", "Requested external port. Use '0' for any external port.", false ),
760 IntegerOption( 0, "ttl", &gPortMapping_TTL
, "seconds", "Requested TTL (renewal period) in seconds. Use '0' for a default value.", false ),
762 CLI_OPTION_GROUP( "Flags" ),
765 CLI_OPTION_GROUP( "Operation" ),
772 //===========================================================================================================================
773 // BrowseAll Command Options
774 //===========================================================================================================================
776 static const char * gBrowseAll_Domain
= NULL
;
777 static const char ** gBrowseAll_ServiceTypes
= NULL
;
778 static size_t gBrowseAll_ServiceTypesCount
= 0;
779 static int gBrowseAll_BrowseTimeSecs
= 5;
780 static int gBrowseAll_ConnectTimeout
= 0;
782 static CLIOption kBrowseAllOpts
[] =
785 StringOption( 'd', "domain", &gBrowseAll_Domain
, "domain", "Domain in which to browse for the service.", false ),
786 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 ),
788 CLI_OPTION_GROUP( "Flags" ),
789 DNSSDFlagsOption_IncludeAWDL(),
791 CLI_OPTION_GROUP( "Operation" ),
792 IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs
, "seconds", "Amount of time to spend browsing in seconds. (default: 5)", false ),
793 IntegerOption( 'c', "connectTimeout", &gBrowseAll_ConnectTimeout
, "seconds", "Timeout for connection attempts. If <= 0, no connections are attempted. (default: 0)", false ),
797 //===========================================================================================================================
798 // GetNameInfo Command Options
799 //===========================================================================================================================
801 static void GetNameInfoCmd( void );
803 static char * gGetNameInfo_IPAddress
= NULL
;
804 static int gGetNameInfoFlag_DGram
= false;
805 static int gGetNameInfoFlag_NameReqd
= false;
806 static int gGetNameInfoFlag_NoFQDN
= false;
807 static int gGetNameInfoFlag_NumericHost
= false;
808 static int gGetNameInfoFlag_NumericScope
= false;
809 static int gGetNameInfoFlag_NumericServ
= false;
811 static CLIOption kGetNameInfoOpts
[] =
813 StringOption( 'a', "address", &gGetNameInfo_IPAddress
, "IP address", "IPv4 or IPv6 address to use in sockaddr structure.", true ),
815 CLI_OPTION_GROUP( "Flags" ),
816 BooleanOption( 0 , "flag-dgram", &gGetNameInfoFlag_DGram
, "Use NI_DGRAM flag." ),
817 BooleanOption( 0 , "flag-namereqd", &gGetNameInfoFlag_NameReqd
, "Use NI_NAMEREQD flag." ),
818 BooleanOption( 0 , "flag-nofqdn", &gGetNameInfoFlag_NoFQDN
, "Use NI_NOFQDN flag." ),
819 BooleanOption( 0 , "flag-numerichost", &gGetNameInfoFlag_NumericHost
, "Use NI_NUMERICHOST flag." ),
820 BooleanOption( 0 , "flag-numericscope", &gGetNameInfoFlag_NumericScope
, "Use NI_NUMERICSCOPE flag." ),
821 BooleanOption( 0 , "flag-numericserv", &gGetNameInfoFlag_NumericServ
, "Use NI_NUMERICSERV flag." ),
823 CLI_SECTION( "Notes", "See getnameinfo(3) man page for more details.\n" ),
827 //===========================================================================================================================
828 // GetAddrInfoStress Command Options
829 //===========================================================================================================================
831 static int gGAIStress_TestDurationSecs
= 0;
832 static int gGAIStress_ConnectionCount
= 0;
833 static int gGAIStress_DurationMinMs
= 0;
834 static int gGAIStress_DurationMaxMs
= 0;
835 static int gGAIStress_RequestCountMax
= 0;
837 static CLIOption kGetAddrInfoStressOpts
[] =
841 CLI_OPTION_GROUP( "Flags" ),
842 DNSSDFlagsOption_ReturnIntermediates(),
843 DNSSDFlagsOption_SuppressUnusable(),
845 CLI_OPTION_GROUP( "Operation" ),
846 IntegerOption( 0, "testDuration", &gGAIStress_TestDurationSecs
, "seconds", "Stress test duration in seconds. Use '0' for forever.", false ),
847 IntegerOption( 0, "connectionCount", &gGAIStress_ConnectionCount
, "integer", "Number of simultaneous DNS-SD connections.", true ),
848 IntegerOption( 0, "requestDurationMin", &gGAIStress_DurationMinMs
, "ms", "Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
849 IntegerOption( 0, "requestDurationMax", &gGAIStress_DurationMaxMs
, "ms", "Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
850 IntegerOption( 0, "consecutiveRequestMax", &gGAIStress_RequestCountMax
, "integer", "Maximum number of requests on a connection before restarting it.", true ),
854 //===========================================================================================================================
855 // DNSQuery Command Options
856 //===========================================================================================================================
858 static char * gDNSQuery_Name
= NULL
;
859 static char * gDNSQuery_Type
= "A";
860 static char * gDNSQuery_Server
= NULL
;
861 static int gDNSQuery_TimeLimitSecs
= 5;
862 static int gDNSQuery_UseTCP
= false;
863 static int gDNSQuery_Flags
= kDNSHeaderFlag_RecursionDesired
;
864 static int gDNSQuery_RawRData
= false;
865 static int gDNSQuery_Verbose
= false;
867 #if( TARGET_OS_DARWIN )
868 #define kDNSQueryServerOptionIsRequired false
870 #define kDNSQueryServerOptionIsRequired true
873 static CLIOption kDNSQueryOpts
[] =
875 StringOption( 'n', "name", &gDNSQuery_Name
, "name", "Question name (QNAME) to put in DNS query message.", true ),
876 StringOption( 't', "type", &gDNSQuery_Type
, "type", "Question type (QTYPE) to put in DNS query message. Default value is 'A'.", false ),
877 StringOption( 's', "server", &gDNSQuery_Server
, "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired
),
878 IntegerOption( 'l', "timeLimit", &gDNSQuery_TimeLimitSecs
, "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ),
879 BooleanOption( 0 , "tcp", &gDNSQuery_UseTCP
, "Send the DNS query via TCP instead of UDP." ),
880 IntegerOption( 'f', "flags", &gDNSQuery_Flags
, "flags", "16-bit value for DNS header flags/codes field. Default value is 0x0100 (Recursion Desired).", false ),
881 BooleanOption( 0 , "raw", &gDNSQuery_RawRData
, "Present record data as a hexdump." ),
882 BooleanOption( 'v', "verbose", &gDNSQuery_Verbose
, "Prints the DNS message to be sent to the server." ),
886 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
887 //===========================================================================================================================
888 // DNSCrypt Command Options
889 //===========================================================================================================================
891 static char * gDNSCrypt_ProviderName
= NULL
;
892 static char * gDNSCrypt_ProviderKey
= NULL
;
893 static char * gDNSCrypt_Name
= NULL
;
894 static char * gDNSCrypt_Type
= NULL
;
895 static char * gDNSCrypt_Server
= NULL
;
896 static int gDNSCrypt_TimeLimitSecs
= 5;
897 static int gDNSCrypt_RawRData
= false;
898 static int gDNSCrypt_Verbose
= false;
900 static CLIOption kDNSCryptOpts
[] =
902 StringOption( 'p', "providerName", &gDNSCrypt_ProviderName
, "name", "The DNSCrypt provider name.", true ),
903 StringOption( 'k', "providerKey", &gDNSCrypt_ProviderKey
, "hex string", "The DNSCrypt provider's public signing key.", true ),
904 StringOption( 'n', "name", &gDNSCrypt_Name
, "name", "Question name (QNAME) to put in DNS query message.", true ),
905 StringOption( 't', "type", &gDNSCrypt_Type
, "type", "Question type (QTYPE) to put in DNS query message.", true ),
906 StringOption( 's', "server", &gDNSCrypt_Server
, "IP address", "DNS server's IPv4 or IPv6 address.", true ),
907 IntegerOption( 'l', "timeLimit", &gDNSCrypt_TimeLimitSecs
, "seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ),
908 BooleanOption( 0 , "raw", &gDNSCrypt_RawRData
, "Present record data as a hexdump." ),
909 BooleanOption( 'v', "verbose", &gDNSCrypt_Verbose
, "Prints the DNS message to be sent to the server." ),
914 //===========================================================================================================================
915 // MDNSQuery Command Options
916 //===========================================================================================================================
918 static char * gMDNSQuery_Name
= NULL
;
919 static char * gMDNSQuery_Type
= NULL
;
920 static int gMDNSQuery_SourcePort
= 0;
921 static int gMDNSQuery_IsQU
= false;
922 static int gMDNSQuery_RawRData
= false;
923 static int gMDNSQuery_UseIPv4
= false;
924 static int gMDNSQuery_UseIPv6
= false;
925 static int gMDNSQuery_AllResponses
= false;
926 static int gMDNSQuery_ReceiveSecs
= 1;
928 static CLIOption kMDNSQueryOpts
[] =
930 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
931 StringOption( 'n', "name", &gMDNSQuery_Name
, "name", "Question name (QNAME) to put in mDNS message.", true ),
932 StringOption( 't', "type", &gMDNSQuery_Type
, "type", "Question type (QTYPE) to put in mDNS message.", true ),
933 IntegerOption( 'p', "sourcePort", &gMDNSQuery_SourcePort
, "port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ),
934 BooleanOption( 'u', "QU", &gMDNSQuery_IsQU
, "Set the unicast-response bit, i.e., send a QU question." ),
935 BooleanOption( 0 , "raw", &gMDNSQuery_RawRData
, "Present record data as a hexdump." ),
936 BooleanOption( 0 , "ipv4", &gMDNSQuery_UseIPv4
, "Use IPv4." ),
937 BooleanOption( 0 , "ipv6", &gMDNSQuery_UseIPv6
, "Use IPv6." ),
938 BooleanOption( 'a', "allResponses", &gMDNSQuery_AllResponses
, "Print all received mDNS messages, not just those containing answers." ),
939 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 ),
943 //===========================================================================================================================
944 // MDNSCollider Command Options
945 //===========================================================================================================================
947 #define kMDNSColliderProgramSection_Intro \
948 "Programs dictate when the collider sends out unsolicited response messages for its record and how the collider\n" \
949 "ought to react to probe queries that match its record's name, if at all.\n" \
951 "For example, suppose that the goal is to cause a specific unique record in the verified state to be renamed.\n" \
952 "The collider should be invoked such that its record's name is equal to that of the record being targeted. Also,\n" \
953 "the record's type and data should be such that no record with that name, type, and data combination currently\n" \
954 "exists. If the mDNS responder that owns the record follows sections 8.1 and 9 of RFC 6762, then the goal can be\n" \
955 "accomplished with the following program:\n" \
957 " probes 3r; send; wait 5000\n" \
959 "The first command, 'probes 3r', tells the collider to respond to the next three probe queries that match its\n" \
960 "record's name. The second command, makes the collider send an unsolicited response message that contains its\n" \
961 "record in the answer section. The third command makes the collider wait for five seconds before exiting, which\n" \
962 "is more than enough time for the collider to respond to probe queries.\n" \
964 "The send command will cause the targeted record to go into the probing state per section 9 since the collider's\n" \
965 "record conflicts with target record. Per the probes command, the subsequent probe query sent during the probing\n" \
966 "state will be answered by the collider, which will cause the record to be renamed per section 8.1.\n"
968 #define kMDNSColliderProgramSection_Probes \
969 "The probes command defines how the collider ought to react to probe queries that match its record's name.\n" \
971 "Usage: probes [<action-string>]\n" \
973 "The syntax for an action-string is\n" \
975 " <action-string> ::= <action> | <action-string> \"-\" <action>\n" \
976 " <action> ::= [<repeat-count>] <action-code>\n" \
977 " <repeat-count> ::= \"1\" | \"2\" | ... | \"10\"\n" \
978 " <action-code> ::= \"n\" | \"r\" | \"u\" | \"m\" | \"p\"\n" \
980 "An expanded action-string is defined as\n" \
982 " <expanded-action-string> ::= <action-code> | <expanded-action-string> \"-\" <action-code>\n" \
984 "The action-string argument is converted into an expanded-action-string by expanding each action with a\n" \
985 "repeat-count into an expanded-action-string consisting of exactly <repeat-count> <action-code>s. For example,\n" \
986 "2n-r expands to n-n-r. Action-strings that expand to expanded-action-strings with more than 10 action-codes\n" \
987 "are not allowed.\n" \
989 "When the probes command is executed, it does two things. Firstly, it resets to zero the collider's count of\n" \
990 "probe queries that match its record's name. Secondly, it defines how the collider ought to react to such probe\n" \
991 "queries based on the action-string argument. Specifically, the nth action-code in the expanded version of the\n" \
992 "action-string argument defines how the collider ought to react to the nth received probe query:\n" \
997 " r Respond to the probe query.\n" \
998 " u Respond to the probe query via unicast.\n" \
999 " m Respond to the probe query via multicast.\n" \
1000 " p Multicast own probe query. (Useful for causing simultaneous probe scenarios.)\n" \
1002 "Note: If no action is defined for a received probe query, then the collider does nothing, i.e., it doesn't send\n" \
1003 "a response nor does it multicast its own probe query.\n"
1005 #define kMDNSColliderProgramSection_Send \
1006 "The send command multicasts an unsolicited mDNS response containing the collider's record in the answer\n" \
1007 "section, which can be used to force unique records with the same record name into the probing state.\n" \
1011 #define kMDNSColliderProgramSection_Wait \
1012 "The wait command pauses program execution for the interval of time specified by its argument.\n" \
1014 "Usage: wait <milliseconds>\n"
1016 #define kMDNSColliderProgramSection_Loop \
1017 "The loop command starts a counting loop. The done statement marks the end of the loop body. The loop command's\n" \
1018 "argument specifies the number of loop iterations. Note: Loop nesting is supported up to a depth of 16.\n" \
1020 "Usage: loop <non-zero count>; ... ; done\n" \
1022 "For example, the following program sends three unsolicited responses at an approximate rate of one per second:\n" \
1024 " loop 3; wait 1000; send; done"
1026 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
1028 static const char * gMDNSCollider_Name
= NULL
;
1029 static const char * gMDNSCollider_Type
= NULL
;
1030 static const char * gMDNSCollider_RecordData
= NULL
;
1031 static int gMDNSCollider_UseIPv4
= false;
1032 static int gMDNSCollider_UseIPv6
= false;
1033 static const char * gMDNSCollider_Program
= NULL
;
1035 static CLIOption kMDNSColliderOpts
[] =
1037 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
1038 StringOption( 'n', "name", &gMDNSCollider_Name
, "name", "Collider's record name.", true ),
1039 StringOption( 't', "type", &gMDNSCollider_Type
, "type", "Collider's record type.", true ),
1040 StringOption( 'd', "data", &gMDNSCollider_RecordData
, "record data", "Collider's record data. See " kRecordDataSection_Name
" below.", true ),
1041 StringOption( 'p', "program", &gMDNSCollider_Program
, "program", "Program to execute. See Program section below.", true ),
1042 BooleanOption( 0 , "ipv4", &gMDNSCollider_UseIPv4
, "Use IPv4." ),
1043 BooleanOption( 0 , "ipv6", &gMDNSCollider_UseIPv6
, "Use IPv6." ),
1045 RecordDataSection(),
1046 CLI_SECTION( "Program", kMDNSColliderProgramSection_Intro
),
1047 CLI_SECTION( "Program Command: probes", kMDNSColliderProgramSection_Probes
),
1048 CLI_SECTION( "Program Command: send", kMDNSColliderProgramSection_Send
),
1049 CLI_SECTION( "Program Command: wait", kMDNSColliderProgramSection_Wait
),
1050 CLI_SECTION( "Program Command: loop", kMDNSColliderProgramSection_Loop
),
1054 static void MDNSColliderCmd( void );
1056 //===========================================================================================================================
1057 // PIDToUUID Command Options
1058 //===========================================================================================================================
1060 static int gPIDToUUID_PID
= 0;
1062 static CLIOption kPIDToUUIDOpts
[] =
1064 IntegerOption( 'p', "pid", &gPIDToUUID_PID
, "PID", "Process ID.", true ),
1068 //===========================================================================================================================
1069 // DNSServer Command Options
1070 //===========================================================================================================================
1072 #define kDNSServerInfoText_Intro \
1073 "The DNS server answers certain queries in the d.test. domain. Responses are dynamically generated based on the\n" \
1074 "presence of special labels in the query's QNAME. There are currently eight types of special labels that can be\n" \
1075 "used to generate specific responses: Alias labels, Alias-TTL labels, Count labels, Tag labels, TTL labels, the\n" \
1076 "IPv4 label, the IPv6 label, and SRV labels.\n" \
1078 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n"
1080 #define kDNSServerInfoText_NameExistence \
1081 "A name is considered to exist if it's an Address name or an SRV name.\n" \
1083 "An Address name is defined as a name that ends with d.test., and the other labels, if any, and in no particular\n" \
1084 "order, unless otherwise noted, consist of\n" \
1086 " 1. at most one Alias or Alias-TTL label as the first label;\n" \
1087 " 2. at most one Count label;\n" \
1088 " 3. zero or more Tag labels;\n" \
1089 " 4. at most one TTL label; and\n" \
1090 " 5. at most one IPv4 or IPv6 label.\n" \
1092 "An SRV name is defined as a name with the following form:\n" \
1094 " _<service>._<proto>[.<parent domain>][.<SRV label 1>[.<target 1>][.<SRV label 2>[.<target 2>][...]]].d.test.\n" \
1096 "See \"SRV Names\" for details.\n"
1098 #define kDNSServerInfoText_ResourceRecords \
1099 "Currently, the server only supports CNAME, A, AAAA, and SRV records.\n" \
1101 "Address names that begin with an Alias or Alias-TTL label are aliases of canonical names, i.e., they're the\n" \
1102 "names of CNAME records. See \"Alias Labels\" and \"Alias-TTL Labels\" for details.\n" \
1104 "A canonical Address name can exclusively be the name of one or more A records, can exclusively be the name or\n" \
1105 "one or more AAAA records, or can be the name of both A and AAAA records. Address names that contain an IPv4\n" \
1106 "label have at least one A record, but no AAAA records. Address names that contain an IPv6 label, have at least\n" \
1107 "one AAAA record, but no A records. All other Address names have at least one A record and at least one AAAA\n" \
1108 "record. See \"Count Labels\" for how the number of address records for a given Address name is determined.\n" \
1110 "A records contain IPv4 addresses in the 203.0.113.0/24 block, while AAAA records contain IPv6 addresses in the\n" \
1111 "2001:db8:1::/120 block. Both of these address blocks are reserved for documentation. See\n" \
1112 "<https://tools.ietf.org/html/rfc5737> and <https://tools.ietf.org/html/rfc3849>.\n" \
1114 "SRV names are names of SRV records.\n" \
1116 "Unless otherwise specified, all resource records will use a default TTL. The default TTL can be set with the\n" \
1117 "--defaultTTL option. See \"Alias-TTL Labels\" and \"TTL Labels\" for details on how to query for CNAME, A, and\n" \
1118 "AAAA records with specific TTL values.\n"
1120 #define kDNSServerInfoText_AliasLabel \
1121 "Alias labels are of the form \"alias\" or \"alias-N\", where N is an integer in [2, 2^31 - 1].\n" \
1123 "If QNAME is an Address name and its first label is Alias label \"alias-N\", then the response will contain\n" \
1124 "exactly N CNAME records:\n" \
1126 " 1. For each i in [3, N], the response will contain a CNAME record whose name is identical to QNAME, except\n" \
1127 " that the first label is \"alias-i\" instead, and whose RDATA is the name of the other CNAME record whose\n" \
1128 " name has \"alias-(i - 1)\" as its first label.\n" \
1130 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1131 " is \"alias-2\" instead, and whose RDATA is the name identical to QNAME, except that the first label is\n" \
1132 " \"alias\" instead.\n" \
1134 " 3. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1135 " is \"alias\" instead, and whose RDATA is the name identical to QNAME minus its first label.\n" \
1137 "If QNAME is an Address name and its first label is Alias label \"alias\", then the response will contain a\n" \
1138 "single CNAME record. The CNAME record's name will be equal to QNAME and its RDATA will be the name identical to\n" \
1139 "QNAME minus its first label.\n" \
1141 "Example. A response to a query with a QNAME of alias-3.count-5.d.test will contain the following CNAME\n" \
1144 " alias-4.count-5.d.test. 60 IN CNAME alias-3.count-5.d.test.\n" \
1145 " alias-3.count-5.d.test. 60 IN CNAME alias-2.count-5.d.test.\n" \
1146 " alias-2.count-5.d.test. 60 IN CNAME alias.count-5.d.test.\n" \
1147 " alias.count-5.d.test. 60 IN CNAME count-5.d.test.\n"
1149 #define kDNSServerInfoText_AliasTTLLabel \
1150 "Alias-TTL labels are of the form \"alias-ttl-T_1[-T_2[...-T_N]]\", where each T_i is an integer in\n" \
1151 "[0, 2^31 - 1] and N is a positive integer bounded by the size of the maximum legal label length (63 octets).\n" \
1153 "If QNAME is an Address name and its first label is Alias-TTL label \"alias-ttl-T_1...-T_N\", then the response\n" \
1154 "will contain exactly N CNAME records:\n" \
1156 " 1. For each i in [1, N - 1], the response will contain a CNAME record whose name is identical to QNAME,\n" \
1157 " except that the first label is \"alias-ttl-T_i...-T_N\" instead, whose TTL value is T_i, and whose RDATA\n" \
1158 " is the name of the other CNAME record whose name has \"alias-ttl-T_(i+1)...-T_N\" as its first label.\n" \
1160 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1161 " is \"alias-ttl-T_N\", whose TTL is T_N, and whose RDATA is identical to QNAME stripped of its first\n" \
1164 "Example. A response to a query with a QNAME of alias-ttl-20-40-80.count-5.d.test will contain the following\n" \
1165 "CNAME records:\n" \
1167 " alias-ttl-20-40-80.count-5.d.test. 20 IN CNAME alias-ttl-40-80.count-5.d.test.\n" \
1168 " alias-ttl-40-80.count-5.d.test. 40 IN CNAME alias-ttl-80.count-5.d.test.\n" \
1169 " alias-ttl-80.count-5.d.test. 80 IN CNAME count-5.d.test.\n"
1171 #define kDNSServerInfoText_CountLabel \
1172 "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" \
1173 "an integer in [N_1, 255].\n" \
1175 "If QNAME is an Address name, contains Count label \"count-N\", and has the type of address records specified by\n" \
1176 "QTYPE, then the response will contain exactly N address records:\n" \
1178 " 1. For i in [1, N], the response will contain an address record of type QTYPE whose name is equal to QNAME\n" \
1179 " and whose RDATA is an address equal to a constant base address + i.\n" \
1181 " 2. The address records will be ordered by the address contained in RDATA in ascending order.\n" \
1183 "Example. A response to an A record query with a QNAME of alias.count-3.d.test will contain the following A\n" \
1186 " count-3.d.test. 60 IN A 203.0.113.1\n" \
1187 " count-3.d.test. 60 IN A 203.0.113.2\n" \
1188 " count-3.d.test. 60 IN A 203.0.113.3\n" \
1190 "If QNAME is an Address name, contains Count label \"count-N_1-N_2\", and has the type of address records\n" \
1191 "specified by QTYPE, then the response will contain exactly N_1 address records:\n" \
1193 " 1. Each of the address records will be of type QTYPE, have name equal to QNAME, and have as its RDATA a\n" \
1194 " unique address equal to a constant base address + i, where i is a randomly chosen integer in [1, N_2].\n" \
1196 " 2. The order of the address records will be random.\n" \
1198 "Example. A response to a AAAA record query with a QNAME of count-3-100.ttl-20.d.test could contain the\n" \
1199 "following AAAA records:\n" \
1201 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::c\n" \
1202 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::3a\n" \
1203 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::4f\n" \
1205 "If QNAME is an Address name, but doesn't have the type of address records specified by QTYPE, then the response\n" \
1206 "will contain no address records, regardless of whether it contains a Count label.\n" \
1208 "Address names that don't have a Count label are treated as though they contain a count label equal to\n" \
1211 #define kDNSServerInfoText_TagLabel \
1212 "Tag labels are labels prefixed with \"tag-\" and contain zero or more arbitrary octets after the prefix.\n" \
1214 "This type of label exists to allow testers to \"uniquify\" domain names. Tag labels can also serve as padding\n" \
1215 "to increase the sizes of domain names.\n"
1217 #define kDNSServerInfoText_TTLLabel \
1218 "TTL labels are of the form \"ttl-T\", where T is an integer in [0, 2^31 - 1].\n" \
1220 "If QNAME is an Address name and contains TTL label \"ttl-T\", then all non-CNAME records contained in the\n" \
1221 "response will have a TTL value equal to T.\n"
1223 #define kDNSServerInfoText_IPv4Label \
1224 "The IPv4 label is \"ipv4\". See \"Resource Records\" for the affect of this label.\n"
1226 #define kDNSServerInfoText_IPv6Label \
1227 "The IPv6 label is \"ipv6\". See \"Resource Records\" for the affect of this label.\n"
1229 #define kDNSServerInfoText_SRVNames \
1230 "SRV labels are of the form \"srv-R-W-P\", where R, W, and P are integers in [0, 2^16 - 1].\n" \
1232 "After the first two labels, i.e., the service and protocol labels, the sequence of labels, which may be empty,\n" \
1233 "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" \
1234 "the target hostname of each of the SRV name's SRV records.\n" \
1236 "If QNAME is an SRV name and QTYPE is SRV, then for each SRV label, the response will contain an SRV record with\n" \
1237 "priority R, weight W, port P, and target hostname <target>[.<parent domain>]., where <target> is the sequence\n" \
1238 "of labels, which may be empty, that follows the SRV label leading up to either the next SRV label or the\n" \
1239 "d.test. labels, whichever comes first.\n" \
1241 "Example. A response to an SRV record query with a QNAME of\n" \
1242 "_http._tcp.example.com.srv-0-0-80.www.srv-1-0-8080.www.d.test. will contain the following SRV records:\n" \
1244 "_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" \
1245 "_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"
1247 #define kDNSServerInfoText_BadUDPMode \
1248 "The purpose of Bad UDP mode is to test mDNSResponder's TCP fallback mechanism by which mDNSResponder reissues a\n" \
1249 "UDP query as a TCP query if the UDP response contains the expected QNAME, QTYPE, and QCLASS, but a message ID\n" \
1250 "that's not equal to the query's message ID.\n" \
1252 "This mode is identical to the normal mode except that all responses sent via UDP have a message ID equal to the\n" \
1253 "query's message ID plus one. Also, in this mode, to aid in debugging, A records in responses sent via UDP have\n" \
1254 "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" \
1255 "base address, and AAAA records in responses sent via UDP have IPv6 addresses in the ::ffff:0:0/120 block\n" \
1256 "instead of the 2001:db8:1::/120 block, i.e., ::ffff:0:0 is used as the IPv6 base address.\n"
1258 static int gDNSServer_LoopbackOnly
= false;
1259 static int gDNSServer_Foreground
= false;
1260 static int gDNSServer_ResponseDelayMs
= 0;
1261 static int gDNSServer_DefaultTTL
= 60;
1262 static int gDNSServer_Port
= kDNSPort
;
1263 static const char * gDNSServer_DomainOverride
= NULL
;
1264 #if( TARGET_OS_DARWIN )
1265 static const char * gDNSServer_FollowPID
= NULL
;
1267 static int gDNSServer_BadUDPMode
= false;
1269 static CLIOption kDNSServerOpts
[] =
1271 BooleanOption( 'l', "loopback", &gDNSServer_LoopbackOnly
, "Bind to to the loopback interface." ),
1272 BooleanOption( 'f', "foreground", &gDNSServer_Foreground
, "Direct log output to stdout instead of system logging." ),
1273 IntegerOption( 'd', "responseDelay", &gDNSServer_ResponseDelayMs
, "ms", "The amount of additional delay in milliseconds to apply to responses. (default: 0)", false ),
1274 IntegerOption( 0 , "defaultTTL", &gDNSServer_DefaultTTL
, "seconds", "Resource record TTL value to use when unspecified. (default: 60)", false ),
1275 IntegerOption( 'p', "port", &gDNSServer_Port
, "port number", "UDP/TCP port number to use. Use 0 for any port. (default: 53)", false ),
1276 StringOption( 0 , "domain", &gDNSServer_DomainOverride
, "domain", "Used to override 'd.test.' as the server's domain.", false ),
1277 #if( TARGET_OS_DARWIN )
1278 StringOption( 0 , "follow", &gDNSServer_FollowPID
, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1280 BooleanOption( 0 , "badUDPMode", &gDNSServer_BadUDPMode
, "Run in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1282 CLI_SECTION( "Intro", kDNSServerInfoText_Intro
),
1283 CLI_SECTION( "Name Existence", kDNSServerInfoText_NameExistence
),
1284 CLI_SECTION( "Resource Records", kDNSServerInfoText_ResourceRecords
),
1285 CLI_SECTION( "Alias Labels", kDNSServerInfoText_AliasLabel
),
1286 CLI_SECTION( "Alias-TTL Labels", kDNSServerInfoText_AliasTTLLabel
),
1287 CLI_SECTION( "Count Labels", kDNSServerInfoText_CountLabel
),
1288 CLI_SECTION( "Tag Labels", kDNSServerInfoText_TagLabel
),
1289 CLI_SECTION( "TTL Labels", kDNSServerInfoText_TTLLabel
),
1290 CLI_SECTION( "IPv4 Label", kDNSServerInfoText_IPv4Label
),
1291 CLI_SECTION( "IPv6 Label", kDNSServerInfoText_IPv6Label
),
1292 CLI_SECTION( "SRV Names", kDNSServerInfoText_SRVNames
),
1293 CLI_SECTION( "Bad UDP Mode", kDNSServerInfoText_BadUDPMode
),
1297 static void DNSServerCmd( void );
1299 //===========================================================================================================================
1300 // MDNSReplier Command Options
1301 //===========================================================================================================================
1303 #define kMDNSReplierPortBase 50000
1305 #define kMDNSReplierInfoText_Intro \
1306 "The mDNS replier answers mDNS queries for its authoritative records. These records are of class IN and of types\n" \
1307 "PTR, SRV, TXT, A, and AAAA as described below.\n" \
1309 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n"
1311 #define kMDNSReplierInfoText_Parameters \
1312 "There are five parameters that control the replier's set of authoritative records.\n" \
1314 " 1. <hostname> is the base name used for service instance names and the names of A and AAAA records. This\n" \
1315 " parameter is specified with the --hostname option.\n" \
1316 " 2. <tag> is an arbitrary string used to uniquify service types. This parameter is specified with the --tag\n" \
1318 " 3. N_max in an integer in [1, 65535] and limits service types to those that have no more than N_max\n" \
1319 " instances. It also limits the number of hostnames to N_max, i.e., <hostname>.local.,\n" \
1320 " <hostname>-1.local., ..., <hostname>-N_max.local. This parameter is specified with the\n" \
1321 " --maxInstanceCount option.\n" \
1322 " 4. N_a is an integer in [1, 255] and the number of A records per hostname. This parameter is specified\n" \
1323 " with the --countA option.\n" \
1324 " 5. N_aaaa is an integer in [1, 255] and the number of AAAA records per hostname. This parameter is\n" \
1325 " specified with the --countAAAA option.\n"
1327 #define kMDNSReplierInfoText_PTR \
1328 "The replier's authoritative PTR records have names of the form _t-<tag>-<L>-<N>._tcp.local., where L is an\n" \
1329 "integer in [1, 65535], and N is an integer in [1, N_max].\n" \
1331 "For a given L and N, the replier has exactly N authoritative PTR records:\n" \
1333 " 1. The first PTR record is defined as\n" \
1335 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n" \
1339 " RDATA: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1341 " 2. For each i in [2, N], there is one PTR record defined as\n" \
1343 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n" \
1347 " RDATA: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n"
1349 #define kMDNSReplierInfoText_SRV \
1350 "The replier's authoritative SRV records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n" \
1351 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n" \
1352 "\"<hostname> (<i>)\", where i is in [2, N].\n" \
1354 "For a given L and N, the replier has exactly N authoritative SRV records:\n" \
1356 " 1. The first SRV record is defined as\n" \
1358 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1365 " Port: (50000 + L) mod 2^16\n" \
1366 " Target: <hostname>.local.\n" \
1368 " 2. For each i in [2, N], there is one SRV record defined as:\n" \
1370 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n" \
1377 " Port: (50000 + L) mod 2^16\n" \
1378 " Target: <hostname>-<i>.local.\n"
1380 #define kMDNSReplierInfoText_TXT \
1381 "The replier's authoritative TXT records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n" \
1382 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n" \
1383 "\"<hostname> (<i>)\", where i is in [2, N].\n" \
1385 "For a given L and N, the replier has exactly N authoritative TXT records:\n" \
1387 " 1. The first TXT record is defined as\n" \
1389 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1394 " RDATA: <one or more strings with an aggregate length of L octets>\n" \
1396 " 2. For each i in [2, N], there is one TXT record:\n" \
1398 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n" \
1403 " RDATA: <one or more strings with an aggregate length of L octets>\n" \
1405 "The RDATA of each TXT record is exactly L octets and consists of a repeating series of the 15-byte string\n" \
1406 "\"hash=0x<32-bit FNV-1 hash of the record name as an 8-character hexadecimal string>\". The last instance of\n" \
1407 "the string may be truncated to satisfy the TXT record data's size requirement.\n"
1409 #define kMDNSReplierInfoText_A \
1410 "The replier has exactly N_max x N_a authoritative A records:\n" \
1412 " 1. For each j in [1, N_a], an A record is defined as\n" \
1414 " NAME: <hostname>.local.\n" \
1419 " RDATA: 0.0.1.<j>\n" \
1421 " 2. For each i in [2, N_max], for each j in [1, N_a], an A record is defined as\n" \
1423 " NAME: <hostname>-<i>.local.\n" \
1428 " RDATA: 0.<ceil(i / 256)>.<i mod 256>.<j>\n"
1430 #define kMDNSReplierInfoText_AAAA \
1431 "The replier has exactly N_max x N_aaaa authoritative AAAA records:\n" \
1433 " 1. For each j in [1, N_aaaa], a AAAA record is defined as\n" \
1435 " NAME: <hostname>.local.\n" \
1440 " RDATA: 2001:db8:2::1:<j>\n" \
1442 " 2. For each i in [2, N_max], for each j in [1, N_aaaa], a AAAA record is defined as\n" \
1444 " NAME: <hostname>-<i>.local.\n" \
1449 " RDATA: 2001:db8:2::<i>:<j>\n"
1451 #define kMDNSReplierInfoText_Responses \
1452 "When generating answers for a query message, any two records pertaining to the same hostname will be grouped\n" \
1453 "together in the same response message, and any two records pertaining to different hostnames will be in\n" \
1454 "separate response messages.\n"
1456 static const char * gMDNSReplier_Hostname
= NULL
;
1457 static const char * gMDNSReplier_ServiceTypeTag
= NULL
;
1458 static int gMDNSReplier_MaxInstanceCount
= 1000;
1459 static int gMDNSReplier_NoAdditionals
= false;
1460 static int gMDNSReplier_RecordCountA
= 1;
1461 static int gMDNSReplier_RecordCountAAAA
= 1;
1462 static double gMDNSReplier_UnicastDropRate
= 0.0;
1463 static double gMDNSReplier_MulticastDropRate
= 0.0;
1464 static int gMDNSReplier_MaxDropCount
= 0;
1465 static int gMDNSReplier_UseIPv4
= false;
1466 static int gMDNSReplier_UseIPv6
= false;
1467 static int gMDNSReplier_Foreground
= false;
1468 static const char * gMDNSReplier_FollowPID
= NULL
;
1470 static CLIOption kMDNSReplierOpts
[] =
1472 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
1473 StringOption( 'n', "hostname", &gMDNSReplier_Hostname
, "string", "Base name to use for hostnames and service instance names.", true ),
1474 StringOption( 't', "tag", &gMDNSReplier_ServiceTypeTag
, "string", "Tag to use for service types, e.g., _t-<tag>-<TXT size>-<count>._tcp.", true ),
1475 IntegerOption( 'c', "maxInstanceCount", &gMDNSReplier_MaxInstanceCount
, "count", "Maximum number of service instances. (default: 1000)", false ),
1476 BooleanOption( 0 , "noAdditionals", &gMDNSReplier_NoAdditionals
, "When answering queries, don't include any additional records." ),
1477 IntegerOption( 0 , "countA", &gMDNSReplier_RecordCountA
, "count", "Number of A records per hostname. (default: 1)", false ),
1478 IntegerOption( 0 , "countAAAA", &gMDNSReplier_RecordCountAAAA
, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1479 DoubleOption( 0 , "udrop", &gMDNSReplier_UnicastDropRate
, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1480 DoubleOption( 0 , "mdrop", &gMDNSReplier_MulticastDropRate
, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1481 IntegerOption( 0 , "maxDropCount", &gMDNSReplier_MaxDropCount
, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1482 BooleanOption( 0 , "ipv4", &gMDNSReplier_UseIPv4
, "Use IPv4." ),
1483 BooleanOption( 0 , "ipv6", &gMDNSReplier_UseIPv6
, "Use IPv6." ),
1484 BooleanOption( 'f', "foreground", &gMDNSReplier_Foreground
, "Direct log output to stdout instead of system logging." ),
1485 #if( TARGET_OS_DARWIN )
1486 StringOption( 0 , "follow", &gMDNSReplier_FollowPID
, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1489 CLI_SECTION( "Intro", kMDNSReplierInfoText_Intro
),
1490 CLI_SECTION( "Authoritative Record Parameters", kMDNSReplierInfoText_Parameters
),
1491 CLI_SECTION( "Authoritative PTR Records", kMDNSReplierInfoText_PTR
),
1492 CLI_SECTION( "Authoritative SRV Records", kMDNSReplierInfoText_SRV
),
1493 CLI_SECTION( "Authoritative TXT Records", kMDNSReplierInfoText_TXT
),
1494 CLI_SECTION( "Authoritative A Records", kMDNSReplierInfoText_A
),
1495 CLI_SECTION( "Authoritative AAAA Records", kMDNSReplierInfoText_AAAA
),
1496 CLI_SECTION( "Responses", kMDNSReplierInfoText_Responses
),
1500 static void MDNSReplierCmd( void );
1502 //===========================================================================================================================
1503 // Test Command Options
1504 //===========================================================================================================================
1506 #define kTestExitStatusSection_Name "Exit Status"
1507 #define kTestExitStatusSection_Text \
1508 "This test command can exit with one of three status codes:\n" \
1510 "0 - The test ran to completion and passed.\n" \
1511 "1 - A fatal error prevented the test from completing.\n" \
1512 "2 - The test ran to completion, but it or a subtest failed. See test output for details.\n" \
1514 "Note: The pass/fail status applies to the correctness or results. It does not necessarily imply anything about\n" \
1517 #define TestExitStatusSection() CLI_SECTION( kTestExitStatusSection_Name, kTestExitStatusSection_Text )
1519 #define kGAIPerfTestSuiteName_Basic "basic"
1520 #define kGAIPerfTestSuiteName_Advanced "advanced"
1522 static const char * gGAIPerf_TestSuite
= NULL
;
1523 static int gGAIPerf_CallDelayMs
= 10;
1524 static int gGAIPerf_ServerDelayMs
= 10;
1525 static int gGAIPerf_SkipPathEvalulation
= false;
1526 static int gGAIPerf_BadUDPMode
= false;
1527 static int gGAIPerf_IterationCount
= 100;
1528 static int gGAIPerf_IterationTimeLimitMs
= 100;
1529 static const char * gGAIPerf_OutputFilePath
= NULL
;
1530 static const char * gGAIPerf_OutputFormat
= kOutputFormatStr_JSON
;
1531 static int gGAIPerf_OutputAppendNewline
= false;
1533 static void GAIPerfCmd( void );
1535 #define kGAIPerfSectionText_TestSuiteBasic \
1536 "This test suite consists of the following three test cases:\n" \
1538 "Test Case #1: Resolve a domain name with\n" \
1540 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1542 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1543 "server queries.\n" \
1545 "Test Case #2: Resolve a domain name with\n" \
1547 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1549 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1550 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1551 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1553 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1554 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1555 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1557 "Test Case #3: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1559 #define kGAIPerfSectionText_TestSuiteAdvanced \
1560 "This test suite consists of 33 test cases. Test cases 1 through 32 can be described in the following way\n" \
1562 "Test Case #N (where N is in [1, 32] and odd): Resolve a domain name with\n" \
1564 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1566 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1567 "server queries.\n" \
1569 "Test Case #N (where N is in [1, 32] and even): Resolve a domain name with\n" \
1571 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1573 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1574 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1575 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1577 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1578 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1579 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1581 "N_c and N_a take on the following values, depending on the value of N:\n" \
1583 " N_c is 0 if N is in [1, 8].\n" \
1584 " N_c is 1 if N is in [9, 16].\n" \
1585 " N_c is 2 if N is in [17, 24].\n" \
1586 " N_c is 4 if N is in [25, 32].\n" \
1588 " N_a is 1 if N mod 8 is 1 or 2.\n" \
1589 " N_a is 2 if N mod 8 is 3 or 4.\n" \
1590 " N_a is 4 if N mod 8 is 5 or 6.\n" \
1591 " N_a is 8 if N mod 8 is 7 or 0.\n" \
1595 "Test Case #33: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1597 static CLIOption kGAIPerfOpts
[] =
1599 StringOptionEx( 's', "suite", &gGAIPerf_TestSuite
, "name", "Name of the predefined test suite to run.", true,
1601 "There are currently two predefined test suites, '" kGAIPerfTestSuiteName_Basic
"' and '" kGAIPerfTestSuiteName_Advanced
"', which are described below.\n"
1604 StringOption( 'o', "output", &gGAIPerf_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1605 FormatOption( 'f', "format", &gGAIPerf_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1606 BooleanOption( 'n', "appendNewline", &gGAIPerf_OutputAppendNewline
, "If the output format is JSON, output a trailing newline character." ),
1607 IntegerOption( 'i', "iterations", &gGAIPerf_IterationCount
, "count", "The number of iterations per test case. (default: 100)", false ),
1608 IntegerOption( 'l', "timeLimit", &gGAIPerf_IterationTimeLimitMs
, "ms", "Time limit for each DNSServiceGetAddrInfo() operation in milliseconds. (default: 100)", false ),
1609 IntegerOption( 0 , "callDelay", &gGAIPerf_CallDelayMs
, "ms", "Time to wait before calling DNSServiceGetAddrInfo() in milliseconds. (default: 10)", false ),
1610 BooleanOption( 0 , "skipPathEval", &gGAIPerf_SkipPathEvalulation
, "Use kDNSServiceFlagsPathEvaluationDone when calling DNSServiceGetAddrInfo()." ),
1612 CLI_OPTION_GROUP( "DNS Server Options" ),
1613 IntegerOption( 0 , "responseDelay", &gGAIPerf_ServerDelayMs
, "ms", "Additional delay in milliseconds to have the server apply to responses. (default: 10)", false ),
1614 BooleanOption( 0 , "badUDPMode", &gGAIPerf_BadUDPMode
, "Run server in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1616 CLI_SECTION( "Test Suite \"Basic\"", kGAIPerfSectionText_TestSuiteBasic
),
1617 CLI_SECTION( "Test Suite \"Advanced\"", kGAIPerfSectionText_TestSuiteAdvanced
),
1618 TestExitStatusSection(),
1622 static void MDNSDiscoveryTestCmd( void );
1624 static int gMDNSDiscoveryTest_InstanceCount
= 100;
1625 static int gMDNSDiscoveryTest_TXTSize
= 100;
1626 static int gMDNSDiscoveryTest_BrowseTimeSecs
= 2;
1627 static int gMDNSDiscoveryTest_FlushCache
= false;
1628 static char * gMDNSDiscoveryTest_Interface
= NULL
;
1629 static int gMDNSDiscoveryTest_NoAdditionals
= false;
1630 static int gMDNSDiscoveryTest_RecordCountA
= 1;
1631 static int gMDNSDiscoveryTest_RecordCountAAAA
= 1;
1632 static double gMDNSDiscoveryTest_UnicastDropRate
= 0.0;
1633 static double gMDNSDiscoveryTest_MulticastDropRate
= 0.0;
1634 static int gMDNSDiscoveryTest_MaxDropCount
= 0;
1635 static int gMDNSDiscoveryTest_UseIPv4
= false;
1636 static int gMDNSDiscoveryTest_UseIPv6
= false;
1637 static const char * gMDNSDiscoveryTest_OutputFormat
= kOutputFormatStr_JSON
;
1638 static int gMDNSDiscoveryTest_OutputAppendNewline
= false;
1639 static const char * gMDNSDiscoveryTest_OutputFilePath
= NULL
;
1641 static CLIOption kMDNSDiscoveryTestOpts
[] =
1643 IntegerOption( 'c', "instanceCount", &gMDNSDiscoveryTest_InstanceCount
, "count", "Number of service instances to discover. (default: 100)", false ),
1644 IntegerOption( 's', "txtSize", &gMDNSDiscoveryTest_TXTSize
, "bytes", "Desired size of each service instance's TXT record data. (default: 100)", false ),
1645 IntegerOption( 'b', "browseTime", &gMDNSDiscoveryTest_BrowseTimeSecs
, "seconds", "Amount of time to spend browsing in seconds. (default: 2)", false ),
1646 BooleanOption( 0 , "flushCache", &gMDNSDiscoveryTest_FlushCache
, "Flush mDNSResponder's record cache before browsing. Requires root privileges." ),
1648 CLI_OPTION_GROUP( "mDNS Replier Parameters" ),
1649 StringOption( 'i', "interface", &gMDNSDiscoveryTest_Interface
, "name or index", "Network interface. If unspecified, any available mDNS-capable interface will be used.", false ),
1650 BooleanOption( 0 , "noAdditionals", &gMDNSDiscoveryTest_NoAdditionals
, "When answering queries, don't include any additional records." ),
1651 IntegerOption( 0 , "countA", &gMDNSDiscoveryTest_RecordCountA
, "count", "Number of A records per hostname. (default: 1)", false ),
1652 IntegerOption( 0 , "countAAAA", &gMDNSDiscoveryTest_RecordCountAAAA
, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1653 DoubleOption( 0 , "udrop", &gMDNSDiscoveryTest_UnicastDropRate
, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1654 DoubleOption( 0 , "mdrop", &gMDNSDiscoveryTest_MulticastDropRate
, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1655 IntegerOption( 0 , "maxDropCount", &gMDNSDiscoveryTest_MaxDropCount
, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1656 BooleanOption( 0 , "ipv4", &gMDNSDiscoveryTest_UseIPv4
, "Use IPv4." ),
1657 BooleanOption( 0 , "ipv6", &gMDNSDiscoveryTest_UseIPv6
, "Use IPv6." ),
1659 CLI_OPTION_GROUP( "Results" ),
1660 FormatOption( 'f', "format", &gMDNSDiscoveryTest_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1661 StringOption( 'o', "output", &gMDNSDiscoveryTest_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1663 TestExitStatusSection(),
1667 static void DotLocalTestCmd( void );
1669 static const char * gDotLocalTest_Interface
= NULL
;
1670 static const char * gDotLocalTest_OutputFormat
= kOutputFormatStr_JSON
;
1671 static const char * gDotLocalTest_OutputFilePath
= NULL
;
1673 #define kDotLocalTestSubtestDesc_GAIMDNSOnly "GAI for a dotlocal name that has only MDNS A and AAAA records."
1674 #define kDotLocalTestSubtestDesc_GAIDNSOnly "GAI for a dotlocal name that has only DNS A and AAAA records."
1675 #define kDotLocalTestSubtestDesc_GAIBoth "GAI for a dotlocal name that has both mDNS and DNS A and AAAA records."
1676 #define kDotLocalTestSubtestDesc_GAINeither "GAI for a dotlocal name that has no A or AAAA records."
1677 #define kDotLocalTestSubtestDesc_GAINoSuchRecord \
1678 "GAI for a dotlocal name that has no A or AAAA records, but is a subdomain name of a search domain."
1679 #define kDotLocalTestSubtestDesc_QuerySRV "SRV query for a dotlocal name that has only a DNS SRV record."
1681 #define kDotLocalTestSectionText_Description \
1682 "The goal of the dotlocal test is to verify that mDNSResponder properly handles queries for domain names in the\n" \
1683 "local domain when a local SOA record exists. As part of the test setup, a test DNS server and an mdnsreplier are\n" \
1684 "spawned, and a dummy local SOA record is registered with DNSServiceRegisterRecord(). The server is invoked such\n" \
1685 "that its domain is a second-level subdomain of the local domain, i.e., <some label>.local, while the mdnsreplier is\n" \
1686 "invoked such that its base hostname is equal to the server's domain, e.g., if the server's domain is test.local.,\n" \
1687 "then the mdnsreplier's base hostname is test.local.\n" \
1689 "The dotlocal test consists of six subtests that perform either a DNSServiceGetAddrInfo (GAI) operation for a\n" \
1690 "hostname in the local domain or a DNSServiceQueryRecord operation to query for an SRV record in the local domain:\n" \
1692 "1. " kDotLocalTestSubtestDesc_GAIMDNSOnly "\n" \
1693 "2. " kDotLocalTestSubtestDesc_GAIDNSOnly "\n" \
1694 "3. " kDotLocalTestSubtestDesc_GAIBoth "\n" \
1695 "4. " kDotLocalTestSubtestDesc_GAINeither "\n" \
1696 "5. " kDotLocalTestSubtestDesc_GAINoSuchRecord "\n" \
1697 "6. " kDotLocalTestSubtestDesc_QuerySRV "\n" \
1699 "Each subtest runs for five seconds.\n"
1701 static CLIOption kDotLocalTestOpts
[] =
1703 StringOption( 'i', "interface", &gDotLocalTest_Interface
, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1705 CLI_OPTION_GROUP( "Results" ),
1706 FormatOption( 'f', "format", &gDotLocalTest_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1707 StringOption( 'o', "output", &gDotLocalTest_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1709 CLI_SECTION( "Description", kDotLocalTestSectionText_Description
),
1710 TestExitStatusSection(),
1714 static void ProbeConflictTestCmd( void );
1716 static const char * gProbeConflictTest_Interface
= NULL
;
1717 static int gProbeConflictTest_UseComputerName
= false;
1718 static const char * gProbeConflictTest_OutputFormat
= kOutputFormatStr_JSON
;
1719 static const char * gProbeConflictTest_OutputFilePath
= NULL
;
1721 static CLIOption kProbeConflictTestOpts
[] =
1723 StringOption( 'i', "interface", &gProbeConflictTest_Interface
, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1724 BooleanOption( 'c', "useComputerName", &gProbeConflictTest_UseComputerName
, "Use the device's \"computer name\" for the test service's name." ),
1726 CLI_OPTION_GROUP( "Results" ),
1727 FormatOption( 'f', "format", &gProbeConflictTest_OutputFormat
, "Specifies the test report output format. (default: " kOutputFormatStr_JSON
")", false ),
1728 StringOption( 'o', "output", &gProbeConflictTest_OutputFilePath
, "path", "Path of the file to write test report to instead of standard output (stdout).", false ),
1730 TestExitStatusSection(),
1734 static void RegistrationTestCmd( void );
1736 static int gRegistrationTest_BATSEnvironment
= false;
1737 static const char * gRegistrationTest_OutputFormat
= kOutputFormatStr_JSON
;
1738 static const char * gRegistrationTest_OutputFilePath
= NULL
;
1740 static CLIOption kRegistrationTestOpts
[] =
1742 CLI_OPTION_BOOLEAN( 0, "bats", &gRegistrationTest_BATSEnvironment
, "Informs the test that it's running in a BATS environment.",
1744 "This option allows the test to take special measures while running in a BATS environment. Currently, this option\n"
1745 "only has an effect on watchOS. Because it has been observed that the Wi-Fi interface sometimes goes down during\n"
1746 "watchOS BATS testing, for watchOS, when a service is registered using kDNSServiceInterfaceIndexAny,\n"
1748 " 1. missing browse and query \"add\" results for Wi-Fi interfaces aren't enough for a subtest to fail; and\n"
1749 " 2. unexpected browse and query results for Wi-Fi interfaces are ignored.\n"
1751 CLI_OPTION_GROUP( "Results" ),
1752 FormatOption( 'f', "format", &gRegistrationTest_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1753 StringOption( 'o', "output", &gRegistrationTest_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1755 TestExitStatusSection(),
1759 static void ExpensiveConstrainedTestCmd( void );
1761 static const char * gExpensiveConstrainedTest_Interface
= NULL
;
1762 static const char * gExpensiveConstrainedTest_Name
= NULL
;
1763 static Boolean gExpensiveConstrainedTest_DenyExpensive
= false;
1764 static Boolean gExpensiveConstrainedTest_DenyConstrained
= false;
1765 static Boolean gExpensiveConstrainedTest_StartFromExpensive
= false;
1766 static int gExpensiveConstrainedTest_ProtocolIPv4
= false;
1767 static int gExpensiveConstrainedTest_ProtocolIPv6
= false;
1768 static const char * gExpensiveConstrainedTest_OutputFormat
= kOutputFormatStr_JSON
;
1769 static const char * gExpensiveConstrainedTest_OutputFilePath
= NULL
;
1771 static CLIOption kExpensiveConstrainedTestOpts
[] =
1773 CLI_OPTION_GROUP( "Results" ),
1774 FormatOption( 'f', "format", &gExpensiveConstrainedTest_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1775 StringOption( 'o', "output", &gExpensiveConstrainedTest_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1777 TestExitStatusSection(),
1781 #if( MDNSRESPONDER_PROJECT )
1782 static void XCTestCmd( void );
1784 static const char * gXCTest_Classname
= NULL
;
1786 static CLIOption kXCTestOpts
[] =
1788 StringOption( 'c', "class", &gXCTest_Classname
, "classname", "The classname of the XCTest to run (from /AppleInternal/XCTests/com.apple.mDNSResponder/Tests.xctest)", true ),
1793 static CLIOption kTestOpts
[] =
1795 Command( "gaiperf", GAIPerfCmd
, kGAIPerfOpts
, "Runs DNSServiceGetAddrInfo() performance tests.", false ),
1796 Command( "mdnsdiscovery", MDNSDiscoveryTestCmd
, kMDNSDiscoveryTestOpts
, "Tests mDNS service discovery for correctness.", false ),
1797 Command( "dotlocal", DotLocalTestCmd
, kDotLocalTestOpts
, "Tests DNS and mDNS queries for domain names in the local domain.", false ),
1798 Command( "probeconflicts", ProbeConflictTestCmd
, kProbeConflictTestOpts
, "Tests various probing conflict scenarios.", false ),
1799 Command( "registration", RegistrationTestCmd
, kRegistrationTestOpts
, "Tests service registrations.", false ),
1800 Command( "expensive_constrained_updates", ExpensiveConstrainedTestCmd
, kExpensiveConstrainedTestOpts
, "Tests if the mDNSResponder can handle expensive and constrained property change correctly", false),
1801 #if( MDNSRESPONDER_PROJECT )
1802 Command( "xctest", XCTestCmd
, kXCTestOpts
, "Run a XCTest from /AppleInternal/XCTests/com.apple.mDNSResponder/Tests.xctest.", true ),
1807 //===========================================================================================================================
1808 // SSDP Command Options
1809 //===========================================================================================================================
1811 static int gSSDPDiscover_MX
= 1;
1812 static const char * gSSDPDiscover_ST
= "ssdp:all";
1813 static int gSSDPDiscover_ReceiveSecs
= 1;
1814 static int gSSDPDiscover_UseIPv4
= false;
1815 static int gSSDPDiscover_UseIPv6
= false;
1816 static int gSSDPDiscover_Verbose
= false;
1818 static CLIOption kSSDPDiscoverOpts
[] =
1820 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
1821 IntegerOption( 'm', "mx", &gSSDPDiscover_MX
, "seconds", "MX value in search request, i.e., max response delay in seconds. (Default: 1 second)", false ),
1822 StringOption( 's', "st", &gSSDPDiscover_ST
, "string", "ST value in search request, i.e., the search target. (Default: \"ssdp:all\")", false ),
1823 IntegerOption( 'r', "receiveTime", &gSSDPDiscover_ReceiveSecs
, "seconds", "Amount of time to spend receiving responses. -1 means unlimited. (Default: 1 second)", false ),
1824 BooleanOption( 0 , "ipv4", &gSSDPDiscover_UseIPv4
, "Use IPv4, i.e., multicast to 239.255.255.250:1900." ),
1825 BooleanOption( 0 , "ipv6", &gSSDPDiscover_UseIPv6
, "Use IPv6, i.e., multicast to [ff02::c]:1900" ),
1826 BooleanOption( 'v', "verbose", &gSSDPDiscover_Verbose
, "Prints the search request(s) that were sent." ),
1830 static void SSDPDiscoverCmd( void );
1832 static CLIOption kSSDPOpts
[] =
1834 Command( "discover", SSDPDiscoverCmd
, kSSDPDiscoverOpts
, "Crafts and multicasts an SSDP search message.", false ),
1838 #if( TARGET_OS_DARWIN )
1839 //===========================================================================================================================
1840 // res_query Command Options
1841 //===========================================================================================================================
1843 static void ResQueryCmd( void );
1845 static const char * gResQuery_Name
= NULL
;
1846 static const char * gResQuery_Type
= NULL
;
1847 static const char * gResQuery_Class
= NULL
;
1848 static int gResQuery_UseLibInfo
= false;
1850 static CLIOption kResQueryOpts
[] =
1852 StringOption( 'n', "name", &gResQuery_Name
, "domain name", "Full domain name of record to query.", true ),
1853 StringOption( 't', "type", &gResQuery_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1854 StringOption( 'c', "class", &gResQuery_Class
, "record class", "Record class by name or number. Default class is IN.", false ),
1855 BooleanOption( 0 , "libinfo", &gResQuery_UseLibInfo
, "Use res_query from libinfo instead of libresolv." ),
1859 //===========================================================================================================================
1860 // dns_query Command Options
1861 //===========================================================================================================================
1863 static void ResolvDNSQueryCmd( void );
1865 static const char * gResolvDNSQuery_Name
= NULL
;
1866 static const char * gResolvDNSQuery_Type
= NULL
;
1867 static const char * gResolvDNSQuery_Class
= NULL
;
1868 static const char * gResolvDNSQuery_Path
= NULL
;
1870 static CLIOption kResolvDNSQueryOpts
[] =
1872 StringOption( 'n', "name", &gResolvDNSQuery_Name
, "domain name", "Full domain name of record to query.", true ),
1873 StringOption( 't', "type", &gResolvDNSQuery_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1874 StringOption( 'c', "class", &gResolvDNSQuery_Class
, "record class", "Record class by name or number. Default class is IN.", false ),
1875 StringOption( 'p', "path", &gResolvDNSQuery_Path
, "file path", "The path argument to pass to dns_open() before calling dns_query(). Default value is NULL.", false ),
1879 //===========================================================================================================================
1880 // CFHost Command Options
1881 //===========================================================================================================================
1883 static void CFHostCmd( void );
1885 static const char * gCFHost_Name
= NULL
;
1886 static int gCFHost_WaitSecs
= 0;
1888 static CLIOption kCFHostOpts
[] =
1890 StringOption( 'n', "name", &gCFHost_Name
, "hostname", "Hostname to resolve.", true ),
1891 IntegerOption( 'w', "wait", &gCFHost_WaitSecs
, "seconds", "Time in seconds to wait before a normal exit. (default: 0)", false ),
1895 static CLIOption kLegacyOpts
[] =
1897 Command( "res_query", ResQueryCmd
, kResQueryOpts
, "Uses res_query() from either libresolv or libinfo to query for a record.", true ),
1898 Command( "dns_query", ResolvDNSQueryCmd
, kResolvDNSQueryOpts
, "Uses dns_query() from libresolv to query for a record.", true ),
1899 Command( "cfhost", CFHostCmd
, kCFHostOpts
, "Uses CFHost to resolve a hostname.", true ),
1903 //===========================================================================================================================
1904 // DNSConfigAdd Command Options
1905 //===========================================================================================================================
1907 static void DNSConfigAddCmd( void );
1909 static CFStringRef gDNSConfigAdd_ID
= NULL
;
1910 static char ** gDNSConfigAdd_IPAddrArray
= NULL
;
1911 static size_t gDNSConfigAdd_IPAddrCount
= 0;
1912 static char ** gDNSConfigAdd_DomainArray
= NULL
;
1913 static size_t gDNSConfigAdd_DomainCount
= 0;
1914 static const char * gDNSConfigAdd_Interface
= NULL
;
1916 static CLIOption kDNSConfigAddOpts
[] =
1918 CFStringOption( 0 , "id", &gDNSConfigAdd_ID
, "ID", "Arbitrary ID to use for resolver entry.", true ),
1919 MultiStringOption( 'a', "address", &gDNSConfigAdd_IPAddrArray
, &gDNSConfigAdd_IPAddrCount
, "IP address", "DNS server IP address(es). Can be specified more than once.", true ),
1920 MultiStringOption( 'd', "domain", &gDNSConfigAdd_DomainArray
, &gDNSConfigAdd_DomainCount
, "domain", "Specific domain(s) for the resolver entry. Can be specified more than once.", false ),
1921 StringOption( 'i', "interface", &gDNSConfigAdd_Interface
, "interface name", "Specific interface for the resolver entry.", false ),
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 //===========================================================================================================================
1928 // DNSConfigRemove Command Options
1929 //===========================================================================================================================
1931 static void DNSConfigRemoveCmd( void );
1933 static CFStringRef gDNSConfigRemove_ID
= NULL
;
1935 static CLIOption kDNSConfigRemoveOpts
[] =
1937 CFStringOption( 0, "id", &gDNSConfigRemove_ID
, "ID", "ID of resolver entry to remove.", true ),
1939 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1943 static CLIOption kDNSConfigOpts
[] =
1945 Command( "add", DNSConfigAddCmd
, kDNSConfigAddOpts
, "Add a supplemental resolver entry to the system's DNS configuration.", true ),
1946 Command( "remove", DNSConfigRemoveCmd
, kDNSConfigRemoveOpts
, "Remove a supplemental resolver entry from the system's DNS configuration.", true ),
1950 //===========================================================================================================================
1952 //===========================================================================================================================
1954 static void XPCSendCmd( void );
1956 static const char * gXPCSend_ServiceName
= NULL
;
1957 static const char * gXPCSend_MessageStr
= NULL
;
1959 static const char kXPCSendMessageSection_Name
[] = "Message Argument";
1960 static const char kXPCSendMessageSection_Text
[] =
1961 "XPC messages are described as a string using the following syntax.\n"
1963 "With the exception of the top-most XPC message dictionary, dictionaries begin with a '{' and end with a '}'.\n"
1964 "Key-value pairs are of the form <key>=<value>, where <key> is a string and <value> is a value of any of the\n"
1965 "currently supported XPC types.\n"
1967 "Arrays begin with a '[' and end with a ']'.\n"
1969 "The following non-container XPC types are supported:\n"
1971 "Type Syntax Example\n"
1972 "bool bool:<string> bool:true (or yes/y/on/1), bool:false (or no/n/off/0)\n"
1973 "data data:<hex string> data:C0000201\n"
1974 "int64 (signed 64-bit integer) int:<pos. or neg. integer> int:-1\n"
1975 "string string:<string> string:hello\\ world\n"
1976 "uint64 (unsigned 64-bit integer) uint:<pos. integer> uint:1024 or uint:0x400\n"
1977 "UUID uuid:<UUID> uuid:dab10183-84b5-4859-9de6-4bee287cfea3\n"
1979 "Example 1: 'cmd=string:add make=string:Apple model=string:Macintosh aliases=[string:Mac string:Macintosh\\ 128K]'\n"
1980 "Example 2: 'cmd=string:search features={portable=bool:yes solar=bool:no} priceMin=uint:100 priceMax=uint:200'\n";
1982 static CLIOption kXPCSendOpts
[] =
1984 StringOption( 's', "service", &gXPCSend_ServiceName
, "service name", "XPC service name.", true ),
1985 StringOption( 'm', "message", &gXPCSend_MessageStr
, "message", "XPC message as a string.", true ),
1987 CLI_SECTION( kXPCSendMessageSection_Name
, kXPCSendMessageSection_Text
),
1990 #endif // TARGET_OS_DARWIN
1992 #if( MDNSRESPONDER_PROJECT )
1993 //===========================================================================================================================
1995 //===========================================================================================================================
1997 static void InterfaceMonitorCmd( void );
1999 static CLIOption kInterfaceMonitorOpts
[] =
2001 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
2005 //===========================================================================================================================
2007 //===========================================================================================================================
2009 static void DNSProxyCmd( void );
2011 static char ** gDNSProxy_InputInterfaces
= NULL
;
2012 static size_t gDNSProxy_InputInterfaceCount
= 0;
2013 static const char * gDNSProxy_OutputInterface
= NULL
;
2015 static CLIOption kDNSProxyOpts
[] =
2017 MultiStringOption( 'i', "inputInterface", &gDNSProxy_InputInterfaces
, &gDNSProxy_InputInterfaceCount
, "name or index", "Interface to accept queries on. Can be specified more than once.", true ),
2018 StringOption( 'o', "outputInterface", &gDNSProxy_OutputInterface
, "name or index", "Interface to forward queries over. Use '0' for primary interface. (default: 0)", false ),
2022 #endif // MDNSRESPONDER_PROJECT
2024 //===========================================================================================================================
2026 //===========================================================================================================================
2028 static OSStatus
VersionOptionCallback( CLIOption
*inOption
, const char *inArg
, int inUnset
);
2030 static void BrowseCmd( void );
2031 static void GetAddrInfoCmd( void );
2032 static void QueryRecordCmd( void );
2033 static void RegisterCmd( void );
2034 static void RegisterRecordCmd( void );
2035 static void ResolveCmd( void );
2036 static void ReconfirmCmd( void );
2037 static void GetAddrInfoPOSIXCmd( void );
2038 static void ReverseLookupCmd( void );
2039 static void PortMappingCmd( void );
2040 static void BrowseAllCmd( void );
2041 static void GetAddrInfoStressCmd( void );
2042 static void DNSQueryCmd( void );
2043 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2044 static void DNSCryptCmd( void );
2046 static void MDNSQueryCmd( void );
2047 static void PIDToUUIDCmd( void );
2048 static void DaemonVersionCmd( void );
2050 static CLIOption kGlobalOpts
[] =
2052 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback
, NULL
, NULL
,
2053 kCLIOptionFlags_NoArgument
| kCLIOptionFlags_GlobalOnly
, "Displays the version of this tool.", NULL
),
2058 Command( "browse", BrowseCmd
, kBrowseOpts
, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
2059 Command( "getAddrInfo", GetAddrInfoCmd
, kGetAddrInfoOpts
, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
2060 Command( "queryRecord", QueryRecordCmd
, kQueryRecordOpts
, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
2061 Command( "register", RegisterCmd
, kRegisterOpts
, "Uses DNSServiceRegister() to register a service.", false ),
2062 Command( "registerRecord", RegisterRecordCmd
, kRegisterRecordOpts
, "Uses DNSServiceRegisterRecord() to register a record.", false ),
2063 Command( "resolve", ResolveCmd
, kResolveOpts
, "Uses DNSServiceResolve() to resolve a service.", false ),
2064 Command( "reconfirm", ReconfirmCmd
, kReconfirmOpts
, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
2065 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd
, kGetAddrInfoPOSIXOpts
, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
2066 Command( "reverseLookup", ReverseLookupCmd
, kReverseLookupOpts
, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
2067 Command( "portMapping", PortMappingCmd
, kPortMappingOpts
, "Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ),
2068 Command( "browseAll", BrowseAllCmd
, kBrowseAllOpts
, "Browse and resolve all (or specific) services and, optionally, attempt connections.", false ),
2070 // Uncommon commands.
2072 Command( "getnameinfo", GetNameInfoCmd
, kGetNameInfoOpts
, "Calls getnameinfo() and prints results.", true ),
2073 Command( "getAddrInfoStress", GetAddrInfoStressCmd
, kGetAddrInfoStressOpts
, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
2074 Command( "DNSQuery", DNSQueryCmd
, kDNSQueryOpts
, "Crafts and sends a DNS query.", true ),
2075 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2076 Command( "DNSCrypt", DNSCryptCmd
, kDNSCryptOpts
, "Crafts and sends a DNSCrypt query.", true ),
2078 Command( "mdnsquery", MDNSQueryCmd
, kMDNSQueryOpts
, "Crafts and sends an mDNS query over the specified interface.", true ),
2079 Command( "mdnscollider", MDNSColliderCmd
, kMDNSColliderOpts
, "Creates record name collision scenarios.", true ),
2080 Command( "pid2uuid", PIDToUUIDCmd
, kPIDToUUIDOpts
, "Prints the UUID of a process.", true ),
2081 Command( "server", DNSServerCmd
, kDNSServerOpts
, "DNS server for testing.", true ),
2082 Command( "mdnsreplier", MDNSReplierCmd
, kMDNSReplierOpts
, "Responds to mDNS queries for a set of authoritative resource records.", true ),
2083 Command( "test", NULL
, kTestOpts
, "Commands for testing DNS-SD.", true ),
2084 Command( "ssdp", NULL
, kSSDPOpts
, "Simple Service Discovery Protocol (SSDP).", true ),
2085 #if( TARGET_OS_DARWIN )
2086 Command( "legacy", NULL
, kLegacyOpts
, "Legacy DNS API.", true ),
2087 Command( "dnsconfig", NULL
, kDNSConfigOpts
, "Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ),
2088 Command( "xpcsend", XPCSendCmd
, kXPCSendOpts
, "Sends a message to an XPC service.", true ),
2090 #if( MDNSRESPONDER_PROJECT )
2091 Command( "interfaceMonitor", InterfaceMonitorCmd
, kInterfaceMonitorOpts
, "mDNSResponder's interface monitor.", true ),
2092 Command( "dnsproxy", DNSProxyCmd
, kDNSProxyOpts
, "Enables mDNSResponder's DNS proxy.", true ),
2094 Command( "daemonVersion", DaemonVersionCmd
, NULL
, "Prints the version of the DNS-SD daemon.", true ),
2100 //===========================================================================================================================
2101 // Helper Prototypes
2102 //===========================================================================================================================
2104 #define kExitReason_OneShotDone "one-shot done"
2105 #define kExitReason_ReceivedResponse "received response"
2106 #define kExitReason_SIGINT "interrupt signal"
2107 #define kExitReason_Timeout "timeout"
2108 #define kExitReason_TimeLimit "time limit"
2110 static void Exit( void *inContext
) ATTRIBUTE_NORETURN
;
2112 static DNSServiceFlags
GetDNSSDFlagsFromOpts( void );
2116 kConnectionType_None
= 0,
2117 kConnectionType_Normal
= 1,
2118 kConnectionType_DelegatePID
= 2,
2119 kConnectionType_DelegateUUID
= 3
2125 ConnectionType type
;
2136 CreateConnectionFromArgString(
2137 const char * inString
,
2138 dispatch_queue_t inQueue
,
2139 DNSServiceRef
* outSDRef
,
2140 ConnectionDesc
* outDesc
);
2141 static OSStatus
InterfaceIndexFromArgString( const char *inString
, uint32_t *outIndex
);
2142 static OSStatus
RecordDataFromArgString( const char *inString
, uint8_t **outDataPtr
, size_t *outDataLen
);
2143 static OSStatus
RecordTypeFromArgString( const char *inString
, uint16_t *outValue
);
2144 static OSStatus
RecordClassFromArgString( const char *inString
, uint16_t *outValue
);
2146 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
2148 static char * InterfaceIndexToName( uint32_t inIfIndex
, char inNameBuf
[ kInterfaceNameBufLen
] );
2149 static const char * RecordTypeToString( unsigned int inValue
);
2152 DNSRecordDataToString(
2153 const void * inRDataPtr
,
2155 unsigned int inRDataType
,
2156 const void * inMsgPtr
,
2158 char ** outString
);
2162 const uint8_t * inMsgPtr
,
2169 WriteDNSQueryMessage(
2170 uint8_t inMsg
[ kDNSQueryMessageMaxLen
],
2173 const char * inQName
,
2176 size_t * outMsgLen
);
2180 typedef void ( *DispatchHandler
)( void *inContext
);
2183 DispatchSignalSourceCreate(
2185 DispatchHandler inEventHandler
,
2187 dispatch_source_t
* outSource
);
2189 DispatchSocketSourceCreate(
2191 dispatch_source_type_t inType
,
2192 dispatch_queue_t inQueue
,
2193 DispatchHandler inEventHandler
,
2194 DispatchHandler inCancelHandler
,
2196 dispatch_source_t
* outSource
);
2198 #define DispatchReadSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2199 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_READ, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2201 #define DispatchWriteSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2202 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_WRITE, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2205 DispatchTimerCreate(
2206 dispatch_time_t inStart
,
2207 uint64_t inIntervalNs
,
2208 uint64_t inLeewayNs
,
2209 dispatch_queue_t inQueue
,
2210 DispatchHandler inEventHandler
,
2211 DispatchHandler inCancelHandler
,
2213 dispatch_source_t
* outTimer
);
2215 #define DispatchTimerOneShotCreate( IN_START, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, IN_CONTEXT, OUT_TIMER ) \
2216 DispatchTimerCreate( IN_START, DISPATCH_TIME_FOREVER, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, NULL, IN_CONTEXT, OUT_TIMER )
2219 DispatchProcessMonitorCreate(
2221 unsigned long inFlags
,
2222 dispatch_queue_t inQueue
,
2223 DispatchHandler inEventHandler
,
2224 DispatchHandler inCancelHandler
,
2226 dispatch_source_t
* outMonitor
);
2228 static const char * ServiceTypeDescription( const char *inName
);
2232 SocketRef sock
; // Socket.
2233 void * userContext
; // User context.
2234 int32_t refCount
; // Reference count.
2238 static OSStatus
SocketContextCreate( SocketRef inSock
, void * inUserContext
, SocketContext
**outContext
);
2239 static SocketContext
* SocketContextRetain( SocketContext
*inContext
);
2240 static void SocketContextRelease( SocketContext
*inContext
);
2241 static void SocketContextCancelHandler( void *inContext
);
2243 #define ForgetSocketContext( X ) ForgetCustom( X, SocketContextRelease )
2245 static OSStatus
StringToInt32( const char *inString
, int32_t *outValue
);
2246 static OSStatus
StringToUInt32( const char *inString
, uint32_t *outValue
);
2247 #if( TARGET_OS_DARWIN )
2248 static int64_t _StringToInt64( const char *inString
, OSStatus
*outError
);
2249 static uint64_t _StringToUInt64( const char *inString
, OSStatus
*outError
);
2250 static pid_t
_StringToPID( const char *inString
, OSStatus
*outError
);
2252 _ParseEscapedString(
2255 const char * inDelimiters
,
2258 size_t * outCopiedLen
,
2259 size_t * outActualLen
,
2260 const char ** outPtr
);
2262 static OSStatus
StringToARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2263 static OSStatus
StringToAAAARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2264 static OSStatus
StringToDomainName( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2265 #if( TARGET_OS_DARWIN )
2266 static OSStatus
GetDefaultDNSServer( sockaddr_ip
*outAddr
);
2269 _ServerSocketOpenEx2(
2273 const void * inAddr
,
2277 Boolean inNoPortReuse
,
2278 SocketRef
* outSock
);
2280 static const struct sockaddr
* GetMDNSMulticastAddrV4( void );
2281 static const struct sockaddr
* GetMDNSMulticastAddrV6( void );
2284 CreateMulticastSocket(
2285 const struct sockaddr
* inAddr
,
2287 const char * inIfName
,
2291 SocketRef
* outSock
);
2293 static OSStatus
DecimalTextToUInt32( const char *inSrc
, const char *inEnd
, uint32_t *outValue
, const char **outPtr
);
2294 static OSStatus
CheckIntegerArgument( int inArgValue
, const char *inArgName
, int inMin
, int inMax
);
2295 static OSStatus
CheckDoubleArgument( double inArgValue
, const char *inArgName
, double inMin
, double inMax
);
2296 static OSStatus
CheckRootUser( void );
2297 static OSStatus
SpawnCommand( pid_t
*outPID
, const char *inFormat
, ... );
2298 static OSStatus
OutputFormatFromArgString( const char *inArgString
, OutputFormatType
*outFormat
);
2299 static OSStatus
OutputPropertyList( CFPropertyListRef inPList
, OutputFormatType inType
, const char *inOutputFilePath
);
2300 static OSStatus
CreateSRVRecordDataFromString( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2301 static OSStatus
CreateTXTRecordDataFromString( const char *inString
, int inDelimiter
, uint8_t **outPtr
, size_t *outLen
);
2303 CreateNSECRecordData(
2304 const uint8_t * inNextDomainName
,
2307 unsigned int inTypeCount
,
2312 const uint8_t * inNamePtr
,
2317 const uint8_t * inMName
,
2318 const uint8_t * inRName
,
2323 uint32_t inMinimumTTL
,
2326 CreateSOARecordData(
2327 const uint8_t * inMName
,
2328 const uint8_t * inRName
,
2333 uint32_t inMinimumTTL
,
2337 _DataBuffer_AppendDNSQuestion(
2339 const uint8_t * inNamePtr
,
2344 _DataBuffer_AppendDNSRecord(
2346 const uint8_t * inNamePtr
,
2351 const uint8_t * inRDataPtr
,
2352 size_t inRDataLen
);
2353 static char * _NanoTime64ToTimestamp( NanoTime64 inTime
, char *inBuf
, size_t inMaxLen
);
2355 typedef struct MDNSInterfaceItem MDNSInterfaceItem
;
2356 struct MDNSInterfaceItem
2358 MDNSInterfaceItem
* next
;
2369 kMDNSInterfaceSubset_All
= 0, // All mDNS-capable interfaces.
2370 kMDNSInterfaceSubset_AWDL
= 1, // All mDNS-capable AWDL interfaces.
2371 kMDNSInterfaceSubset_NonAWDL
= 2 // All mDNS-capable non-AWDL iterfaces.
2373 } MDNSInterfaceSubset
;
2375 static OSStatus
_MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset
, size_t inItemSize
, MDNSInterfaceItem
**outList
);
2376 static void _MDNSInterfaceListFree( MDNSInterfaceItem
*inList
);
2377 #define _MDNSInterfaceListForget( X ) ForgetCustom( X, _MDNSInterfaceListFree )
2378 static OSStatus
_MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset
, char inNameBuf
[ IF_NAMESIZE
+ 1 ], uint32_t *outIndex
);
2380 static OSStatus
_SetComputerName( CFStringRef inComputerName
, CFStringEncoding inEncoding
);
2381 static OSStatus
_SetComputerNameWithUTF8CString( const char *inComputerName
);
2382 static OSStatus
_SetLocalHostName( CFStringRef inLocalHostName
);
2383 static OSStatus
_SetLocalHostNameWithUTF8CString( const char *inLocalHostName
);
2385 static OSStatus
_SocketWriteAll( SocketRef inSock
, const void *inData
, size_t inSize
, int32_t inTimeoutSecs
);
2387 _StringToIPv4Address(
2389 StringToIPAddressFlags inFlags
,
2392 uint32_t * outSubnet
,
2393 uint32_t * outRouter
,
2394 const char ** outStr
);
2395 static void _StringArray_Free( char **inArray
, size_t inCount
);
2397 _StringToIPv6Address(
2399 StringToIPAddressFlags inFlags
,
2400 uint8_t outIPv6
[ 16 ],
2401 uint32_t * outScope
,
2404 const char ** outStr
);
2406 _ParseQuotedEscapedString(
2409 const char * inDelimiters
,
2412 size_t * outCopiedLen
,
2413 size_t * outTotalLen
,
2414 const char ** outSrc
);
2415 static void * _memdup( const void *inPtr
, size_t inLen
);
2416 static int _memicmp( const void *inP1
, const void *inP2
, size_t inLen
);
2417 static uint32_t _FNV1( const void *inData
, size_t inSize
);
2419 #define Unused( X ) (void)(X)
2421 //===========================================================================================================================
2423 //===========================================================================================================================
2425 typedef struct MDNSColliderPrivate
* MDNSColliderRef
;
2427 typedef uint32_t MDNSColliderProtocols
;
2428 #define kMDNSColliderProtocol_None 0
2429 #define kMDNSColliderProtocol_IPv4 ( 1 << 0 )
2430 #define kMDNSColliderProtocol_IPv6 ( 1 << 1 )
2432 typedef void ( *MDNSColliderStopHandler_f
)( void *inContext
, OSStatus inError
);
2434 static OSStatus
MDNSColliderCreate( dispatch_queue_t inQueue
, MDNSColliderRef
*outCollider
);
2435 static OSStatus
MDNSColliderStart( MDNSColliderRef inCollider
);
2436 static void MDNSColliderStop( MDNSColliderRef inCollider
);
2437 static void MDNSColliderSetProtocols( MDNSColliderRef inCollider
, MDNSColliderProtocols inProtocols
);
2438 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef inCollider
, uint32_t inInterfaceIndex
);
2439 static OSStatus
MDNSColliderSetProgram( MDNSColliderRef inCollider
, const char *inProgramStr
);
2441 MDNSColliderSetStopHandler(
2442 MDNSColliderRef inCollider
,
2443 MDNSColliderStopHandler_f inStopHandler
,
2444 void * inStopContext
);
2446 MDNSColliderSetRecord(
2447 MDNSColliderRef inCollider
,
2448 const uint8_t * inName
,
2450 const void * inRDataPtr
,
2451 size_t inRDataLen
);
2452 static CFTypeID
MDNSColliderGetTypeID( void );
2454 //===========================================================================================================================
2456 //===========================================================================================================================
2458 typedef struct ServiceBrowserPrivate
* ServiceBrowserRef
;
2459 typedef struct ServiceBrowserResults ServiceBrowserResults
;
2460 typedef struct SBRDomain SBRDomain
;
2461 typedef struct SBRServiceType SBRServiceType
;
2462 typedef struct SBRServiceInstance SBRServiceInstance
;
2463 typedef struct SBRIPAddress SBRIPAddress
;
2465 typedef void ( *ServiceBrowserCallback_f
)( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
);
2467 struct ServiceBrowserResults
2469 SBRDomain
* domainList
; // List of domains in which services were found.
2474 SBRDomain
* next
; // Next domain in list.
2475 char * name
; // Name of domain represented by this object.
2476 SBRServiceType
* typeList
; // List of service types in this domain.
2479 struct SBRServiceType
2481 SBRServiceType
* next
; // Next service type in list.
2482 char * name
; // Name of service type represented by this object.
2483 SBRServiceInstance
* instanceList
; // List of service instances of this service type.
2486 struct SBRServiceInstance
2488 SBRServiceInstance
* next
; // Next service instance in list.
2489 char * name
; // Name of service instance represented by this object.
2490 char * hostname
; // Target from service instance's SRV record.
2491 uint32_t ifIndex
; // Index of interface over which this service instance was discovered.
2492 uint16_t port
; // Port from service instance's SRV record.
2493 uint8_t * txtPtr
; // Service instance's TXT record data.
2494 size_t txtLen
; // Service instance's TXT record data length.
2495 SBRIPAddress
* ipaddrList
; // List of IP addresses that the hostname resolved to.
2496 uint64_t discoverTimeUs
; // Time it took to discover this service instance in microseconds.
2497 uint64_t resolveTimeUs
; // Time it took to resolve this service instance in microseconds.
2502 SBRIPAddress
* next
; // Next IP address in list.
2503 sockaddr_ip sip
; // IPv4 or IPv6 address.
2504 uint64_t resolveTimeUs
; // Time it took to resolve this IP address in microseconds.
2507 static CFTypeID
ServiceBrowserGetTypeID( void );
2509 ServiceBrowserCreate(
2510 dispatch_queue_t inQueue
,
2511 uint32_t inInterfaceIndex
,
2512 const char * inDomain
,
2513 unsigned int inBrowseTimeSecs
,
2514 Boolean inIncludeAWDL
,
2515 ServiceBrowserRef
* outBrowser
);
2516 static void ServiceBrowserStart( ServiceBrowserRef inBrowser
);
2517 static OSStatus
ServiceBrowserAddServiceType( ServiceBrowserRef inBrowser
, const char *inServiceType
);
2519 ServiceBrowserSetCallback(
2520 ServiceBrowserRef inBrowser
,
2521 ServiceBrowserCallback_f inCallback
,
2523 static void ServiceBrowserResultsRetain( ServiceBrowserResults
*inResults
);
2524 static void ServiceBrowserResultsRelease( ServiceBrowserResults
*inResults
);
2526 #define ForgetServiceBrowserResults( X ) ForgetCustom( X, ServiceBrowserResultsRelease )
2528 //===========================================================================================================================
2530 //===========================================================================================================================
2532 #define _PRINTF_EXTENSION_HANDLER_DECLARE( NAME ) \
2534 _PrintFExtension ## NAME ## Handler( \
2535 PrintFContext * inContext, \
2536 PrintFFormat * inFormat, \
2537 PrintFVAList * inArgs, \
2538 void * inUserContext )
2540 _PRINTF_EXTENSION_HANDLER_DECLARE( Timestamp
);
2541 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSMessage
);
2542 _PRINTF_EXTENSION_HANDLER_DECLARE( CallbackFlags
);
2543 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSRecordData
);
2545 int main( int argc
, const char **argv
)
2549 // Route DebugServices logging output to stderr.
2551 dlog_control( "DebugServices:output=file;stderr" );
2553 PrintFRegisterExtension( "du:time", _PrintFExtensionTimestampHandler
, NULL
);
2554 PrintFRegisterExtension( "du:dnsmsg", _PrintFExtensionDNSMessageHandler
, NULL
);
2555 PrintFRegisterExtension( "du:cbflags", _PrintFExtensionCallbackFlagsHandler
, NULL
);
2556 PrintFRegisterExtension( "du:rdata", _PrintFExtensionDNSRecordDataHandler
, NULL
);
2557 CLIInit( argc
, argv
);
2558 err
= CLIParse( kGlobalOpts
, kCLIFlags_None
);
2559 if( err
) exit( 1 );
2561 return( gExitCode
);
2564 //===========================================================================================================================
2565 // VersionOptionCallback
2566 //===========================================================================================================================
2568 static OSStatus
VersionOptionCallback( CLIOption
*inOption
, const char *inArg
, int inUnset
)
2570 const char * srcVers
;
2571 #if( MDNSRESPONDER_PROJECT )
2579 #if( MDNSRESPONDER_PROJECT )
2580 srcVers
= SourceVersionToCString( _DNS_SD_H
, srcStr
);
2582 srcVers
= DNSSDUTIL_SOURCE_VERSION
;
2584 FPrintF( stdout
, "%s version %v (%s)\n", gProgramName
, kDNSSDUtilNumVersion
, srcVers
);
2586 return( kEndingErr
);
2589 //===========================================================================================================================
2591 //===========================================================================================================================
2593 typedef struct BrowseResolveOp BrowseResolveOp
;
2595 struct BrowseResolveOp
2597 BrowseResolveOp
* next
; // Next resolve operation in list.
2598 DNSServiceRef sdRef
; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
2599 char * fullName
; // Full name of the service to resolve.
2600 uint32_t interfaceIndex
; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
2605 DNSServiceRef mainRef
; // Main sdRef for shared connection.
2606 DNSServiceRef
* opRefs
; // Array of sdRefs for individual Browse operarions.
2607 size_t opRefsCount
; // Count of array of sdRefs for non-shared connections.
2608 const char * domain
; // Domain for DNSServiceBrowse operation(s).
2609 DNSServiceFlags flags
; // Flags for DNSServiceBrowse operation(s).
2610 char ** serviceTypes
; // Array of service types to browse for.
2611 size_t serviceTypesCount
; // Count of array of service types to browse for.
2612 int timeLimitSecs
; // Time limit of DNSServiceBrowse operation in seconds.
2613 BrowseResolveOp
* resolveList
; // List of resolve and/or TXT record query operations.
2614 uint32_t ifIndex
; // Interface index of DNSServiceBrowse operation(s).
2615 Boolean printedHeader
; // True if results header has been printed.
2616 Boolean doResolve
; // True if service instances are to be resolved.
2617 Boolean doResolveTXTOnly
; // True if TXT records of service instances are to be queried.
2621 static void BrowsePrintPrologue( const BrowseContext
*inContext
);
2622 static void BrowseContextFree( BrowseContext
*inContext
);
2623 static OSStatus
BrowseResolveOpCreate( const char *inFullName
, uint32_t inInterfaceIndex
, BrowseResolveOp
**outOp
);
2624 static void BrowseResolveOpFree( BrowseResolveOp
*inOp
);
2625 static void DNSSD_API
2627 DNSServiceRef inSDRef
,
2628 DNSServiceFlags inFlags
,
2629 uint32_t inInterfaceIndex
,
2630 DNSServiceErrorType inError
,
2631 const char * inName
,
2632 const char * inRegType
,
2633 const char * inDomain
,
2635 static void DNSSD_API
2636 BrowseResolveCallback(
2637 DNSServiceRef inSDRef
,
2638 DNSServiceFlags inFlags
,
2639 uint32_t inInterfaceIndex
,
2640 DNSServiceErrorType inError
,
2641 const char * inFullName
,
2642 const char * inHostname
,
2645 const unsigned char * inTXTPtr
,
2647 static void DNSSD_API
2648 BrowseQueryRecordCallback(
2649 DNSServiceRef inSDRef
,
2650 DNSServiceFlags inFlags
,
2651 uint32_t inInterfaceIndex
,
2652 DNSServiceErrorType inError
,
2653 const char * inFullName
,
2656 uint16_t inRDataLen
,
2657 const void * inRDataPtr
,
2661 static void BrowseCmd( void )
2665 BrowseContext
* context
= NULL
;
2666 dispatch_source_t signalSource
= NULL
;
2667 int useMainConnection
;
2669 // Set up SIGINT handler.
2671 signal( SIGINT
, SIG_IGN
);
2672 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
2673 require_noerr( err
, exit
);
2674 dispatch_resume( signalSource
);
2678 context
= (BrowseContext
*) calloc( 1, sizeof( *context
) );
2679 require_action( context
, exit
, err
= kNoMemoryErr
);
2681 context
->opRefs
= (DNSServiceRef
*) calloc( gBrowse_ServiceTypesCount
, sizeof( DNSServiceRef
) );
2682 require_action( context
->opRefs
, exit
, err
= kNoMemoryErr
);
2683 context
->opRefsCount
= gBrowse_ServiceTypesCount
;
2685 // Check command parameters.
2687 if( gBrowse_TimeLimitSecs
< 0 )
2689 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs
);
2694 // Create main connection.
2696 if( gConnectionOpt
)
2698 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
2699 require_noerr_quiet( err
, exit
);
2700 useMainConnection
= true;
2704 useMainConnection
= false;
2709 context
->flags
= GetDNSSDFlagsFromOpts();
2710 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
2714 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
2715 require_noerr_quiet( err
, exit
);
2717 // Set remaining parameters.
2719 context
->serviceTypes
= gBrowse_ServiceTypes
;
2720 context
->serviceTypesCount
= gBrowse_ServiceTypesCount
;
2721 context
->domain
= gBrowse_Domain
;
2722 context
->doResolve
= gBrowse_DoResolve
? true : false;
2723 context
->timeLimitSecs
= gBrowse_TimeLimitSecs
;
2724 context
->doResolveTXTOnly
= gBrowse_QueryTXT
? true : false;
2728 BrowsePrintPrologue( context
);
2730 // Start operation(s).
2732 for( i
= 0; i
< context
->serviceTypesCount
; ++i
)
2734 DNSServiceRef sdRef
;
2736 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
2737 err
= DNSServiceBrowse( &sdRef
, context
->flags
, context
->ifIndex
, context
->serviceTypes
[ i
], context
->domain
,
2738 BrowseCallback
, context
);
2739 require_noerr( err
, exit
);
2741 context
->opRefs
[ i
] = sdRef
;
2742 if( !useMainConnection
)
2744 err
= DNSServiceSetDispatchQueue( context
->opRefs
[ i
], dispatch_get_main_queue() );
2745 require_noerr( err
, exit
);
2751 if( context
->timeLimitSecs
> 0 )
2753 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
2754 kExitReason_TimeLimit
, Exit
);
2759 dispatch_source_forget( &signalSource
);
2760 if( context
) BrowseContextFree( context
);
2761 if( err
) exit( 1 );
2764 //===========================================================================================================================
2765 // BrowsePrintPrologue
2766 //===========================================================================================================================
2768 static void BrowsePrintPrologue( const BrowseContext
*inContext
)
2770 const int timeLimitSecs
= inContext
->timeLimitSecs
;
2771 const char * const * ptr
= (const char **) inContext
->serviceTypes
;
2772 const char * const * const end
= (const char **) inContext
->serviceTypes
+ inContext
->serviceTypesCount
;
2773 char ifName
[ kInterfaceNameBufLen
];
2775 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
2777 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
2778 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
2779 FPrintF( stdout
, "Service types: %s", *ptr
++ );
2780 while( ptr
< end
) FPrintF( stdout
, ", %s", *ptr
++ );
2781 FPrintF( stdout
, "\n" );
2782 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "<NULL> (default domains)" );
2783 FPrintF( stdout
, "Time limit: " );
2784 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
2785 else FPrintF( stdout
, "∞\n" );
2786 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
2787 FPrintF( stdout
, "---\n" );
2790 //===========================================================================================================================
2791 // BrowseContextFree
2792 //===========================================================================================================================
2794 static void BrowseContextFree( BrowseContext
*inContext
)
2798 for( i
= 0; i
< inContext
->opRefsCount
; ++i
)
2800 DNSServiceForget( &inContext
->opRefs
[ i
] );
2802 if( inContext
->serviceTypes
)
2804 _StringArray_Free( inContext
->serviceTypes
, inContext
->serviceTypesCount
);
2805 inContext
->serviceTypes
= NULL
;
2806 inContext
->serviceTypesCount
= 0;
2808 DNSServiceForget( &inContext
->mainRef
);
2812 //===========================================================================================================================
2813 // BrowseResolveOpCreate
2814 //===========================================================================================================================
2816 static OSStatus
BrowseResolveOpCreate( const char *inFullName
, uint32_t inInterfaceIndex
, BrowseResolveOp
**outOp
)
2819 BrowseResolveOp
* resolveOp
;
2821 resolveOp
= (BrowseResolveOp
*) calloc( 1, sizeof( *resolveOp
) );
2822 require_action( resolveOp
, exit
, err
= kNoMemoryErr
);
2824 resolveOp
->fullName
= strdup( inFullName
);
2825 require_action( resolveOp
->fullName
, exit
, err
= kNoMemoryErr
);
2827 resolveOp
->interfaceIndex
= inInterfaceIndex
;
2834 if( resolveOp
) BrowseResolveOpFree( resolveOp
);
2838 //===========================================================================================================================
2839 // BrowseResolveOpFree
2840 //===========================================================================================================================
2842 static void BrowseResolveOpFree( BrowseResolveOp
*inOp
)
2844 DNSServiceForget( &inOp
->sdRef
);
2845 ForgetMem( &inOp
->fullName
);
2849 //===========================================================================================================================
2851 //===========================================================================================================================
2853 static void DNSSD_API
2855 DNSServiceRef inSDRef
,
2856 DNSServiceFlags inFlags
,
2857 uint32_t inInterfaceIndex
,
2858 DNSServiceErrorType inError
,
2859 const char * inName
,
2860 const char * inRegType
,
2861 const char * inDomain
,
2864 BrowseContext
* const context
= (BrowseContext
*) inContext
;
2866 BrowseResolveOp
* newOp
= NULL
;
2867 BrowseResolveOp
** p
;
2868 char fullName
[ kDNSServiceMaxDomainName
];
2873 gettimeofday( &now
, NULL
);
2876 require_noerr( err
, exit
);
2878 if( !context
->printedHeader
)
2880 FPrintF( stdout
, "%-26s %-16s IF %-20s %-20s Instance Name\n", "Timestamp", "Flags", "Domain", "Service Type" );
2881 context
->printedHeader
= true;
2883 FPrintF( stdout
, "%{du:time} %{du:cbflags} %2d %-20s %-20s %s\n",
2884 &now
, inFlags
, (int32_t) inInterfaceIndex
, inDomain
, inRegType
, inName
);
2886 if( !context
->doResolve
&& !context
->doResolveTXTOnly
) goto exit
;
2888 err
= DNSServiceConstructFullName( fullName
, inName
, inRegType
, inDomain
);
2889 require_noerr( err
, exit
);
2891 if( inFlags
& kDNSServiceFlagsAdd
)
2893 DNSServiceRef sdRef
;
2894 DNSServiceFlags flags
;
2896 err
= BrowseResolveOpCreate( fullName
, inInterfaceIndex
, &newOp
);
2897 require_noerr( err
, exit
);
2899 if( context
->mainRef
)
2901 sdRef
= context
->mainRef
;
2902 flags
= kDNSServiceFlagsShareConnection
;
2908 if( context
->doResolve
)
2910 err
= DNSServiceResolve( &sdRef
, flags
, inInterfaceIndex
, inName
, inRegType
, inDomain
, BrowseResolveCallback
,
2912 require_noerr( err
, exit
);
2916 err
= DNSServiceQueryRecord( &sdRef
, flags
, inInterfaceIndex
, fullName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
,
2917 BrowseQueryRecordCallback
, NULL
);
2918 require_noerr( err
, exit
);
2921 newOp
->sdRef
= sdRef
;
2922 if( !context
->mainRef
)
2924 err
= DNSServiceSetDispatchQueue( newOp
->sdRef
, dispatch_get_main_queue() );
2925 require_noerr( err
, exit
);
2927 for( p
= &context
->resolveList
; *p
; p
= &( *p
)->next
) {}
2933 BrowseResolveOp
* resolveOp
;
2935 for( p
= &context
->resolveList
; ( resolveOp
= *p
) != NULL
; p
= &resolveOp
->next
)
2937 if( ( resolveOp
->interfaceIndex
== inInterfaceIndex
) && ( strcasecmp( resolveOp
->fullName
, fullName
) == 0 ) )
2944 *p
= resolveOp
->next
;
2945 BrowseResolveOpFree( resolveOp
);
2950 if( newOp
) BrowseResolveOpFree( newOp
);
2951 if( err
) exit( 1 );
2954 //===========================================================================================================================
2955 // BrowseQueryRecordCallback
2956 //===========================================================================================================================
2958 static void DNSSD_API
2959 BrowseQueryRecordCallback(
2960 DNSServiceRef inSDRef
,
2961 DNSServiceFlags inFlags
,
2962 uint32_t inInterfaceIndex
,
2963 DNSServiceErrorType inError
,
2964 const char * inFullName
,
2967 uint16_t inRDataLen
,
2968 const void * inRDataPtr
,
2978 Unused( inContext
);
2980 gettimeofday( &now
, NULL
);
2983 require_noerr( err
, exit
);
2984 require_action( inType
== kDNSServiceType_TXT
, exit
, err
= kTypeErr
);
2986 FPrintF( stdout
, "%{du:time} %s %s TXT on interface %d\n TXT: %#{txt}\n",
2987 &now
, DNSServiceFlagsToAddRmvStr( inFlags
), inFullName
, (int32_t) inInterfaceIndex
, inRDataPtr
,
2988 (size_t) inRDataLen
);
2991 if( err
) exit( 1 );
2994 //===========================================================================================================================
2995 // BrowseResolveCallback
2996 //===========================================================================================================================
2998 static void DNSSD_API
2999 BrowseResolveCallback(
3000 DNSServiceRef inSDRef
,
3001 DNSServiceFlags inFlags
,
3002 uint32_t inInterfaceIndex
,
3003 DNSServiceErrorType inError
,
3004 const char * inFullName
,
3005 const char * inHostname
,
3008 const unsigned char * inTXTPtr
,
3012 char errorStr
[ 64 ];
3016 Unused( inContext
);
3018 gettimeofday( &now
, NULL
);
3020 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " error %#m", inError
);
3022 FPrintF( stdout
, "%{du:time} %s can be reached at %s:%u (interface %d)%?s\n",
3023 &now
, inFullName
, inHostname
, ntohs( inPort
), (int32_t) inInterfaceIndex
, inError
, errorStr
);
3026 FPrintF( stdout
, " TXT record: %#H\n", inTXTPtr
, (int) inTXTLen
, INT_MAX
);
3030 FPrintF( stdout
, " TXT record: %#{txt}\n", inTXTPtr
, (size_t) inTXTLen
);
3034 //===========================================================================================================================
3036 //===========================================================================================================================
3040 DNSServiceRef mainRef
; // Main sdRef for shared connection.
3041 DNSServiceRef opRef
; // sdRef for the DNSServiceGetAddrInfo operation.
3042 const char * name
; // Hostname to resolve.
3043 DNSServiceFlags flags
; // Flags argument for DNSServiceGetAddrInfo().
3044 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceGetAddrInfo().
3045 uint32_t ifIndex
; // Interface index argument for DNSServiceGetAddrInfo().
3046 int timeLimitSecs
; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
3047 Boolean printedHeader
; // True if the results header has been printed.
3048 Boolean oneShotMode
; // True if command is done after the first set of results (one-shot mode).
3049 Boolean needIPv4
; // True if in one-shot mode and an IPv4 result is needed.
3050 Boolean needIPv6
; // True if in one-shot mode and an IPv6 result is needed.
3052 } GetAddrInfoContext
;
3054 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext
*inContext
);
3055 static void GetAddrInfoContextFree( GetAddrInfoContext
*inContext
);
3056 static void DNSSD_API
3057 GetAddrInfoCallback(
3058 DNSServiceRef inSDRef
,
3059 DNSServiceFlags inFlags
,
3060 uint32_t inInterfaceIndex
,
3061 DNSServiceErrorType inError
,
3062 const char * inHostname
,
3063 const struct sockaddr
* inSockAddr
,
3067 static void GetAddrInfoCmd( void )
3070 DNSServiceRef sdRef
;
3071 GetAddrInfoContext
* context
= NULL
;
3072 dispatch_source_t signalSource
= NULL
;
3073 int useMainConnection
;
3075 // Set up SIGINT handler.
3077 signal( SIGINT
, SIG_IGN
);
3078 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3079 require_noerr( err
, exit
);
3080 dispatch_resume( signalSource
);
3082 // Check command parameters.
3084 if( gGetAddrInfo_TimeLimitSecs
< 0 )
3086 FPrintF( stderr
, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs
);
3093 context
= (GetAddrInfoContext
*) calloc( 1, sizeof( *context
) );
3094 require_action( context
, exit
, err
= kNoMemoryErr
);
3096 // Create main connection.
3098 if( gConnectionOpt
)
3100 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
3101 require_noerr_quiet( err
, exit
);
3102 useMainConnection
= true;
3106 useMainConnection
= false;
3111 context
->flags
= GetDNSSDFlagsFromOpts();
3112 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
3116 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3117 require_noerr_quiet( err
, exit
);
3119 // Set remaining parameters.
3121 context
->name
= gGetAddrInfo_Name
;
3122 context
->timeLimitSecs
= gGetAddrInfo_TimeLimitSecs
;
3123 if( gGetAddrInfo_ProtocolIPv4
) context
->protocols
|= kDNSServiceProtocol_IPv4
;
3124 if( gGetAddrInfo_ProtocolIPv6
) context
->protocols
|= kDNSServiceProtocol_IPv6
;
3125 if( gGetAddrInfo_OneShot
)
3127 context
->oneShotMode
= true;
3128 context
->needIPv4
= ( gGetAddrInfo_ProtocolIPv4
|| !gGetAddrInfo_ProtocolIPv6
) ? true : false;
3129 context
->needIPv6
= ( gGetAddrInfo_ProtocolIPv6
|| !gGetAddrInfo_ProtocolIPv4
) ? true : false;
3134 GetAddrInfoPrintPrologue( context
);
3138 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
3139 err
= DNSServiceGetAddrInfo( &sdRef
, context
->flags
, context
->ifIndex
, context
->protocols
, context
->name
,
3140 GetAddrInfoCallback
, context
);
3141 require_noerr( err
, exit
);
3143 context
->opRef
= sdRef
;
3144 if( !useMainConnection
)
3146 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3147 require_noerr( err
, exit
);
3152 if( context
->timeLimitSecs
> 0 )
3154 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
3155 kExitReason_TimeLimit
, Exit
);
3160 dispatch_source_forget( &signalSource
);
3161 if( context
) GetAddrInfoContextFree( context
);
3162 if( err
) exit( 1 );
3165 //===========================================================================================================================
3166 // GetAddrInfoPrintPrologue
3167 //===========================================================================================================================
3169 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext
*inContext
)
3171 const int timeLimitSecs
= inContext
->timeLimitSecs
;
3172 char ifName
[ kInterfaceNameBufLen
];
3174 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3176 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3177 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3178 FPrintF( stdout
, "Protocols: %#{flags}\n", inContext
->protocols
, kDNSServiceProtocolDescriptors
);
3179 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
3180 FPrintF( stdout
, "Mode: %s\n", inContext
->oneShotMode
? "one-shot" : "continuous" );
3181 FPrintF( stdout
, "Time limit: " );
3182 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
3183 else FPrintF( stdout
, "∞\n" );
3184 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3185 FPrintF( stdout
, "---\n" );
3188 //===========================================================================================================================
3189 // GetAddrInfoContextFree
3190 //===========================================================================================================================
3192 static void GetAddrInfoContextFree( GetAddrInfoContext
*inContext
)
3194 DNSServiceForget( &inContext
->opRef
);
3195 DNSServiceForget( &inContext
->mainRef
);
3199 //===========================================================================================================================
3200 // GetAddrInfoCallback
3201 //===========================================================================================================================
3203 static void DNSSD_API
3204 GetAddrInfoCallback(
3205 DNSServiceRef inSDRef
,
3206 DNSServiceFlags inFlags
,
3207 uint32_t inInterfaceIndex
,
3208 DNSServiceErrorType inError
,
3209 const char * inHostname
,
3210 const struct sockaddr
* inSockAddr
,
3214 GetAddrInfoContext
* const context
= (GetAddrInfoContext
*) inContext
;
3217 const char * addrStr
;
3218 char addrStrBuf
[ kSockAddrStringMaxSize
];
3222 gettimeofday( &now
, NULL
);
3226 case kDNSServiceErr_NoError
:
3227 case kDNSServiceErr_NoSuchRecord
:
3231 case kDNSServiceErr_Timeout
:
3232 Exit( kExitReason_Timeout
);
3239 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
3241 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
3248 err
= SockAddrToString( inSockAddr
, kSockAddrStringFlagsNone
, addrStrBuf
);
3249 require_noerr( err
, exit
);
3250 addrStr
= addrStrBuf
;
3254 addrStr
= ( inSockAddr
->sa_family
== AF_INET
) ? kNoSuchRecordAStr
: kNoSuchRecordAAAAStr
;
3257 if( !context
->printedHeader
)
3259 FPrintF( stdout
, "%-26s %-16s IF %-30s %-34s %6s\n", "Timestamp", "Flags", "Hostname", "Address", "TTL" );
3260 context
->printedHeader
= true;
3262 FPrintF( stdout
, "%{du:time} %{du:cbflags} %2d %-30s %-34s %6u\n",
3263 &now
, inFlags
, (int32_t) inInterfaceIndex
, inHostname
, addrStr
, inTTL
);
3265 if( context
->oneShotMode
)
3267 if( inFlags
& kDNSServiceFlagsAdd
)
3269 if( inSockAddr
->sa_family
== AF_INET
) context
->needIPv4
= false;
3270 else context
->needIPv6
= false;
3272 if( !( inFlags
& kDNSServiceFlagsMoreComing
) && !context
->needIPv4
&& !context
->needIPv6
)
3274 Exit( kExitReason_OneShotDone
);
3279 if( err
) exit( 1 );
3282 //===========================================================================================================================
3284 //===========================================================================================================================
3288 DNSServiceRef mainRef
; // Main sdRef for shared connection.
3289 DNSServiceRef opRef
; // sdRef for the DNSServiceQueryRecord operation.
3290 const char * recordName
; // Resource record name argument for DNSServiceQueryRecord().
3291 DNSServiceFlags flags
; // Flags argument for DNSServiceQueryRecord().
3292 uint32_t ifIndex
; // Interface index argument for DNSServiceQueryRecord().
3293 int timeLimitSecs
; // Time limit for the DNSServiceQueryRecord() operation in seconds.
3294 uint16_t recordType
; // Resource record type argument for DNSServiceQueryRecord().
3295 Boolean printedHeader
; // True if the results header was printed.
3296 Boolean oneShotMode
; // True if command is done after the first set of results (one-shot mode).
3297 Boolean gotRecord
; // True if in one-shot mode and received at least one record of the desired type.
3298 Boolean printRawRData
; // True if RDATA results are not to be formatted when printed.
3300 } QueryRecordContext
;
3302 static void QueryRecordPrintPrologue( const QueryRecordContext
*inContext
);
3303 static void QueryRecordContextFree( QueryRecordContext
*inContext
);
3304 static void DNSSD_API
3305 QueryRecordCallback(
3306 DNSServiceRef inSDRef
,
3307 DNSServiceFlags inFlags
,
3308 uint32_t inInterfaceIndex
,
3309 DNSServiceErrorType inError
,
3310 const char * inFullName
,
3313 uint16_t inRDataLen
,
3314 const void * inRDataPtr
,
3318 static void QueryRecordCmd( void )
3321 DNSServiceRef sdRef
;
3322 QueryRecordContext
* context
= NULL
;
3323 dispatch_source_t signalSource
= NULL
;
3324 int useMainConnection
;
3326 // Set up SIGINT handler.
3328 signal( SIGINT
, SIG_IGN
);
3329 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3330 require_noerr( err
, exit
);
3331 dispatch_resume( signalSource
);
3335 context
= (QueryRecordContext
*) calloc( 1, sizeof( *context
) );
3336 require_action( context
, exit
, err
= kNoMemoryErr
);
3338 // Check command parameters.
3340 if( gQueryRecord_TimeLimitSecs
< 0 )
3342 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs
);
3347 // Create main connection.
3349 if( gConnectionOpt
)
3351 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
3352 require_noerr_quiet( err
, exit
);
3353 useMainConnection
= true;
3357 useMainConnection
= false;
3362 context
->flags
= GetDNSSDFlagsFromOpts();
3363 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
3367 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3368 require_noerr_quiet( err
, exit
);
3372 err
= RecordTypeFromArgString( gQueryRecord_Type
, &context
->recordType
);
3373 require_noerr( err
, exit
);
3375 // Set remaining parameters.
3377 context
->recordName
= gQueryRecord_Name
;
3378 context
->timeLimitSecs
= gQueryRecord_TimeLimitSecs
;
3379 context
->oneShotMode
= gQueryRecord_OneShot
? true : false;
3380 context
->printRawRData
= gQueryRecord_RawRData
? true : false;
3384 QueryRecordPrintPrologue( context
);
3388 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
3389 err
= DNSServiceQueryRecord( &sdRef
, context
->flags
, context
->ifIndex
, context
->recordName
, context
->recordType
,
3390 kDNSServiceClass_IN
, QueryRecordCallback
, context
);
3391 require_noerr( err
, exit
);
3393 context
->opRef
= sdRef
;
3394 if( !useMainConnection
)
3396 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3397 require_noerr( err
, exit
);
3402 if( context
->timeLimitSecs
> 0 )
3404 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_TimeLimit
,
3410 dispatch_source_forget( &signalSource
);
3411 if( context
) QueryRecordContextFree( context
);
3412 if( err
) exit( 1 );
3415 //===========================================================================================================================
3416 // QueryRecordContextFree
3417 //===========================================================================================================================
3419 static void QueryRecordContextFree( QueryRecordContext
*inContext
)
3421 DNSServiceForget( &inContext
->opRef
);
3422 DNSServiceForget( &inContext
->mainRef
);
3426 //===========================================================================================================================
3427 // QueryRecordPrintPrologue
3428 //===========================================================================================================================
3430 static void QueryRecordPrintPrologue( const QueryRecordContext
*inContext
)
3432 const int timeLimitSecs
= inContext
->timeLimitSecs
;
3433 char ifName
[ kInterfaceNameBufLen
];
3435 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3437 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3438 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3439 FPrintF( stdout
, "Name: %s\n", inContext
->recordName
);
3440 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->recordType
), inContext
->recordType
);
3441 FPrintF( stdout
, "Mode: %s\n", inContext
->oneShotMode
? "one-shot" : "continuous" );
3442 FPrintF( stdout
, "Time limit: " );
3443 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
3444 else FPrintF( stdout
, "∞\n" );
3445 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3446 FPrintF( stdout
, "---\n" );
3450 //===========================================================================================================================
3451 // QueryRecordCallback
3452 //===========================================================================================================================
3454 static void DNSSD_API
3455 QueryRecordCallback(
3456 DNSServiceRef inSDRef
,
3457 DNSServiceFlags inFlags
,
3458 uint32_t inInterfaceIndex
,
3459 DNSServiceErrorType inError
,
3460 const char * inFullName
,
3463 uint16_t inRDataLen
,
3464 const void * inRDataPtr
,
3468 QueryRecordContext
* const context
= (QueryRecordContext
*) inContext
;
3471 char * rdataStr
= NULL
;
3475 gettimeofday( &now
, NULL
);
3479 case kDNSServiceErr_NoError
:
3480 case kDNSServiceErr_NoSuchRecord
:
3484 case kDNSServiceErr_Timeout
:
3485 Exit( kExitReason_Timeout
);
3492 if( inError
!= kDNSServiceErr_NoSuchRecord
)
3494 if( !context
->printRawRData
) DNSRecordDataToString( inRDataPtr
, inRDataLen
, inType
, NULL
, 0, &rdataStr
);
3497 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, INT_MAX
);
3498 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
3502 if( !context
->printedHeader
)
3504 FPrintF( stdout
, "%-26s %-16s IF %-32s %-5s %-5s %6s RData\n",
3505 "Timestamp", "Flags", "Name", "Type", "Class", "TTL" );
3506 context
->printedHeader
= true;
3508 FPrintF( stdout
, "%{du:time} %{du:cbflags} %2d %-32s %-5s %?-5s%?5u %6u %s\n",
3509 &now
, inFlags
, (int32_t) inInterfaceIndex
, inFullName
, RecordTypeToString( inType
),
3510 ( inClass
== kDNSServiceClass_IN
), "IN", ( inClass
!= kDNSServiceClass_IN
), inClass
, inTTL
,
3511 rdataStr
? rdataStr
: kNoSuchRecordStr
);
3513 if( context
->oneShotMode
)
3515 if( ( inFlags
& kDNSServiceFlagsAdd
) &&
3516 ( ( context
->recordType
== kDNSServiceType_ANY
) || ( context
->recordType
== inType
) ) )
3518 context
->gotRecord
= true;
3520 if( !( inFlags
& kDNSServiceFlagsMoreComing
) && context
->gotRecord
) Exit( kExitReason_OneShotDone
);
3524 FreeNullSafe( rdataStr
);
3525 if( err
) exit( 1 );
3528 //===========================================================================================================================
3530 //===========================================================================================================================
3534 DNSRecordRef recordRef
; // Reference returned by DNSServiceAddRecord().
3535 uint8_t * dataPtr
; // Record data.
3536 size_t dataLen
; // Record data length.
3537 uint32_t ttl
; // Record TTL value.
3538 uint16_t type
; // Record type.
3544 DNSServiceRef opRef
; // sdRef for DNSServiceRegister operation.
3545 const char * name
; // Service name argument for DNSServiceRegister().
3546 const char * type
; // Service type argument for DNSServiceRegister().
3547 const char * domain
; // Domain in which advertise the service.
3548 uint8_t * txtPtr
; // Service TXT record data. (malloc'd)
3549 size_t txtLen
; // Service TXT record data len.
3550 ExtraRecord
* extraRecords
; // Array of extra records to add to registered service.
3551 size_t extraRecordsCount
; // Number of extra records.
3552 uint8_t * updateTXTPtr
; // Pointer to record data for TXT record update. (malloc'd)
3553 size_t updateTXTLen
; // Length of record data for TXT record update.
3554 uint32_t updateTTL
; // TTL of updated TXT record.
3555 int updateDelayMs
; // Post-registration TXT record update delay in milliseconds.
3556 DNSServiceFlags flags
; // Flags argument for DNSServiceRegister().
3557 uint32_t ifIndex
; // Interface index argument for DNSServiceRegister().
3558 int lifetimeMs
; // Lifetime of the record registration in milliseconds.
3559 uint16_t port
; // Service instance's port number.
3560 Boolean printedHeader
; // True if results header was printed.
3561 Boolean didRegister
; // True if service was registered.
3565 static void RegisterPrintPrologue( const RegisterContext
*inContext
);
3566 static void RegisterContextFree( RegisterContext
*inContext
);
3567 static void DNSSD_API
3569 DNSServiceRef inSDRef
,
3570 DNSServiceFlags inFlags
,
3571 DNSServiceErrorType inError
,
3572 const char * inName
,
3573 const char * inType
,
3574 const char * inDomain
,
3576 static void RegisterUpdate( void *inContext
);
3578 static void RegisterCmd( void )
3581 RegisterContext
* context
= NULL
;
3582 dispatch_source_t signalSource
= NULL
;
3584 // Set up SIGINT handler.
3586 signal( SIGINT
, SIG_IGN
);
3587 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3588 require_noerr( err
, exit
);
3589 dispatch_resume( signalSource
);
3593 context
= (RegisterContext
*) calloc( 1, sizeof( *context
) );
3594 require_action( context
, exit
, err
= kNoMemoryErr
);
3596 // Check command parameters.
3598 if( ( gRegister_Port
< 0 ) || ( gRegister_Port
> UINT16_MAX
) )
3600 FPrintF( stderr
, "Port number %d is out-of-range.\n", gRegister_Port
);
3605 if( ( gAddRecord_DataCount
!= gAddRecord_TypesCount
) || ( gAddRecord_TTLsCount
!= gAddRecord_TypesCount
) )
3607 FPrintF( stderr
, "There are missing additional record parameters.\n" );
3614 context
->flags
= GetDNSSDFlagsFromOpts();
3616 // Get interface index.
3618 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3619 require_noerr_quiet( err
, exit
);
3621 // Get TXT record data.
3625 err
= RecordDataFromArgString( gRegister_TXT
, &context
->txtPtr
, &context
->txtLen
);
3626 require_noerr_quiet( err
, exit
);
3629 // Set remaining parameters.
3631 context
->name
= gRegister_Name
;
3632 context
->type
= gRegister_Type
;
3633 context
->domain
= gRegister_Domain
;
3634 context
->port
= (uint16_t) gRegister_Port
;
3635 context
->lifetimeMs
= gRegister_LifetimeMs
;
3637 if( gAddRecord_TypesCount
> 0 )
3641 context
->extraRecords
= (ExtraRecord
*) calloc( gAddRecord_TypesCount
, sizeof( ExtraRecord
) );
3642 require_action( context
, exit
, err
= kNoMemoryErr
);
3643 context
->extraRecordsCount
= gAddRecord_TypesCount
;
3645 for( i
= 0; i
< gAddRecord_TypesCount
; ++i
)
3647 ExtraRecord
* const extraRecord
= &context
->extraRecords
[ i
];
3649 err
= RecordTypeFromArgString( gAddRecord_Types
[ i
], &extraRecord
->type
);
3650 require_noerr( err
, exit
);
3652 err
= StringToUInt32( gAddRecord_TTLs
[ i
], &extraRecord
->ttl
);
3655 FPrintF( stderr
, "Invalid TTL value: %s\n", gAddRecord_TTLs
[ i
] );
3660 err
= RecordDataFromArgString( gAddRecord_Data
[ i
], &extraRecord
->dataPtr
, &extraRecord
->dataLen
);
3661 require_noerr_quiet( err
, exit
);
3665 if( gUpdateRecord_Data
)
3667 err
= RecordDataFromArgString( gUpdateRecord_Data
, &context
->updateTXTPtr
, &context
->updateTXTLen
);
3668 require_noerr_quiet( err
, exit
);
3670 context
->updateTTL
= (uint32_t) gUpdateRecord_TTL
;
3671 context
->updateDelayMs
= gUpdateRecord_DelayMs
;
3676 RegisterPrintPrologue( context
);
3680 err
= DNSServiceRegister( &context
->opRef
, context
->flags
, context
->ifIndex
, context
->name
, context
->type
,
3681 context
->domain
, NULL
, htons( context
->port
), (uint16_t) context
->txtLen
, context
->txtPtr
,
3682 RegisterCallback
, context
);
3683 ForgetMem( &context
->txtPtr
);
3684 require_noerr( err
, exit
);
3686 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3687 require_noerr( err
, exit
);
3692 dispatch_source_forget( &signalSource
);
3693 if( context
) RegisterContextFree( context
);
3694 if( err
) exit( 1 );
3697 //===========================================================================================================================
3698 // RegisterPrintPrologue
3699 //===========================================================================================================================
3701 static void RegisterPrintPrologue( const RegisterContext
*inContext
)
3705 char ifName
[ kInterfaceNameBufLen
];
3707 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3709 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3710 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3711 FPrintF( stdout
, "Name: %s\n", inContext
->name
? inContext
->name
: "<NULL>" );
3712 FPrintF( stdout
, "Type: %s\n", inContext
->type
);
3713 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "<NULL> (default domains)" );
3714 FPrintF( stdout
, "Port: %u\n", inContext
->port
);
3715 FPrintF( stdout
, "TXT data: %#{txt}\n", inContext
->txtPtr
, inContext
->txtLen
);
3716 infinite
= ( inContext
->lifetimeMs
< 0 ) ? true : false;
3717 FPrintF( stdout
, "Lifetime: %?s%?d ms\n", infinite
, "∞", !infinite
, inContext
->lifetimeMs
);
3718 if( inContext
->updateTXTPtr
)
3720 FPrintF( stdout
, "\nUpdate record:\n" );
3721 FPrintF( stdout
, " Delay: %d ms\n", ( inContext
->updateDelayMs
> 0 ) ? inContext
->updateDelayMs
: 0 );
3722 FPrintF( stdout
, " TTL: %u%?s\n",
3723 inContext
->updateTTL
, inContext
->updateTTL
== 0, " (system will use a default value.)" );
3724 FPrintF( stdout
, " TXT data: %#{txt}\n", inContext
->updateTXTPtr
, inContext
->updateTXTLen
);
3726 if( inContext
->extraRecordsCount
> 0 ) FPrintF( stdout
, "\n" );
3727 for( i
= 0; i
< inContext
->extraRecordsCount
; ++i
)
3729 const ExtraRecord
* record
= &inContext
->extraRecords
[ i
];
3731 FPrintF( stdout
, "Extra record %zu:\n", i
+ 1 );
3732 FPrintF( stdout
, " Type: %s (%u)\n", RecordTypeToString( record
->type
), record
->type
);
3733 FPrintF( stdout
, " TTL: %u%?s\n", record
->ttl
, record
->ttl
== 0, " (system will use a default value.)" );
3734 FPrintF( stdout
, " RData: %#H\n\n", record
->dataPtr
, (int) record
->dataLen
, INT_MAX
);
3736 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3737 FPrintF( stdout
, "---\n" );
3740 //===========================================================================================================================
3741 // RegisterContextFree
3742 //===========================================================================================================================
3744 static void RegisterContextFree( RegisterContext
*inContext
)
3746 ExtraRecord
* record
;
3747 const ExtraRecord
* const end
= inContext
->extraRecords
+ inContext
->extraRecordsCount
;
3749 DNSServiceForget( &inContext
->opRef
);
3750 ForgetMem( &inContext
->txtPtr
);
3751 for( record
= inContext
->extraRecords
; record
< end
; ++record
)
3753 check( !record
->recordRef
);
3754 ForgetMem( &record
->dataPtr
);
3756 ForgetMem( &inContext
->extraRecords
);
3757 ForgetMem( &inContext
->updateTXTPtr
);
3761 //===========================================================================================================================
3763 //===========================================================================================================================
3765 static void DNSSD_API
3767 DNSServiceRef inSDRef
,
3768 DNSServiceFlags inFlags
,
3769 DNSServiceErrorType inError
,
3770 const char * inName
,
3771 const char * inType
,
3772 const char * inDomain
,
3775 RegisterContext
* const context
= (RegisterContext
*) inContext
;
3781 gettimeofday( &now
, NULL
);
3783 if( !context
->printedHeader
)
3785 FPrintF( stdout
, "%-26s %-16s Service\n", "Timestamp", "Flags" );
3786 context
->printedHeader
= true;
3788 FPrintF( stdout
, "%{du:time} %{du:cbflags} %s.%s%s %?#m\n", &now
, inFlags
, inName
, inType
, inDomain
, inError
, inError
);
3790 require_noerr_action_quiet( inError
, exit
, err
= inError
);
3792 if( !context
->didRegister
&& ( inFlags
& kDNSServiceFlagsAdd
) )
3794 context
->didRegister
= true;
3795 if( context
->updateTXTPtr
)
3797 if( context
->updateDelayMs
> 0 )
3799 dispatch_after_f( dispatch_time_milliseconds( context
->updateDelayMs
), dispatch_get_main_queue(),
3800 context
, RegisterUpdate
);
3804 RegisterUpdate( context
);
3807 if( context
->extraRecordsCount
> 0 )
3809 ExtraRecord
* record
;
3810 const ExtraRecord
* const end
= context
->extraRecords
+ context
->extraRecordsCount
;
3812 for( record
= context
->extraRecords
; record
< end
; ++record
)
3814 err
= DNSServiceAddRecord( context
->opRef
, &record
->recordRef
, 0, record
->type
,
3815 (uint16_t) record
->dataLen
, record
->dataPtr
, record
->ttl
);
3816 require_noerr( err
, exit
);
3819 if( context
->lifetimeMs
== 0 )
3821 Exit( kExitReason_TimeLimit
);
3823 else if( context
->lifetimeMs
> 0 )
3825 dispatch_after_f( dispatch_time_milliseconds( context
->lifetimeMs
), dispatch_get_main_queue(),
3826 kExitReason_TimeLimit
, Exit
);
3832 if( err
) exit( 1 );
3835 //===========================================================================================================================
3837 //===========================================================================================================================
3839 static void RegisterUpdate( void *inContext
)
3842 RegisterContext
* const context
= (RegisterContext
*) inContext
;
3844 err
= DNSServiceUpdateRecord( context
->opRef
, NULL
, 0, (uint16_t) context
->updateTXTLen
, context
->updateTXTPtr
,
3845 context
->updateTTL
);
3846 require_noerr( err
, exit
);
3849 if( err
) exit( 1 );
3852 //===========================================================================================================================
3853 // RegisterRecordCmd
3854 //===========================================================================================================================
3858 DNSServiceRef conRef
; // sdRef to be initialized by DNSServiceCreateConnection().
3859 DNSRecordRef recordRef
; // Registered record reference.
3860 const char * recordName
; // Name of resource record.
3861 uint8_t * dataPtr
; // Pointer to resource record data.
3862 size_t dataLen
; // Length of resource record data.
3863 uint32_t ttl
; // TTL value of resource record in seconds.
3864 uint32_t ifIndex
; // Interface index argument for DNSServiceRegisterRecord().
3865 DNSServiceFlags flags
; // Flags argument for DNSServiceRegisterRecord().
3866 int lifetimeMs
; // Lifetime of the record registration in milliseconds.
3867 uint16_t recordType
; // Resource record type.
3868 uint8_t * updateDataPtr
; // Pointer to data for record update. (malloc'd)
3869 size_t updateDataLen
; // Length of data for record update.
3870 uint32_t updateTTL
; // TTL for updated record.
3871 int updateDelayMs
; // Post-registration record update delay in milliseconds.
3872 Boolean didRegister
; // True if the record was registered.
3874 } RegisterRecordContext
;
3876 static void RegisterRecordPrintPrologue( const RegisterRecordContext
*inContext
);
3877 static void RegisterRecordContextFree( RegisterRecordContext
*inContext
);
3878 static void DNSSD_API
3879 RegisterRecordCallback(
3880 DNSServiceRef inSDRef
,
3881 DNSRecordRef inRecordRef
,
3882 DNSServiceFlags inFlags
,
3883 DNSServiceErrorType inError
,
3885 static void RegisterRecordUpdate( void *inContext
);
3887 static void RegisterRecordCmd( void )
3890 RegisterRecordContext
* context
= NULL
;
3891 dispatch_source_t signalSource
= NULL
;
3893 // Set up SIGINT handler.
3895 signal( SIGINT
, SIG_IGN
);
3896 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3897 require_noerr( err
, exit
);
3898 dispatch_resume( signalSource
);
3902 context
= (RegisterRecordContext
*) calloc( 1, sizeof( *context
) );
3903 require_action( context
, exit
, err
= kNoMemoryErr
);
3905 // Create connection.
3907 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->conRef
, NULL
);
3908 require_noerr_quiet( err
, exit
);
3912 context
->flags
= GetDNSSDFlagsFromOpts();
3916 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3917 require_noerr_quiet( err
, exit
);
3921 err
= RecordTypeFromArgString( gRegisterRecord_Type
, &context
->recordType
);
3922 require_noerr( err
, exit
);
3926 if( gRegisterRecord_Data
)
3928 err
= RecordDataFromArgString( gRegisterRecord_Data
, &context
->dataPtr
, &context
->dataLen
);
3929 require_noerr_quiet( err
, exit
);
3932 // Set remaining parameters.
3934 context
->recordName
= gRegisterRecord_Name
;
3935 context
->ttl
= (uint32_t) gRegisterRecord_TTL
;
3936 context
->lifetimeMs
= gRegisterRecord_LifetimeMs
;
3940 if( gRegisterRecord_UpdateData
)
3942 err
= RecordDataFromArgString( gRegisterRecord_UpdateData
, &context
->updateDataPtr
, &context
->updateDataLen
);
3943 require_noerr_quiet( err
, exit
);
3945 context
->updateTTL
= (uint32_t) gRegisterRecord_UpdateTTL
;
3946 context
->updateDelayMs
= gRegisterRecord_UpdateDelayMs
;
3951 RegisterRecordPrintPrologue( context
);
3955 err
= DNSServiceRegisterRecord( context
->conRef
, &context
->recordRef
, context
->flags
, context
->ifIndex
,
3956 context
->recordName
, context
->recordType
, kDNSServiceClass_IN
, (uint16_t) context
->dataLen
, context
->dataPtr
,
3957 context
->ttl
, RegisterRecordCallback
, context
);
3960 FPrintF( stderr
, "DNSServiceRegisterRecord() returned %#m\n", err
);
3967 dispatch_source_forget( &signalSource
);
3968 if( context
) RegisterRecordContextFree( context
);
3969 if( err
) exit( 1 );
3972 //===========================================================================================================================
3973 // RegisterRecordPrintPrologue
3974 //===========================================================================================================================
3976 static void RegisterRecordPrintPrologue( const RegisterRecordContext
*inContext
)
3979 char ifName
[ kInterfaceNameBufLen
];
3981 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3983 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3984 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3985 FPrintF( stdout
, "Name: %s\n", inContext
->recordName
);
3986 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->recordType
), inContext
->recordType
);
3987 FPrintF( stdout
, "TTL: %u\n", inContext
->ttl
);
3988 FPrintF( stdout
, "Data: %#H\n", inContext
->dataPtr
, (int) inContext
->dataLen
, INT_MAX
);
3989 infinite
= ( inContext
->lifetimeMs
< 0 ) ? true : false;
3990 FPrintF( stdout
, "Lifetime: %?s%?d ms\n", infinite
, "∞", !infinite
, inContext
->lifetimeMs
);
3991 if( inContext
->updateDataPtr
)
3993 FPrintF( stdout
, "\nUpdate record:\n" );
3994 FPrintF( stdout
, " Delay: %d ms\n", ( inContext
->updateDelayMs
>= 0 ) ? inContext
->updateDelayMs
: 0 );
3995 FPrintF( stdout
, " TTL: %u%?s\n",
3996 inContext
->updateTTL
, inContext
->updateTTL
== 0, " (system will use a default value.)" );
3997 FPrintF( stdout
, " RData: %#H\n", inContext
->updateDataPtr
, (int) inContext
->updateDataLen
, INT_MAX
);
3999 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4000 FPrintF( stdout
, "---\n" );
4003 //===========================================================================================================================
4004 // RegisterRecordContextFree
4005 //===========================================================================================================================
4007 static void RegisterRecordContextFree( RegisterRecordContext
*inContext
)
4009 DNSServiceForget( &inContext
->conRef
);
4010 ForgetMem( &inContext
->dataPtr
);
4011 ForgetMem( &inContext
->updateDataPtr
);
4015 //===========================================================================================================================
4016 // RegisterRecordCallback
4017 //===========================================================================================================================
4020 RegisterRecordCallback(
4021 DNSServiceRef inSDRef
,
4022 DNSRecordRef inRecordRef
,
4023 DNSServiceFlags inFlags
,
4024 DNSServiceErrorType inError
,
4027 RegisterRecordContext
* context
= (RegisterRecordContext
*) inContext
;
4031 Unused( inRecordRef
);
4035 gettimeofday( &now
, NULL
);
4036 FPrintF( stdout
, "%{du:time} Record registration result (error %#m)\n", &now
, inError
);
4038 if( !context
->didRegister
&& !inError
)
4040 context
->didRegister
= true;
4041 if( context
->updateDataPtr
)
4043 if( context
->updateDelayMs
> 0 )
4045 dispatch_after_f( dispatch_time_milliseconds( context
->updateDelayMs
), dispatch_get_main_queue(),
4046 context
, RegisterRecordUpdate
);
4050 RegisterRecordUpdate( context
);
4053 if( context
->lifetimeMs
== 0 )
4055 Exit( kExitReason_TimeLimit
);
4057 else if( context
->lifetimeMs
> 0 )
4059 dispatch_after_f( dispatch_time_milliseconds( context
->lifetimeMs
), dispatch_get_main_queue(),
4060 kExitReason_TimeLimit
, Exit
);
4065 //===========================================================================================================================
4066 // RegisterRecordUpdate
4067 //===========================================================================================================================
4069 static void RegisterRecordUpdate( void *inContext
)
4072 RegisterRecordContext
* const context
= (RegisterRecordContext
*) inContext
;
4074 err
= DNSServiceUpdateRecord( context
->conRef
, context
->recordRef
, 0, (uint16_t) context
->updateDataLen
,
4075 context
->updateDataPtr
, context
->updateTTL
);
4076 require_noerr( err
, exit
);
4079 if( err
) exit( 1 );
4082 //===========================================================================================================================
4084 //===========================================================================================================================
4088 DNSServiceRef mainRef
; // Main sdRef for shared connections.
4089 DNSServiceRef opRef
; // sdRef for the DNSServiceResolve operation.
4090 DNSServiceFlags flags
; // Flags argument for DNSServiceResolve().
4091 const char * name
; // Service name argument for DNSServiceResolve().
4092 const char * type
; // Service type argument for DNSServiceResolve().
4093 const char * domain
; // Domain argument for DNSServiceResolve().
4094 uint32_t ifIndex
; // Interface index argument for DNSServiceResolve().
4095 int timeLimitSecs
; // Time limit for the DNSServiceResolve operation in seconds.
4099 static void ResolvePrintPrologue( const ResolveContext
*inContext
);
4100 static void ResolveContextFree( ResolveContext
*inContext
);
4101 static void DNSSD_API
4103 DNSServiceRef inSDRef
,
4104 DNSServiceFlags inFlags
,
4105 uint32_t inInterfaceIndex
,
4106 DNSServiceErrorType inError
,
4107 const char * inFullName
,
4108 const char * inHostname
,
4111 const unsigned char * inTXTPtr
,
4114 static void ResolveCmd( void )
4117 DNSServiceRef sdRef
;
4118 ResolveContext
* context
= NULL
;
4119 dispatch_source_t signalSource
= NULL
;
4120 int useMainConnection
;
4122 // Set up SIGINT handler.
4124 signal( SIGINT
, SIG_IGN
);
4125 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
4126 require_noerr( err
, exit
);
4127 dispatch_resume( signalSource
);
4131 context
= (ResolveContext
*) calloc( 1, sizeof( *context
) );
4132 require_action( context
, exit
, err
= kNoMemoryErr
);
4134 // Check command parameters.
4136 if( gResolve_TimeLimitSecs
< 0 )
4138 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs
);
4143 // Create main connection.
4145 if( gConnectionOpt
)
4147 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
4148 require_noerr_quiet( err
, exit
);
4149 useMainConnection
= true;
4153 useMainConnection
= false;
4158 context
->flags
= GetDNSSDFlagsFromOpts();
4159 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
4161 // Get interface index.
4163 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
4164 require_noerr_quiet( err
, exit
);
4166 // Set remaining parameters.
4168 context
->name
= gResolve_Name
;
4169 context
->type
= gResolve_Type
;
4170 context
->domain
= gResolve_Domain
;
4171 context
->timeLimitSecs
= gResolve_TimeLimitSecs
;
4175 ResolvePrintPrologue( context
);
4179 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
4180 err
= DNSServiceResolve( &sdRef
, context
->flags
, context
->ifIndex
, context
->name
, context
->type
, context
->domain
,
4181 ResolveCallback
, NULL
);
4182 require_noerr( err
, exit
);
4184 context
->opRef
= sdRef
;
4185 if( !useMainConnection
)
4187 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
4188 require_noerr( err
, exit
);
4193 if( context
->timeLimitSecs
> 0 )
4195 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
4196 kExitReason_TimeLimit
, Exit
);
4201 dispatch_source_forget( &signalSource
);
4202 if( context
) ResolveContextFree( context
);
4203 if( err
) exit( 1 );
4206 //===========================================================================================================================
4208 //===========================================================================================================================
4210 static void ReconfirmCmd( void )
4213 uint8_t * rdataPtr
= NULL
;
4214 size_t rdataLen
= 0;
4215 DNSServiceFlags flags
;
4217 uint16_t type
, class;
4218 char ifName
[ kInterfaceNameBufLen
];
4222 flags
= GetDNSSDFlagsFromOpts();
4224 // Get interface index.
4226 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
4227 require_noerr_quiet( err
, exit
);
4231 err
= RecordTypeFromArgString( gReconfirmRecord_Type
, &type
);
4232 require_noerr( err
, exit
);
4236 if( gReconfirmRecord_Data
)
4238 err
= RecordDataFromArgString( gReconfirmRecord_Data
, &rdataPtr
, &rdataLen
);
4239 require_noerr_quiet( err
, exit
);
4242 // Get record class.
4244 if( gReconfirmRecord_Class
)
4246 err
= RecordClassFromArgString( gReconfirmRecord_Class
, &class );
4247 require_noerr( err
, exit
);
4251 class = kDNSServiceClass_IN
;
4256 FPrintF( stdout
, "Flags: %#{flags}\n", flags
, kDNSServiceFlagsDescriptors
);
4257 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
4258 FPrintF( stdout
, "Name: %s\n", gReconfirmRecord_Name
);
4259 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
4260 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
4261 FPrintF( stdout
, "Data: %#H\n", rdataPtr
, (int) rdataLen
, INT_MAX
);
4262 FPrintF( stdout
, "---\n" );
4264 err
= DNSServiceReconfirmRecord( flags
, ifIndex
, gReconfirmRecord_Name
, type
, class, (uint16_t) rdataLen
, rdataPtr
);
4265 FPrintF( stdout
, "Error: %#m\n", err
);
4268 FreeNullSafe( rdataPtr
);
4269 if( err
) exit( 1 );
4272 //===========================================================================================================================
4273 // ResolvePrintPrologue
4274 //===========================================================================================================================
4276 static void ResolvePrintPrologue( const ResolveContext
*inContext
)
4278 const int timeLimitSecs
= inContext
->timeLimitSecs
;
4279 char ifName
[ kInterfaceNameBufLen
];
4281 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
4283 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
4284 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
4285 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
4286 FPrintF( stdout
, "Type: %s\n", inContext
->type
);
4287 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
);
4288 FPrintF( stdout
, "Time limit: " );
4289 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
4290 else FPrintF( stdout
, "∞\n" );
4291 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4292 FPrintF( stdout
, "---\n" );
4295 //===========================================================================================================================
4296 // ResolveContextFree
4297 //===========================================================================================================================
4299 static void ResolveContextFree( ResolveContext
*inContext
)
4301 DNSServiceForget( &inContext
->opRef
);
4302 DNSServiceForget( &inContext
->mainRef
);
4306 //===========================================================================================================================
4308 //===========================================================================================================================
4310 static void DNSSD_API
4312 DNSServiceRef inSDRef
,
4313 DNSServiceFlags inFlags
,
4314 uint32_t inInterfaceIndex
,
4315 DNSServiceErrorType inError
,
4316 const char * inFullName
,
4317 const char * inHostname
,
4320 const unsigned char * inTXTPtr
,
4324 char errorStr
[ 64 ];
4328 Unused( inContext
);
4330 gettimeofday( &now
, NULL
);
4332 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " error %#m", inError
);
4334 FPrintF( stdout
, "%{du:time}: %s can be reached at %s:%u (interface %d)%?s\n",
4335 &now
, inFullName
, inHostname
, ntohs( inPort
), (int32_t) inInterfaceIndex
, inError
, errorStr
);
4338 FPrintF( stdout
, " TXT record: %#H\n", inTXTPtr
, (int) inTXTLen
, INT_MAX
);
4342 FPrintF( stdout
, " TXT record: %#{txt}\n", inTXTPtr
, (size_t) inTXTLen
);
4346 //===========================================================================================================================
4347 // GetAddrInfoPOSIXCmd
4348 //===========================================================================================================================
4350 #define AddressFamilyStr( X ) ( \
4351 ( (X) == AF_INET ) ? "inet" : \
4352 ( (X) == AF_INET6 ) ? "inet6" : \
4353 ( (X) == AF_UNSPEC ) ? "unspec" : \
4363 #define CaseFlagStringify( X ) { (X), # X }
4365 const FlagStringPair kGAIPOSIXFlagStringPairs
[] =
4367 #if( defined( AI_UNUSABLE ) )
4368 CaseFlagStringify( AI_UNUSABLE
),
4370 CaseFlagStringify( AI_NUMERICSERV
),
4371 CaseFlagStringify( AI_V4MAPPED
),
4372 CaseFlagStringify( AI_ADDRCONFIG
),
4373 #if( defined( AI_V4MAPPED_CFG ) )
4374 CaseFlagStringify( AI_V4MAPPED_CFG
),
4376 CaseFlagStringify( AI_ALL
),
4377 CaseFlagStringify( AI_NUMERICHOST
),
4378 CaseFlagStringify( AI_CANONNAME
),
4379 CaseFlagStringify( AI_PASSIVE
),
4383 static void GetAddrInfoPOSIXCmd( void )
4386 struct addrinfo hints
;
4388 const struct addrinfo
* addrInfo
;
4389 struct addrinfo
* addrInfoList
= NULL
;
4390 const FlagStringPair
* pair
;
4392 memset( &hints
, 0, sizeof( hints
) );
4393 hints
.ai_socktype
= SOCK_STREAM
;
4395 // Set hints address family.
4397 if( !gGAIPOSIX_Family
) hints
.ai_family
= AF_UNSPEC
;
4398 else if( strcasecmp( gGAIPOSIX_Family
, "inet" ) == 0 ) hints
.ai_family
= AF_INET
;
4399 else if( strcasecmp( gGAIPOSIX_Family
, "inet6" ) == 0 ) hints
.ai_family
= AF_INET6
;
4400 else if( strcasecmp( gGAIPOSIX_Family
, "unspec" ) == 0 ) hints
.ai_family
= AF_UNSPEC
;
4403 FPrintF( stderr
, "Invalid address family: %s.\n", gGAIPOSIX_Family
);
4410 if( gGAIPOSIXFlag_AddrConfig
) hints
.ai_flags
|= AI_ADDRCONFIG
;
4411 if( gGAIPOSIXFlag_All
) hints
.ai_flags
|= AI_ALL
;
4412 if( gGAIPOSIXFlag_CanonName
) hints
.ai_flags
|= AI_CANONNAME
;
4413 if( gGAIPOSIXFlag_NumericHost
) hints
.ai_flags
|= AI_NUMERICHOST
;
4414 if( gGAIPOSIXFlag_NumericServ
) hints
.ai_flags
|= AI_NUMERICSERV
;
4415 if( gGAIPOSIXFlag_Passive
) hints
.ai_flags
|= AI_PASSIVE
;
4416 if( gGAIPOSIXFlag_V4Mapped
) hints
.ai_flags
|= AI_V4MAPPED
;
4417 #if( defined( AI_V4MAPPED_CFG ) )
4418 if( gGAIPOSIXFlag_V4MappedCFG
) hints
.ai_flags
|= AI_V4MAPPED_CFG
;
4420 #if( defined( AI_DEFAULT ) )
4421 if( gGAIPOSIXFlag_Default
) hints
.ai_flags
|= AI_DEFAULT
;
4423 #if( defined( AI_UNUSABLE ) )
4424 if( gGAIPOSIXFlag_Unusable
) hints
.ai_flags
|= AI_UNUSABLE
;
4429 FPrintF( stdout
, "Hostname: %s\n", gGAIPOSIX_HostName
);
4430 FPrintF( stdout
, "Servname: %s\n", gGAIPOSIX_ServName
);
4431 FPrintF( stdout
, "Address family: %s\n", AddressFamilyStr( hints
.ai_family
) );
4432 FPrintF( stdout
, "Flags: 0x%X < ", hints
.ai_flags
);
4433 for( pair
= kGAIPOSIXFlagStringPairs
; pair
->str
!= NULL
; ++pair
)
4435 if( ( (unsigned int) hints
.ai_flags
) & pair
->flag
) FPrintF( stdout
, "%s ", pair
->str
);
4437 FPrintF( stdout
, ">\n" );
4438 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4439 FPrintF( stdout
, "---\n" );
4441 // Call getaddrinfo().
4443 err
= getaddrinfo( gGAIPOSIX_HostName
, gGAIPOSIX_ServName
, &hints
, &addrInfoList
);
4444 gettimeofday( &now
, NULL
);
4447 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
4453 for( addrInfo
= addrInfoList
; addrInfo
; addrInfo
= addrInfo
->ai_next
) { ++addrCount
; }
4455 FPrintF( stdout
, "Addresses (%d total):\n", addrCount
);
4456 for( addrInfo
= addrInfoList
; addrInfo
; addrInfo
= addrInfo
->ai_next
)
4458 FPrintF( stdout
, "%##a\n", addrInfo
->ai_addr
);
4461 FPrintF( stdout
, "---\n" );
4462 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
4465 if( addrInfoList
) freeaddrinfo( addrInfoList
);
4466 if( err
) exit( 1 );
4469 //===========================================================================================================================
4471 //===========================================================================================================================
4473 #define kIP6ARPADomainStr "ip6.arpa."
4475 static void ReverseLookupCmd( void )
4478 QueryRecordContext
* context
= NULL
;
4479 DNSServiceRef sdRef
;
4480 dispatch_source_t signalSource
= NULL
;
4482 uint8_t ipv6Addr
[ 16 ];
4483 char recordName
[ ( 16 * 4 ) + sizeof( kIP6ARPADomainStr
) ];
4484 int useMainConnection
;
4485 const char * endPtr
;
4487 // Set up SIGINT handler.
4489 signal( SIGINT
, SIG_IGN
);
4490 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
4491 require_noerr( err
, exit
);
4492 dispatch_resume( signalSource
);
4496 context
= (QueryRecordContext
*) calloc( 1, sizeof( *context
) );
4497 require_action( context
, exit
, err
= kNoMemoryErr
);
4499 // Check command parameters.
4501 if( gReverseLookup_TimeLimitSecs
< 0 )
4503 FPrintF( stderr
, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs
);
4508 // Create main connection.
4510 if( gConnectionOpt
)
4512 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
4513 require_noerr_quiet( err
, exit
);
4514 useMainConnection
= true;
4518 useMainConnection
= false;
4523 context
->flags
= GetDNSSDFlagsFromOpts();
4524 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
4526 // Get interface index.
4528 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
4529 require_noerr_quiet( err
, exit
);
4531 // Create reverse lookup record name.
4533 err
= _StringToIPv4Address( gReverseLookup_IPAddr
, kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
,
4534 &ipv4Addr
, NULL
, NULL
, NULL
, &endPtr
);
4535 if( err
|| ( *endPtr
!= '\0' ) )
4540 err
= _StringToIPv6Address( gReverseLookup_IPAddr
,
4541 kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
| kStringToIPAddressFlagsNoScope
,
4542 ipv6Addr
, NULL
, NULL
, NULL
, &endPtr
);
4543 if( err
|| ( *endPtr
!= '\0' ) )
4545 FPrintF( stderr
, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr
);
4550 for( i
= 15; i
>= 0; --i
)
4552 *dst
++ = kHexDigitsLowercase
[ ipv6Addr
[ i
] & 0x0F ];
4554 *dst
++ = kHexDigitsLowercase
[ ipv6Addr
[ i
] >> 4 ];
4557 strcpy_literal( dst
, kIP6ARPADomainStr
);
4558 check( ( strlen( recordName
) + 1 ) <= sizeof( recordName
) );
4562 SNPrintF( recordName
, sizeof( recordName
), "%u.%u.%u.%u.in-addr.arpa.",
4564 ( ipv4Addr
>> 8 ) & 0xFF,
4565 ( ipv4Addr
>> 16 ) & 0xFF,
4566 ( ipv4Addr
>> 24 ) & 0xFF );
4569 // Set remaining parameters.
4571 context
->recordName
= recordName
;
4572 context
->recordType
= kDNSServiceType_PTR
;
4573 context
->timeLimitSecs
= gReverseLookup_TimeLimitSecs
;
4574 context
->oneShotMode
= gReverseLookup_OneShot
? true : false;
4578 QueryRecordPrintPrologue( context
);
4582 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
4583 err
= DNSServiceQueryRecord( &sdRef
, context
->flags
, context
->ifIndex
, context
->recordName
, context
->recordType
,
4584 kDNSServiceClass_IN
, QueryRecordCallback
, context
);
4585 require_noerr( err
, exit
);
4587 context
->opRef
= sdRef
;
4588 if( !useMainConnection
)
4590 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
4591 require_noerr( err
, exit
);
4596 if( context
->timeLimitSecs
> 0 )
4598 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
4599 kExitReason_TimeLimit
, Exit
);
4604 dispatch_source_forget( &signalSource
);
4605 if( context
) QueryRecordContextFree( context
);
4606 if( err
) exit( 1 );
4609 //===========================================================================================================================
4611 //===========================================================================================================================
4615 DNSServiceRef mainRef
; // Main sdRef for shared connection.
4616 DNSServiceRef opRef
; // sdRef for the DNSServiceNATPortMappingCreate operation.
4617 DNSServiceFlags flags
; // Flags for DNSServiceNATPortMappingCreate operation.
4618 uint32_t ifIndex
; // Interface index argument for DNSServiceNATPortMappingCreate operation.
4619 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceNATPortMappingCreate operation.
4620 uint32_t ttl
; // TTL argument for DNSServiceNATPortMappingCreate operation.
4621 uint16_t internalPort
; // Internal port argument for DNSServiceNATPortMappingCreate operation.
4622 uint16_t externalPort
; // External port argument for DNSServiceNATPortMappingCreate operation.
4623 Boolean printedHeader
; // True if results header was printed.
4625 } PortMappingContext
;
4627 static void PortMappingPrintPrologue( const PortMappingContext
*inContext
);
4628 static void PortMappingContextFree( PortMappingContext
*inContext
);
4629 static void DNSSD_API
4630 PortMappingCallback(
4631 DNSServiceRef inSDRef
,
4632 DNSServiceFlags inFlags
,
4633 uint32_t inInterfaceIndex
,
4634 DNSServiceErrorType inError
,
4635 uint32_t inExternalIPv4Address
,
4636 DNSServiceProtocol inProtocol
,
4637 uint16_t inInternalPort
,
4638 uint16_t inExternalPort
,
4642 static void PortMappingCmd( void )
4645 PortMappingContext
* context
= NULL
;
4646 DNSServiceRef sdRef
;
4647 dispatch_source_t signalSource
= NULL
;
4648 int useMainConnection
;
4650 // Set up SIGINT handler.
4652 signal( SIGINT
, SIG_IGN
);
4653 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
4654 require_noerr( err
, exit
);
4655 dispatch_resume( signalSource
);
4659 context
= (PortMappingContext
*) calloc( 1, sizeof( *context
) );
4660 require_action( context
, exit
, err
= kNoMemoryErr
);
4662 // Check command parameters.
4664 if( ( gPortMapping_InternalPort
< 0 ) || ( gPortMapping_InternalPort
> UINT16_MAX
) )
4666 FPrintF( stderr
, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort
);
4671 if( ( gPortMapping_ExternalPort
< 0 ) || ( gPortMapping_ExternalPort
> UINT16_MAX
) )
4673 FPrintF( stderr
, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort
);
4678 // Create main connection.
4680 if( gConnectionOpt
)
4682 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
4683 require_noerr_quiet( err
, exit
);
4684 useMainConnection
= true;
4688 useMainConnection
= false;
4693 context
->flags
= GetDNSSDFlagsFromOpts();
4694 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
4696 // Get interface index.
4698 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
4699 require_noerr_quiet( err
, exit
);
4701 // Set remaining parameters.
4703 if( gPortMapping_ProtocolTCP
) context
->protocols
|= kDNSServiceProtocol_TCP
;
4704 if( gPortMapping_ProtocolUDP
) context
->protocols
|= kDNSServiceProtocol_UDP
;
4705 context
->ttl
= (uint32_t) gPortMapping_TTL
;
4706 context
->internalPort
= (uint16_t) gPortMapping_InternalPort
;
4707 context
->externalPort
= (uint16_t) gPortMapping_ExternalPort
;
4711 PortMappingPrintPrologue( context
);
4715 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
4716 err
= DNSServiceNATPortMappingCreate( &sdRef
, context
->flags
, context
->ifIndex
, context
->protocols
,
4717 htons( context
->internalPort
), htons( context
->externalPort
), context
->ttl
, PortMappingCallback
, context
);
4718 require_noerr( err
, exit
);
4720 context
->opRef
= sdRef
;
4721 if( !useMainConnection
)
4723 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
4724 require_noerr( err
, exit
);
4730 dispatch_source_forget( &signalSource
);
4731 if( context
) PortMappingContextFree( context
);
4732 if( err
) exit( 1 );
4735 //===========================================================================================================================
4736 // PortMappingPrintPrologue
4737 //===========================================================================================================================
4739 static void PortMappingPrintPrologue( const PortMappingContext
*inContext
)
4741 char ifName
[ kInterfaceNameBufLen
];
4743 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
4745 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
4746 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
4747 FPrintF( stdout
, "Protocols: %#{flags}\n", inContext
->protocols
, kDNSServiceProtocolDescriptors
);
4748 FPrintF( stdout
, "Internal Port: %u\n", inContext
->internalPort
);
4749 FPrintF( stdout
, "External Port: %u\n", inContext
->externalPort
);
4750 FPrintF( stdout
, "TTL: %u%?s\n", inContext
->ttl
, !inContext
->ttl
,
4751 " (system will use a default value.)" );
4752 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4753 FPrintF( stdout
, "---\n" );
4757 //===========================================================================================================================
4758 // PortMappingContextFree
4759 //===========================================================================================================================
4761 static void PortMappingContextFree( PortMappingContext
*inContext
)
4763 DNSServiceForget( &inContext
->opRef
);
4764 DNSServiceForget( &inContext
->mainRef
);
4768 //===========================================================================================================================
4769 // PortMappingCallback
4770 //===========================================================================================================================
4772 static void DNSSD_API
4773 PortMappingCallback(
4774 DNSServiceRef inSDRef
,
4775 DNSServiceFlags inFlags
,
4776 uint32_t inInterfaceIndex
,
4777 DNSServiceErrorType inError
,
4778 uint32_t inExternalIPv4Address
,
4779 DNSServiceProtocol inProtocol
,
4780 uint16_t inInternalPort
,
4781 uint16_t inExternalPort
,
4785 PortMappingContext
* const context
= (PortMappingContext
*) inContext
;
4787 char errorStr
[ 128 ];
4792 gettimeofday( &now
, NULL
);
4794 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " (error: %#m)", inError
);
4795 if( !context
->printedHeader
)
4797 FPrintF( stdout
, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" );
4798 context
->printedHeader
= true;
4800 FPrintF( stdout
, "%{du:time} %2u %7u %15.4a %7u %6u %#{flags}%?s\n",
4801 &now
, inInterfaceIndex
, ntohs( inInternalPort
), &inExternalIPv4Address
, ntohs( inExternalPort
), inTTL
,
4802 inProtocol
, kDNSServiceProtocolDescriptors
, inError
, errorStr
);
4805 //===========================================================================================================================
4807 //===========================================================================================================================
4809 typedef struct BrowseAllConnection BrowseAllConnection
;
4813 ServiceBrowserRef browser
; // Service browser.
4814 ServiceBrowserResults
* results
; // Results from the service browser.
4815 BrowseAllConnection
* connectionList
; // List of connections.
4816 dispatch_source_t connectionTimer
; // Timer for connection timeout.
4817 int connectionPendingCount
; // Number of pending connections.
4818 int connectionTimeoutSecs
; // Timeout value for connections in seconds.
4822 struct BrowseAllConnection
4824 BrowseAllConnection
* next
; // Next connection object in list.
4825 sockaddr_ip sip
; // IPv4 or IPv6 address to connect to.
4826 uint16_t port
; // TCP port to connect to.
4827 AsyncConnectionRef asyncCnx
; // AsyncConnection object to handle the actual connection.
4828 OSStatus status
; // Status of connection. NoErr means connection succeeded.
4829 CFTimeInterval connectTimeSecs
; // Time it took to connect in seconds.
4830 int32_t refCount
; // This object's reference count.
4831 BrowseAllContext
* context
; // Back pointer to parent context.
4834 static void _BrowseAllContextFree( BrowseAllContext
*inContext
);
4835 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
);
4837 _BrowseAllConnectionCreate(
4838 const struct sockaddr
* inSockAddr
,
4840 BrowseAllContext
* inContext
,
4841 BrowseAllConnection
** outConnection
);
4842 static void _BrowseAllConnectionRetain( BrowseAllConnection
*inConnection
);
4843 static void _BrowseAllConnectionRelease( BrowseAllConnection
*inConnection
);
4844 static void _BrowseAllConnectionProgress( int inPhase
, const void *inDetails
, void *inArg
);
4845 static void _BrowseAllConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
);
4846 static void _BrowseAllExit( void *inContext
);
4848 static Boolean
_IsServiceTypeTCP( const char *inServiceType
);
4850 static void BrowseAllCmd( void )
4853 BrowseAllContext
* context
= NULL
;
4856 char ifName
[ kInterfaceNameBufLen
];
4858 // Check parameters.
4860 if( gBrowseAll_BrowseTimeSecs
<= 0 )
4862 FPrintF( stdout
, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs
);
4867 context
= (BrowseAllContext
*) calloc( 1, sizeof( *context
) );
4868 require_action( context
, exit
, err
= kNoMemoryErr
);
4870 context
->connectionTimeoutSecs
= gBrowseAll_ConnectTimeout
;
4871 #if( TARGET_OS_POSIX )
4872 // Increase the open file descriptor limit for connection sockets.
4874 if( context
->connectionTimeoutSecs
> 0 )
4876 struct rlimit fdLimits
;
4878 err
= getrlimit( RLIMIT_NOFILE
, &fdLimits
);
4879 err
= map_global_noerr_errno( err
);
4880 require_noerr( err
, exit
);
4882 if( fdLimits
.rlim_cur
< 4096 )
4884 fdLimits
.rlim_cur
= 4096;
4885 err
= setrlimit( RLIMIT_NOFILE
, &fdLimits
);
4886 err
= map_global_noerr_errno( err
);
4887 require_noerr( err
, exit
);
4892 // Get interface index.
4894 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
4895 require_noerr_quiet( err
, exit
);
4899 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
4900 FPrintF( stdout
, "Service types: ");
4901 if( gBrowseAll_ServiceTypesCount
> 0 )
4903 FPrintF( stdout
, "%s", gBrowseAll_ServiceTypes
[ 0 ] );
4904 for( i
= 1; i
< gBrowseAll_ServiceTypesCount
; ++i
)
4906 FPrintF( stdout
, ", %s", gBrowseAll_ServiceTypes
[ i
] );
4908 FPrintF( stdout
, "\n" );
4912 FPrintF( stdout
, "all services\n" );
4914 FPrintF( stdout
, "Domain: %s\n", gBrowseAll_Domain
? gBrowseAll_Domain
: "default domains" );
4915 FPrintF( stdout
, "Browse time: %d second%?c\n", gBrowseAll_BrowseTimeSecs
, gBrowseAll_BrowseTimeSecs
!= 1, 's' );
4916 FPrintF( stdout
, "Connect timeout: %d second%?c\n",
4917 context
->connectionTimeoutSecs
, context
->connectionTimeoutSecs
!= 1, 's' );
4918 FPrintF( stdout
, "IncludeAWDL: %s\n", gDNSSDFlag_IncludeAWDL
? "yes" : "no" );
4919 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4920 FPrintF( stdout
, "---\n" );
4922 err
= ServiceBrowserCreate( dispatch_get_main_queue(), ifIndex
, gBrowseAll_Domain
,
4923 (unsigned int) gBrowseAll_BrowseTimeSecs
, gDNSSDFlag_IncludeAWDL
? true : false, &context
->browser
);
4924 require_noerr( err
, exit
);
4926 for( i
= 0; i
< gBrowseAll_ServiceTypesCount
; ++i
)
4928 err
= ServiceBrowserAddServiceType( context
->browser
, gBrowseAll_ServiceTypes
[ i
] );
4929 require_noerr( err
, exit
);
4931 ServiceBrowserSetCallback( context
->browser
, _BrowseAllServiceBrowserCallback
, context
);
4932 ServiceBrowserStart( context
->browser
);
4936 if( context
) _BrowseAllContextFree( context
);
4937 if( err
) exit( 1 );
4940 //===========================================================================================================================
4941 // _BrowseAllContextFree
4942 //===========================================================================================================================
4944 static void _BrowseAllContextFree( BrowseAllContext
*inContext
)
4946 check( !inContext
->browser
);
4947 check( !inContext
->connectionTimer
);
4948 check( !inContext
->connectionList
);
4949 ForgetServiceBrowserResults( &inContext
->results
);
4953 //===========================================================================================================================
4954 // _BrowseAllServiceBrowserCallback
4955 //===========================================================================================================================
4957 #define kDiscardProtocolPort 9
4959 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
)
4962 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
4964 SBRServiceType
* type
;
4965 SBRServiceInstance
* instance
;
4966 SBRIPAddress
* ipaddr
;
4970 require_action( inResults
, exit
, err
= kUnexpectedErr
);
4972 check( !context
->results
);
4973 context
->results
= inResults
;
4974 ServiceBrowserResultsRetain( context
->results
);
4976 check( context
->connectionPendingCount
== 0 );
4977 if( context
->connectionTimeoutSecs
> 0 )
4979 BrowseAllConnection
* connection
;
4980 BrowseAllConnection
** connectionPtr
= &context
->connectionList
;
4981 char destination
[ kSockAddrStringMaxSize
];
4983 for( domain
= context
->results
->domainList
; domain
; domain
= domain
->next
)
4985 for( type
= domain
->typeList
; type
; type
= type
->next
)
4987 if( !_IsServiceTypeTCP( type
->name
) ) continue;
4988 for( instance
= type
->instanceList
; instance
; instance
= instance
->next
)
4990 if( instance
->port
== kDiscardProtocolPort
) continue;
4991 for( ipaddr
= instance
->ipaddrList
; ipaddr
; ipaddr
= ipaddr
->next
)
4993 err
= _BrowseAllConnectionCreate( &ipaddr
->sip
.sa
, instance
->port
, context
, &connection
);
4994 require_noerr( err
, exit
);
4996 *connectionPtr
= connection
;
4997 connectionPtr
= &connection
->next
;
4999 err
= SockAddrToString( &ipaddr
->sip
, kSockAddrStringFlagsNoPort
, destination
);
5003 err
= AsyncConnection_Connect( &connection
->asyncCnx
, destination
, -instance
->port
,
5004 kAsyncConnectionFlag_P2P
, kAsyncConnectionNoTimeout
,
5005 kSocketBufferSize_DontSet
, kSocketBufferSize_DontSet
,
5006 _BrowseAllConnectionProgress
, connection
, _BrowseAllConnectionHandler
, connection
,
5007 dispatch_get_main_queue() );
5012 _BrowseAllConnectionRetain( connection
);
5013 connection
->status
= kInProgressErr
;
5014 ++context
->connectionPendingCount
;
5018 connection
->status
= err
;
5026 if( context
->connectionPendingCount
> 0 )
5028 check( !context
->connectionTimer
);
5029 err
= DispatchTimerCreate( dispatch_time_seconds( context
->connectionTimeoutSecs
), DISPATCH_TIME_FOREVER
,
5030 100 * kNanosecondsPerMillisecond
, NULL
, _BrowseAllExit
, NULL
, context
, &context
->connectionTimer
);
5031 require_noerr( err
, exit
);
5032 dispatch_resume( context
->connectionTimer
);
5036 dispatch_async_f( dispatch_get_main_queue(), context
, _BrowseAllExit
);
5041 ForgetCF( &context
->browser
);
5042 if( err
) exit( 1 );
5045 //===========================================================================================================================
5046 // _BrowseAllConnectionCreate
5047 //===========================================================================================================================
5050 _BrowseAllConnectionCreate(
5051 const struct sockaddr
* inSockAddr
,
5053 BrowseAllContext
* inContext
,
5054 BrowseAllConnection
** outConnection
)
5057 BrowseAllConnection
* obj
;
5059 obj
= (BrowseAllConnection
*) calloc( 1, sizeof( *obj
) );
5060 require_action( obj
, exit
, err
= kNoMemoryErr
);
5063 SockAddrCopy( inSockAddr
, &obj
->sip
);
5065 obj
->context
= inContext
;
5067 *outConnection
= obj
;
5074 //===========================================================================================================================
5075 // _BrowseAllConnectionRetain
5076 //===========================================================================================================================
5078 static void _BrowseAllConnectionRetain( BrowseAllConnection
*inConnection
)
5080 ++inConnection
->refCount
;
5083 //===========================================================================================================================
5084 // _BrowseAllConnectionRelease
5085 //===========================================================================================================================
5087 static void _BrowseAllConnectionRelease( BrowseAllConnection
*inConnection
)
5089 if( --inConnection
->refCount
== 0 ) free( inConnection
);
5092 //===========================================================================================================================
5093 // _BrowseAllConnectionProgress
5094 //===========================================================================================================================
5096 static void _BrowseAllConnectionProgress( int inPhase
, const void *inDetails
, void *inArg
)
5098 BrowseAllConnection
* const connection
= (BrowseAllConnection
*) inArg
;
5100 if( inPhase
== kAsyncConnectionPhase_Connected
)
5102 const AsyncConnectedInfo
* const info
= (AsyncConnectedInfo
*) inDetails
;
5104 connection
->connectTimeSecs
= info
->connectSecs
;
5108 //===========================================================================================================================
5109 // _BrowseAllConnectionHandler
5110 //===========================================================================================================================
5112 static void _BrowseAllConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
)
5114 BrowseAllConnection
* const connection
= (BrowseAllConnection
*) inArg
;
5115 BrowseAllContext
* const context
= connection
->context
;
5117 connection
->status
= inError
;
5118 ForgetSocket( &inSock
);
5121 check( context
->connectionPendingCount
> 0 );
5122 if( ( --context
->connectionPendingCount
== 0 ) && context
->connectionTimer
)
5124 dispatch_source_forget( &context
->connectionTimer
);
5125 dispatch_async_f( dispatch_get_main_queue(), context
, _BrowseAllExit
);
5128 _BrowseAllConnectionRelease( connection
);
5131 //===========================================================================================================================
5133 //===========================================================================================================================
5135 #define Indent( X ) ( (X) * 4 ), ""
5137 static void _BrowseAllExit( void *inContext
)
5139 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
5141 SBRServiceType
* type
;
5142 SBRServiceInstance
* instance
;
5143 SBRIPAddress
* ipaddr
;
5144 char textBuf
[ 512 ];
5145 #if( TARGET_OS_POSIX )
5146 const Boolean useColor
= isatty( STDOUT_FILENO
) ? true : false;
5149 dispatch_source_forget( &context
->connectionTimer
);
5151 for( domain
= context
->results
->domainList
; domain
; domain
= domain
->next
)
5153 FPrintF( stdout
, "%s\n\n", domain
->name
);
5155 for( type
= domain
->typeList
; type
; type
= type
->next
)
5157 const char * description
;
5158 const Boolean serviceTypeIsTCP
= _IsServiceTypeTCP( type
->name
);
5160 description
= ServiceTypeDescription( type
->name
);
5161 if( description
) FPrintF( stdout
, "%*s" "%s (%s)\n\n", Indent( 1 ), description
, type
->name
);
5162 else FPrintF( stdout
, "%*s" "%s\n\n", Indent( 1 ), type
->name
);
5164 for( instance
= type
->instanceList
; instance
; instance
= instance
->next
)
5166 char * dst
= textBuf
;
5167 char * const lim
= &textBuf
[ countof( textBuf
) ];
5168 char ifname
[ IF_NAMESIZE
+ 1 ];
5170 SNPrintF_Add( &dst
, lim
, "%s via ", instance
->name
);
5171 if( instance
->ifIndex
== 0 )
5173 SNPrintF_Add( &dst
, lim
, "the Internet" );
5175 else if( if_indextoname( instance
->ifIndex
, ifname
) )
5177 NetTransportType netType
;
5179 SocketGetInterfaceInfo( kInvalidSocketRef
, ifname
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &netType
);
5180 SNPrintF_Add( &dst
, lim
, "%s (%s)",
5181 ( netType
== kNetTransportType_Ethernet
) ? "Ethernet" : NetTransportTypeToString( netType
),
5186 SNPrintF_Add( &dst
, lim
, "interface index %u", instance
->ifIndex
);
5188 FPrintF( stdout
, "%*s" "%-55s %4llu.%03llu ms\n\n",
5189 Indent( 2 ), textBuf
, instance
->discoverTimeUs
/ 1000, instance
->discoverTimeUs
% 1000 );
5191 if( instance
->hostname
)
5193 SNPrintF( textBuf
, sizeof( textBuf
), "%s:%u", instance
->hostname
, instance
->port
);
5194 FPrintF( stdout
, "%*s" "%-51s %4llu.%03llu ms\n",
5195 Indent( 3 ), textBuf
, instance
->resolveTimeUs
/ 1000, instance
->resolveTimeUs
% 1000 );
5199 FPrintF( stdout
, "%*s" "%s:%u\n", Indent( 3 ), instance
->hostname
, instance
->port
);
5202 for( ipaddr
= instance
->ipaddrList
; ipaddr
; ipaddr
= ipaddr
->next
)
5204 BrowseAllConnection
* conn
;
5205 BrowseAllConnection
** connPtr
;
5207 FPrintF( stdout
, "%*s" "%-##47a %4llu.%03llu ms",
5208 Indent( 4 ), &ipaddr
->sip
.sa
, ipaddr
->resolveTimeUs
/ 1000, ipaddr
->resolveTimeUs
% 1000 );
5211 if( serviceTypeIsTCP
&& ( instance
->port
!= kDiscardProtocolPort
) )
5213 for( connPtr
= &context
->connectionList
; ( conn
= *connPtr
) != NULL
; connPtr
= &conn
->next
)
5215 if( ( conn
->port
== instance
->port
) &&
5216 ( SockAddrCompareAddr( &conn
->sip
, &ipaddr
->sip
) == 0 ) ) break;
5220 if( conn
->status
== kInProgressErr
) conn
->status
= kTimeoutErr
;
5221 *connPtr
= conn
->next
;
5222 conn
->context
= NULL
;
5223 AsyncConnection_Forget( &conn
->asyncCnx
);
5229 if( conn
->status
== kNoErr
)
5231 FPrintF( stdout
, " (%sconnected%s in %.3f ms)\n",
5232 useColor
? kANSIGreen
: "", useColor
? kANSINormal
: "", conn
->connectTimeSecs
* 1000 );
5236 FPrintF( stdout
, " (%scould not connect%s: %m)\n",
5237 useColor
? kANSIRed
: "", useColor
? kANSINormal
: "", conn
->status
);
5239 _BrowseAllConnectionRelease( conn
);
5243 FPrintF( stdout
, " (no connection attempted)\n" );
5247 FPrintF( stdout
, "\n" );
5248 if( instance
->txtLen
== 0 ) continue;
5250 FPrintF( stdout
, "%*s" "TXT record (%zu byte%?c):\n",
5251 Indent( 3 ), instance
->txtLen
, instance
->txtLen
!= 1, 's' );
5252 if( instance
->txtLen
> 1 )
5254 FPrintF( stdout
, "%3{txt}", instance
->txtPtr
, instance
->txtLen
);
5258 FPrintF( stdout
, "%*s" "%#H\n", Indent( 3 ), instance
->txtPtr
, (int) instance
->txtLen
, INT_MAX
);
5260 FPrintF( stdout
, "\n" );
5262 FPrintF( stdout
, "\n" );
5266 _BrowseAllContextFree( context
);
5270 //===========================================================================================================================
5271 // _IsServiceTypeTCP
5272 //===========================================================================================================================
5274 static Boolean
_IsServiceTypeTCP( const char *inServiceType
)
5277 const uint8_t * secondLabel
;
5278 uint8_t name
[ kDomainNameLengthMax
];
5280 err
= DomainNameFromString( name
, inServiceType
, NULL
);
5283 secondLabel
= DomainNameGetNextLabel( name
);
5284 if( secondLabel
&& DomainNameEqual( secondLabel
, (const uint8_t *) "\x04" "_tcp" ) ) return( true );
5289 //===========================================================================================================================
5291 //===========================================================================================================================
5293 const FlagStringPair kGetNameInfoFlagStringPairs
[] =
5295 CaseFlagStringify( NI_NUMERICSCOPE
),
5296 CaseFlagStringify( NI_DGRAM
),
5297 CaseFlagStringify( NI_NUMERICSERV
),
5298 CaseFlagStringify( NI_NAMEREQD
),
5299 CaseFlagStringify( NI_NUMERICHOST
),
5300 CaseFlagStringify( NI_NOFQDN
),
5304 static void GetNameInfoCmd( void )
5310 const FlagStringPair
* pair
;
5312 char host
[ NI_MAXHOST
];
5313 char serv
[ NI_MAXSERV
];
5315 err
= StringToSockAddr( gGetNameInfo_IPAddress
, &sip
, sizeof( sip
), &sockAddrLen
);
5319 FPrintF( stderr
, "Failed to convert \"%s\" to a sockaddr.\n", gGetNameInfo_IPAddress
);
5324 if( gGetNameInfoFlag_DGram
) flags
|= NI_DGRAM
;
5325 if( gGetNameInfoFlag_NameReqd
) flags
|= NI_NAMEREQD
;
5326 if( gGetNameInfoFlag_NoFQDN
) flags
|= NI_NOFQDN
;
5327 if( gGetNameInfoFlag_NumericHost
) flags
|= NI_NUMERICHOST
;
5328 if( gGetNameInfoFlag_NumericScope
) flags
|= NI_NUMERICSCOPE
;
5329 if( gGetNameInfoFlag_NumericServ
) flags
|= NI_NUMERICSERV
;
5333 FPrintF( stdout
, "SockAddr: %##a\n", &sip
.sa
);
5334 FPrintF( stdout
, "Flags: 0x%X < ", flags
);
5335 for( pair
= kGetNameInfoFlagStringPairs
; pair
->str
!= NULL
; ++pair
)
5337 if( flags
& pair
->flag
) FPrintF( stdout
, "%s ", pair
->str
);
5339 FPrintF( stdout
, ">\n" );
5340 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5341 FPrintF( stdout
, "---\n" );
5343 // Call getnameinfo().
5345 err
= getnameinfo( &sip
.sa
, (socklen_t
) sockAddrLen
, host
, (socklen_t
) sizeof( host
), serv
, (socklen_t
) sizeof( serv
),
5347 gettimeofday( &now
, NULL
);
5350 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
5354 FPrintF( stdout
, "host: %s\n", host
);
5355 FPrintF( stdout
, "serv: %s\n", serv
);
5357 FPrintF( stdout
, "---\n" );
5358 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
5361 gExitCode
= err
? 1 : 0;
5364 //===========================================================================================================================
5365 // GetAddrInfoStressCmd
5366 //===========================================================================================================================
5370 DNSServiceRef mainRef
;
5371 DNSServiceRef sdRef
;
5372 DNSServiceFlags flags
;
5373 unsigned int interfaceIndex
;
5374 unsigned int connectionNumber
;
5375 unsigned int requestCount
;
5376 unsigned int requestCountMax
;
5377 unsigned int requestCountLimit
;
5378 unsigned int durationMinMs
;
5379 unsigned int durationMaxMs
;
5383 static void GetAddrInfoStressEvent( void *inContext
);
5384 static void DNSSD_API
5385 GetAddrInfoStressCallback(
5386 DNSServiceRef inSDRef
,
5387 DNSServiceFlags inFlags
,
5388 uint32_t inInterfaceIndex
,
5389 DNSServiceErrorType inError
,
5390 const char * inHostname
,
5391 const struct sockaddr
* inSockAddr
,
5395 static void GetAddrInfoStressCmd( void )
5398 GAIStressContext
* context
= NULL
;
5400 DNSServiceFlags flags
;
5402 char ifName
[ kInterfaceNameBufLen
];
5404 if( gGAIStress_TestDurationSecs
< 0 )
5406 FPrintF( stdout
, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs
);
5410 if( gGAIStress_ConnectionCount
<= 0 )
5412 FPrintF( stdout
, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount
);
5416 if( gGAIStress_DurationMinMs
<= 0 )
5418 FPrintF( stdout
, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs
);
5422 if( gGAIStress_DurationMaxMs
<= 0 )
5424 FPrintF( stdout
, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs
);
5428 if( gGAIStress_DurationMinMs
> gGAIStress_DurationMaxMs
)
5430 FPrintF( stdout
, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
5431 gGAIStress_DurationMinMs
, gGAIStress_DurationMaxMs
);
5435 if( gGAIStress_RequestCountMax
<= 0 )
5437 FPrintF( stdout
, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax
);
5444 flags
= GetDNSSDFlagsFromOpts();
5446 // Set interface index.
5448 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
5449 require_noerr_quiet( err
, exit
);
5451 for( i
= 0; i
< gGAIStress_ConnectionCount
; ++i
)
5453 context
= (GAIStressContext
*) calloc( 1, sizeof( *context
) );
5454 require_action( context
, exit
, err
= kNoMemoryErr
);
5456 context
->flags
= flags
;
5457 context
->interfaceIndex
= ifIndex
;
5458 context
->connectionNumber
= (unsigned int)( i
+ 1 );
5459 context
->requestCountMax
= (unsigned int) gGAIStress_RequestCountMax
;
5460 context
->durationMinMs
= (unsigned int) gGAIStress_DurationMinMs
;
5461 context
->durationMaxMs
= (unsigned int) gGAIStress_DurationMaxMs
;
5463 dispatch_async_f( dispatch_get_main_queue(), context
, GetAddrInfoStressEvent
);
5467 if( gGAIStress_TestDurationSecs
> 0 )
5469 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs
), dispatch_get_main_queue(), NULL
, Exit
);
5472 FPrintF( stdout
, "Flags: %#{flags}\n", flags
, kDNSServiceFlagsDescriptors
);
5473 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
5474 FPrintF( stdout
, "Test duration: " );
5475 if( gGAIStress_TestDurationSecs
== 0 )
5477 FPrintF( stdout
, "∞\n" );
5481 FPrintF( stdout
, "%d s\n", gGAIStress_TestDurationSecs
);
5483 FPrintF( stdout
, "Connection count: %d\n", gGAIStress_ConnectionCount
);
5484 FPrintF( stdout
, "Request duration min: %d ms\n", gGAIStress_DurationMinMs
);
5485 FPrintF( stdout
, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs
);
5486 FPrintF( stdout
, "Request count max: %d\n", gGAIStress_RequestCountMax
);
5487 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5488 FPrintF( stdout
, "---\n" );
5493 FreeNullSafe( context
);
5494 if( err
) exit( 1 );
5497 //===========================================================================================================================
5498 // GetAddrInfoStressEvent
5499 //===========================================================================================================================
5501 #define kStressRandStrLen 5
5503 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
5505 static void GetAddrInfoStressEvent( void *inContext
)
5507 GAIStressContext
* const context
= (GAIStressContext
*) inContext
;
5509 DNSServiceRef sdRef
;
5510 unsigned int nextMs
;
5511 char randomStr
[ kStressRandStrLen
+ 1 ];
5512 char hostname
[ kStressRandStrLen
+ 4 + 1 ];
5513 Boolean isConnectionNew
= false;
5514 static Boolean printedHeader
= false;
5516 if( !context
->mainRef
|| ( context
->requestCount
>= context
->requestCountLimit
) )
5518 DNSServiceForget( &context
->mainRef
);
5519 context
->sdRef
= NULL
;
5520 context
->requestCount
= 0;
5521 context
->requestCountLimit
= RandomRange( 1, context
->requestCountMax
);
5523 err
= DNSServiceCreateConnection( &context
->mainRef
);
5524 require_noerr( err
, exit
);
5526 err
= DNSServiceSetDispatchQueue( context
->mainRef
, dispatch_get_main_queue() );
5527 require_noerr( err
, exit
);
5529 isConnectionNew
= true;
5532 RandomString( kLowercaseAlphaCharSet
, sizeof_string( kLowercaseAlphaCharSet
), 2, kStressRandStrLen
, randomStr
);
5533 SNPrintF( hostname
, sizeof( hostname
), "%s.com", randomStr
);
5535 nextMs
= RandomRange( context
->durationMinMs
, context
->durationMaxMs
);
5537 if( !printedHeader
)
5539 FPrintF( stdout
, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
5540 printedHeader
= true;
5542 FPrintF( stdout
, "%{du:time} %3u%c %9s %8u\n",
5543 NULL
, context
->connectionNumber
, isConnectionNew
? '*': ' ', hostname
, nextMs
);
5545 DNSServiceForget( &context
->sdRef
);
5546 sdRef
= context
->mainRef
;
5547 err
= DNSServiceGetAddrInfo( &sdRef
, context
->flags
| kDNSServiceFlagsShareConnection
, context
->interfaceIndex
,
5548 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, hostname
, GetAddrInfoStressCallback
, NULL
);
5549 require_noerr( err
, exit
);
5550 context
->sdRef
= sdRef
;
5552 context
->requestCount
++;
5554 dispatch_after_f( dispatch_time_milliseconds( nextMs
), dispatch_get_main_queue(), context
, GetAddrInfoStressEvent
);
5557 if( err
) exit( 1 );
5560 //===========================================================================================================================
5561 // GetAddrInfoStressCallback
5562 //===========================================================================================================================
5564 static void DNSSD_API
5565 GetAddrInfoStressCallback(
5566 DNSServiceRef inSDRef
,
5567 DNSServiceFlags inFlags
,
5568 uint32_t inInterfaceIndex
,
5569 DNSServiceErrorType inError
,
5570 const char * inHostname
,
5571 const struct sockaddr
* inSockAddr
,
5577 Unused( inInterfaceIndex
);
5579 Unused( inHostname
);
5580 Unused( inSockAddr
);
5582 Unused( inContext
);
5585 //===========================================================================================================================
5587 //===========================================================================================================================
5591 sockaddr_ip serverAddr
;
5597 dispatch_source_t readSource
;
5604 Boolean printRawRData
; // True if RDATA results are not to be formatted.
5605 uint8_t msgBuf
[ 512 ];
5609 static void DNSQueryPrintPrologue( const DNSQueryContext
*inContext
);
5610 static void DNSQueryReadHandler( void *inContext
);
5611 static void DNSQueryCancelHandler( void *inContext
);
5613 static void DNSQueryCmd( void )
5616 DNSQueryContext
* context
= NULL
;
5618 size_t msgLen
, sendLen
;
5620 // Check command parameters.
5622 if( gDNSQuery_TimeLimitSecs
< -1 )
5624 FPrintF( stdout
, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs
);
5628 if( ( gDNSQuery_Flags
< INT16_MIN
) || ( gDNSQuery_Flags
> UINT16_MAX
) )
5630 FPrintF( stdout
, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags
);
5637 context
= (DNSQueryContext
*) calloc( 1, sizeof( *context
) );
5638 require_action( context
, exit
, err
= kNoMemoryErr
);
5640 context
->name
= gDNSQuery_Name
;
5641 context
->sock
= kInvalidSocketRef
;
5642 context
->timeLimitSecs
= gDNSQuery_TimeLimitSecs
;
5643 context
->queryID
= (uint16_t) Random32();
5644 context
->useTCP
= gDNSQuery_UseTCP
? true : false;
5645 context
->printRawRData
= gDNSQuery_RawRData
? true : false;
5647 #if( TARGET_OS_DARWIN )
5648 if( gDNSQuery_Server
)
5651 err
= StringToSockAddr( gDNSQuery_Server
, &context
->serverAddr
, sizeof( context
->serverAddr
), NULL
);
5652 require_noerr( err
, exit
);
5654 #if( TARGET_OS_DARWIN )
5657 err
= GetDefaultDNSServer( &context
->serverAddr
);
5658 require_noerr( err
, exit
);
5661 if( SockAddrGetPort( &context
->serverAddr
) == 0 ) SockAddrSetPort( &context
->serverAddr
, kDNSPort
);
5663 err
= RecordTypeFromArgString( gDNSQuery_Type
, &context
->type
);
5664 require_noerr( err
, exit
);
5666 // Write query message.
5668 check_compile_time_code( sizeof( context
->msgBuf
) >= ( kDNSQueryMessageMaxLen
+ 2 ) );
5670 msgPtr
= context
->useTCP
? &context
->msgBuf
[ 2 ] : context
->msgBuf
;
5671 err
= WriteDNSQueryMessage( msgPtr
, context
->queryID
, (uint16_t) gDNSQuery_Flags
, context
->name
, context
->type
,
5672 kDNSServiceClass_IN
, &msgLen
);
5673 require_noerr( err
, exit
);
5674 check( msgLen
<= UINT16_MAX
);
5676 if( context
->useTCP
)
5678 WriteBig16( context
->msgBuf
, msgLen
);
5679 sendLen
= 2 + msgLen
;
5686 DNSQueryPrintPrologue( context
);
5688 if( gDNSQuery_Verbose
)
5690 FPrintF( stdout
, "DNS message to send:\n\n%{du:dnsmsg}", msgPtr
, msgLen
);
5691 FPrintF( stdout
, "---\n" );
5694 if( context
->useTCP
)
5696 // Create TCP socket.
5698 context
->sock
= socket( context
->serverAddr
.sa
.sa_family
, SOCK_STREAM
, IPPROTO_TCP
);
5699 err
= map_socket_creation_errno( context
->sock
);
5700 require_noerr( err
, exit
);
5702 err
= SocketConnect( context
->sock
, &context
->serverAddr
, 5 );
5703 require_noerr( err
, exit
);
5707 // Create UDP socket.
5709 err
= UDPClientSocketOpen( AF_UNSPEC
, &context
->serverAddr
, 0, -1, NULL
, &context
->sock
);
5710 require_noerr( err
, exit
);
5713 context
->sendTicks
= UpTicks();
5714 err
= _SocketWriteAll( context
->sock
, context
->msgBuf
, sendLen
, 5 );
5715 require_noerr( err
, exit
);
5717 if( context
->timeLimitSecs
== 0 ) goto exit
;
5719 err
= DispatchReadSourceCreate( context
->sock
, NULL
, DNSQueryReadHandler
, DNSQueryCancelHandler
, context
,
5720 &context
->readSource
);
5721 require_noerr( err
, exit
);
5722 dispatch_resume( context
->readSource
);
5724 if( context
->timeLimitSecs
> 0 )
5726 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
5734 dispatch_source_forget( &context
->readSource
);
5735 ForgetSocket( &context
->sock
);
5738 if( err
) exit( 1 );
5741 //===========================================================================================================================
5742 // DNSQueryPrintPrologue
5743 //===========================================================================================================================
5745 static void DNSQueryPrintPrologue( const DNSQueryContext
*inContext
)
5747 const int timeLimitSecs
= inContext
->timeLimitSecs
;
5749 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
5750 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->type
), inContext
->type
);
5751 FPrintF( stdout
, "Server: %##a\n", &inContext
->serverAddr
);
5752 FPrintF( stdout
, "Transport: %s\n", inContext
->useTCP
? "TCP" : "UDP" );
5753 FPrintF( stdout
, "Time limit: " );
5754 if( timeLimitSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
5755 else FPrintF( stdout
, "∞\n" );
5756 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5757 FPrintF( stdout
, "---\n" );
5760 //===========================================================================================================================
5761 // DNSQueryReadHandler
5762 //===========================================================================================================================
5764 static void DNSQueryReadHandler( void *inContext
)
5768 const uint64_t nowTicks
= UpTicks();
5769 DNSQueryContext
* const context
= (DNSQueryContext
*) inContext
;
5771 gettimeofday( &now
, NULL
);
5773 if( context
->useTCP
)
5775 if( !context
->haveTCPLen
)
5777 err
= SocketReadData( context
->sock
, &context
->msgBuf
, 2, &context
->msgOffset
);
5778 if( err
== EWOULDBLOCK
) { err
= kNoErr
; goto exit
; }
5779 require_noerr( err
, exit
);
5781 context
->msgOffset
= 0;
5782 context
->msgLen
= ReadBig16( context
->msgBuf
);
5783 context
->haveTCPLen
= true;
5784 if( context
->msgLen
<= sizeof( context
->msgBuf
) )
5786 context
->msgPtr
= context
->msgBuf
;
5790 context
->msgPtr
= (uint8_t *) malloc( context
->msgLen
);
5791 require_action( context
->msgPtr
, exit
, err
= kNoMemoryErr
);
5795 err
= SocketReadData( context
->sock
, context
->msgPtr
, context
->msgLen
, &context
->msgOffset
);
5796 if( err
== EWOULDBLOCK
) { err
= kNoErr
; goto exit
; }
5797 require_noerr( err
, exit
);
5798 context
->msgOffset
= 0;
5799 context
->haveTCPLen
= false;
5803 sockaddr_ip fromAddr
;
5805 context
->msgPtr
= context
->msgBuf
;
5806 err
= SocketRecvFrom( context
->sock
, context
->msgPtr
, sizeof( context
->msgBuf
), &context
->msgLen
, &fromAddr
,
5807 sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
5808 require_noerr( err
, exit
);
5810 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
5813 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
5814 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
5815 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
5816 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
5817 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, context
->msgPtr
, context
->msgLen
);
5819 if( ( context
->msgLen
>= kDNSHeaderLength
) && ( DNSHeaderGetID( (DNSHeader
*) context
->msgPtr
) == context
->queryID
) )
5821 Exit( kExitReason_ReceivedResponse
);
5825 if( err
) dispatch_source_forget( &context
->readSource
);
5828 //===========================================================================================================================
5829 // DNSQueryCancelHandler
5830 //===========================================================================================================================
5832 static void DNSQueryCancelHandler( void *inContext
)
5834 DNSQueryContext
* const context
= (DNSQueryContext
*) inContext
;
5836 check( !context
->readSource
);
5837 ForgetSocket( &context
->sock
);
5838 if( context
->msgPtr
!= context
->msgBuf
) ForgetMem( &context
->msgPtr
);
5840 dispatch_async_f( dispatch_get_main_queue(), NULL
, Exit
);
5843 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
5844 //===========================================================================================================================
5846 //===========================================================================================================================
5848 #define kDNSCryptPort 443
5850 #define kDNSCryptMinPadLength 8
5851 #define kDNSCryptMaxPadLength 256
5852 #define kDNSCryptBlockSize 64
5853 #define kDNSCryptCertMinimumLength 124
5854 #define kDNSCryptClientMagicLength 8
5855 #define kDNSCryptResolverMagicLength 8
5856 #define kDNSCryptHalfNonceLength 12
5857 #define kDNSCryptCertMagicLength 4
5859 check_compile_time( ( kDNSCryptHalfNonceLength
* 2 ) == crypto_box_NONCEBYTES
);
5861 static const uint8_t kDNSCryptCertMagic
[ kDNSCryptCertMagicLength
] = { 'D', 'N', 'S', 'C' };
5862 static const uint8_t kDNSCryptResolverMagic
[ kDNSCryptResolverMagicLength
] =
5864 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
5869 uint8_t certMagic
[ kDNSCryptCertMagicLength
];
5870 uint8_t esVersion
[ 2 ];
5871 uint8_t minorVersion
[ 2 ];
5872 uint8_t signature
[ crypto_sign_BYTES
];
5873 uint8_t publicKey
[ crypto_box_PUBLICKEYBYTES
];
5874 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5875 uint8_t serial
[ 4 ];
5876 uint8_t startTime
[ 4 ];
5877 uint8_t endTime
[ 4 ];
5878 uint8_t extensions
[ 1 ]; // Variably-sized extension data.
5882 check_compile_time( offsetof( DNSCryptCert
, extensions
) == kDNSCryptCertMinimumLength
);
5886 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5887 uint8_t clientPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5888 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5889 uint8_t poly1305MAC
[ 16 ];
5891 } DNSCryptQueryHeader
;
5893 check_compile_time( sizeof( DNSCryptQueryHeader
) == 68 );
5894 check_compile_time( sizeof( DNSCryptQueryHeader
) >= crypto_box_ZEROBYTES
);
5895 check_compile_time( ( sizeof( DNSCryptQueryHeader
) - crypto_box_ZEROBYTES
+ crypto_box_BOXZEROBYTES
) ==
5896 offsetof( DNSCryptQueryHeader
, poly1305MAC
) );
5900 uint8_t resolverMagic
[ kDNSCryptResolverMagicLength
];
5901 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5902 uint8_t resolverNonce
[ kDNSCryptHalfNonceLength
];
5903 uint8_t poly1305MAC
[ 16 ];
5905 } DNSCryptResponseHeader
;
5907 check_compile_time( sizeof( DNSCryptResponseHeader
) == 48 );
5908 check_compile_time( offsetof( DNSCryptResponseHeader
, poly1305MAC
) >= crypto_box_BOXZEROBYTES
);
5909 check_compile_time( ( offsetof( DNSCryptResponseHeader
, poly1305MAC
) - crypto_box_BOXZEROBYTES
+ crypto_box_ZEROBYTES
) ==
5910 sizeof( DNSCryptResponseHeader
) );
5914 sockaddr_ip serverAddr
;
5916 const char * providerName
;
5918 const uint8_t * certPtr
;
5920 dispatch_source_t readSource
;
5925 Boolean printRawRData
;
5926 uint8_t serverPublicSignKey
[ crypto_sign_PUBLICKEYBYTES
];
5927 uint8_t serverPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5928 uint8_t clientPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5929 uint8_t clientSecretKey
[ crypto_box_SECRETKEYBYTES
];
5930 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5931 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5932 uint8_t nmKey
[ crypto_box_BEFORENMBYTES
];
5933 uint8_t msgBuf
[ 512 ];
5937 static void DNSCryptReceiveCertHandler( void *inContext
);
5938 static void DNSCryptReceiveResponseHandler( void *inContext
);
5939 static void DNSCryptProceed( void *inContext
);
5940 static OSStatus
DNSCryptProcessCert( DNSCryptContext
*inContext
);
5941 static OSStatus
DNSCryptBuildQuery( DNSCryptContext
*inContext
);
5942 static OSStatus
DNSCryptSendQuery( DNSCryptContext
*inContext
);
5943 static void DNSCryptPrintCertificate( const DNSCryptCert
*inCert
, size_t inLen
);
5945 static void DNSCryptCmd( void )
5948 DNSCryptContext
* context
= NULL
;
5949 size_t writtenBytes
;
5951 SocketContext
* sockCtx
;
5952 SocketRef sock
= kInvalidSocketRef
;
5955 // Check command parameters.
5957 if( gDNSCrypt_TimeLimitSecs
< -1 )
5959 FPrintF( stdout
, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs
);
5966 context
= (DNSCryptContext
*) calloc( 1, sizeof( *context
) );
5967 require_action( context
, exit
, err
= kNoMemoryErr
);
5969 context
->providerName
= gDNSCrypt_ProviderName
;
5970 context
->qname
= gDNSCrypt_Name
;
5971 context
->timeLimitSecs
= gDNSCrypt_TimeLimitSecs
;
5972 context
->printRawRData
= gDNSCrypt_RawRData
? true : false;
5974 err
= crypto_box_keypair( context
->clientPublicKey
, context
->clientSecretKey
);
5975 require_noerr( err
, exit
);
5977 err
= HexToData( gDNSCrypt_ProviderKey
, kSizeCString
, kHexToData_DefaultFlags
,
5978 context
->serverPublicSignKey
, sizeof( context
->serverPublicSignKey
), &writtenBytes
, &totalBytes
, &ptr
);
5979 if( err
|| ( *ptr
!= '\0' ) )
5981 FPrintF( stderr
, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey
);
5984 else if( totalBytes
!= sizeof( context
->serverPublicSignKey
) )
5986 FPrintF( stderr
, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
5987 totalBytes
, sizeof( context
->serverPublicSignKey
) );
5991 check( writtenBytes
== totalBytes
);
5993 err
= StringToSockAddr( gDNSCrypt_Server
, &context
->serverAddr
, sizeof( context
->serverAddr
), NULL
);
5994 require_noerr( err
, exit
);
5995 if( SockAddrGetPort( &context
->serverAddr
) == 0 ) SockAddrSetPort( &context
->serverAddr
, kDNSCryptPort
);
5997 err
= RecordTypeFromArgString( gDNSCrypt_Type
, &context
->qtype
);
5998 require_noerr( err
, exit
);
6000 // Write query message.
6002 context
->queryID
= (uint16_t) Random32();
6003 err
= WriteDNSQueryMessage( context
->msgBuf
, context
->queryID
, kDNSHeaderFlag_RecursionDesired
, context
->providerName
,
6004 kDNSServiceType_TXT
, kDNSServiceClass_IN
, &context
->msgLen
);
6005 require_noerr( err
, exit
);
6007 // Create UDP socket.
6009 err
= UDPClientSocketOpen( AF_UNSPEC
, &context
->serverAddr
, 0, -1, NULL
, &sock
);
6010 require_noerr( err
, exit
);
6014 context
->sendTicks
= UpTicks();
6015 err
= _SocketWriteAll( sock
, context
->msgBuf
, context
->msgLen
, 5 );
6016 require_noerr( err
, exit
);
6018 err
= SocketContextCreate( sock
, context
, &sockCtx
);
6019 require_noerr( err
, exit
);
6020 sock
= kInvalidSocketRef
;
6022 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, DNSCryptReceiveCertHandler
, SocketContextCancelHandler
, sockCtx
,
6023 &context
->readSource
);
6024 if( err
) ForgetSocketContext( &sockCtx
);
6025 require_noerr( err
, exit
);
6027 dispatch_resume( context
->readSource
);
6029 if( context
->timeLimitSecs
> 0 )
6031 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
6037 if( context
) free( context
);
6038 ForgetSocket( &sock
);
6039 if( err
) exit( 1 );
6042 //===========================================================================================================================
6043 // DNSCryptReceiveCertHandler
6044 //===========================================================================================================================
6046 static void DNSCryptReceiveCertHandler( void *inContext
)
6050 const uint64_t nowTicks
= UpTicks();
6051 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6052 DNSCryptContext
* const context
= (DNSCryptContext
*) sockCtx
->userContext
;
6053 const DNSHeader
* hdr
;
6054 sockaddr_ip fromAddr
;
6055 const uint8_t * ptr
;
6056 const uint8_t * txtPtr
;
6058 unsigned int answerCount
, i
;
6059 uint8_t targetName
[ kDomainNameLengthMax
];
6061 gettimeofday( &now
, NULL
);
6063 dispatch_source_forget( &context
->readSource
);
6065 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &context
->msgLen
,
6066 &fromAddr
, sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6067 require_noerr( err
, exit
);
6068 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
6070 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6071 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
6072 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
6073 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
6074 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, context
->msgBuf
, context
->msgLen
);
6076 require_action_quiet( context
->msgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
6078 hdr
= (DNSHeader
*) context
->msgBuf
;
6079 require_action_quiet( DNSHeaderGetID( hdr
) == context
->queryID
, exit
, err
= kMismatchErr
);
6081 err
= DNSMessageGetAnswerSection( context
->msgBuf
, context
->msgLen
, &ptr
);
6082 require_noerr( err
, exit
);
6084 err
= DomainNameFromString( targetName
, context
->providerName
, NULL
);
6085 require_noerr( err
, exit
);
6087 answerCount
= DNSHeaderGetAnswerCount( hdr
);
6088 for( i
= 0; i
< answerCount
; ++i
)
6092 uint8_t name
[ kDomainNameLengthMax
];
6094 err
= DNSMessageExtractRecord( context
->msgBuf
, context
->msgLen
, ptr
, name
, &type
, &class, NULL
, &txtPtr
, &txtLen
,
6096 require_noerr( err
, exit
);
6098 if( ( type
== kDNSServiceType_TXT
) && ( class == kDNSServiceClass_IN
) && DomainNameEqual( name
, targetName
) )
6104 if( txtLen
< ( 1 + kDNSCryptCertMinimumLength
) )
6106 FPrintF( stderr
, "TXT record length is too short (%u < %u)\n", txtLen
, kDNSCryptCertMinimumLength
+ 1 );
6110 if( txtPtr
[ 0 ] < kDNSCryptCertMinimumLength
)
6112 FPrintF( stderr
, "TXT record value length is too short (%u < %u)\n", txtPtr
[ 0 ], kDNSCryptCertMinimumLength
);
6117 context
->certLen
= txtPtr
[ 0 ];
6118 context
->certPtr
= &txtPtr
[ 1 ];
6120 dispatch_async_f( dispatch_get_main_queue(), context
, DNSCryptProceed
);
6123 if( err
) Exit( NULL
);
6126 //===========================================================================================================================
6127 // DNSCryptReceiveResponseHandler
6128 //===========================================================================================================================
6130 static void DNSCryptReceiveResponseHandler( void *inContext
)
6134 const uint64_t nowTicks
= UpTicks();
6135 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6136 DNSCryptContext
* const context
= (DNSCryptContext
*) sockCtx
->userContext
;
6137 sockaddr_ip fromAddr
;
6138 DNSCryptResponseHeader
* hdr
;
6139 const uint8_t * end
;
6140 uint8_t * ciphertext
;
6141 uint8_t * plaintext
;
6142 const uint8_t * response
;
6143 uint8_t nonce
[ crypto_box_NONCEBYTES
];
6145 gettimeofday( &now
, NULL
);
6147 dispatch_source_forget( &context
->readSource
);
6149 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &context
->msgLen
,
6150 &fromAddr
, sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6151 require_noerr( err
, exit
);
6152 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
6154 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6155 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
6156 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
6157 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
6159 if( context
->msgLen
< sizeof( DNSCryptResponseHeader
) )
6161 FPrintF( stderr
, "DNSCrypt response is too short.\n" );
6166 hdr
= (DNSCryptResponseHeader
*) context
->msgBuf
;
6168 if( memcmp( hdr
->resolverMagic
, kDNSCryptResolverMagic
, kDNSCryptResolverMagicLength
) != 0 )
6170 FPrintF( stderr
, "DNSCrypt response resolver magic %#H != %#H\n",
6171 hdr
->resolverMagic
, kDNSCryptResolverMagicLength
, INT_MAX
,
6172 kDNSCryptResolverMagic
, kDNSCryptResolverMagicLength
, INT_MAX
);
6177 if( memcmp( hdr
->clientNonce
, context
->clientNonce
, kDNSCryptHalfNonceLength
) != 0 )
6179 FPrintF( stderr
, "DNSCrypt response client nonce mismatch.\n" );
6184 memcpy( nonce
, hdr
->clientNonce
, crypto_box_NONCEBYTES
);
6186 ciphertext
= hdr
->poly1305MAC
- crypto_box_BOXZEROBYTES
;
6187 memset( ciphertext
, 0, crypto_box_BOXZEROBYTES
);
6189 plaintext
= (uint8_t *)( hdr
+ 1 ) - crypto_box_ZEROBYTES
;
6190 check( plaintext
== ciphertext
);
6192 end
= context
->msgBuf
+ context
->msgLen
;
6194 err
= crypto_box_open_afternm( plaintext
, ciphertext
, (size_t)( end
- ciphertext
), nonce
, context
->nmKey
);
6195 require_noerr( err
, exit
);
6197 response
= plaintext
+ crypto_box_ZEROBYTES
;
6198 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, response
, (size_t)( end
- response
) );
6199 Exit( kExitReason_ReceivedResponse
);
6202 if( err
) Exit( NULL
);
6205 //===========================================================================================================================
6207 //===========================================================================================================================
6209 static void DNSCryptProceed( void *inContext
)
6212 DNSCryptContext
* const context
= (DNSCryptContext
*) inContext
;
6214 err
= DNSCryptProcessCert( context
);
6215 require_noerr_quiet( err
, exit
);
6217 err
= DNSCryptBuildQuery( context
);
6218 require_noerr_quiet( err
, exit
);
6220 err
= DNSCryptSendQuery( context
);
6221 require_noerr_quiet( err
, exit
);
6224 if( err
) Exit( NULL
);
6227 //===========================================================================================================================
6228 // DNSCryptProcessCert
6229 //===========================================================================================================================
6231 static OSStatus
DNSCryptProcessCert( DNSCryptContext
*inContext
)
6234 const DNSCryptCert
* const cert
= (DNSCryptCert
*) inContext
->certPtr
;
6235 const uint8_t * const certEnd
= inContext
->certPtr
+ inContext
->certLen
;
6237 time_t startTimeSecs
, endTimeSecs
;
6240 unsigned long long tempLen
;
6242 DNSCryptPrintCertificate( cert
, inContext
->certLen
);
6244 if( memcmp( cert
->certMagic
, kDNSCryptCertMagic
, kDNSCryptCertMagicLength
) != 0 )
6246 FPrintF( stderr
, "DNSCrypt certificate magic %#H != %#H\n",
6247 cert
->certMagic
, kDNSCryptCertMagicLength
, INT_MAX
,
6248 kDNSCryptCertMagic
, kDNSCryptCertMagicLength
, INT_MAX
);
6253 startTimeSecs
= (time_t) ReadBig32( cert
->startTime
);
6254 endTimeSecs
= (time_t) ReadBig32( cert
->endTime
);
6256 gettimeofday( &now
, NULL
);
6257 if( now
.tv_sec
< startTimeSecs
)
6259 FPrintF( stderr
, "DNSCrypt certificate start time is in the future.\n" );
6263 if( now
.tv_sec
>= endTimeSecs
)
6265 FPrintF( stderr
, "DNSCrypt certificate has expired.\n" );
6270 signedLen
= (size_t)( certEnd
- cert
->signature
);
6271 tempBuf
= (uint8_t *) malloc( signedLen
);
6272 require_action( tempBuf
, exit
, err
= kNoMemoryErr
);
6273 err
= crypto_sign_open( tempBuf
, &tempLen
, cert
->signature
, signedLen
, inContext
->serverPublicSignKey
);
6277 FPrintF( stderr
, "DNSCrypt certificate failed verification.\n" );
6278 err
= kAuthenticationErr
;
6282 memcpy( inContext
->serverPublicKey
, cert
->publicKey
, crypto_box_PUBLICKEYBYTES
);
6283 memcpy( inContext
->clientMagic
, cert
->clientMagic
, kDNSCryptClientMagicLength
);
6285 err
= crypto_box_beforenm( inContext
->nmKey
, inContext
->serverPublicKey
, inContext
->clientSecretKey
);
6286 require_noerr( err
, exit
);
6288 inContext
->certPtr
= NULL
;
6289 inContext
->certLen
= 0;
6290 inContext
->msgLen
= 0;
6296 //===========================================================================================================================
6297 // DNSCryptBuildQuery
6298 //===========================================================================================================================
6300 static OSStatus
DNSCryptPadQuery( uint8_t *inMsgPtr
, size_t inMsgLen
, size_t inMaxLen
, size_t *outPaddedLen
);
6302 static OSStatus
DNSCryptBuildQuery( DNSCryptContext
*inContext
)
6305 DNSCryptQueryHeader
* const hdr
= (DNSCryptQueryHeader
*) inContext
->msgBuf
;
6306 uint8_t * const queryPtr
= (uint8_t *)( hdr
+ 1 );
6308 size_t paddedQueryLen
;
6309 const uint8_t * const msgLimit
= inContext
->msgBuf
+ sizeof( inContext
->msgBuf
);
6310 const uint8_t * padLimit
;
6311 uint8_t nonce
[ crypto_box_NONCEBYTES
];
6313 check_compile_time_code( sizeof( inContext
->msgBuf
) >= ( sizeof( DNSCryptQueryHeader
) + kDNSQueryMessageMaxLen
) );
6315 inContext
->queryID
= (uint16_t) Random32();
6316 err
= WriteDNSQueryMessage( queryPtr
, inContext
->queryID
, kDNSHeaderFlag_RecursionDesired
, inContext
->qname
,
6317 inContext
->qtype
, kDNSServiceClass_IN
, &queryLen
);
6318 require_noerr( err
, exit
);
6320 padLimit
= &queryPtr
[ queryLen
+ kDNSCryptMaxPadLength
];
6321 if( padLimit
> msgLimit
) padLimit
= msgLimit
;
6323 err
= DNSCryptPadQuery( queryPtr
, queryLen
, (size_t)( padLimit
- queryPtr
), &paddedQueryLen
);
6324 require_noerr( err
, exit
);
6326 memset( queryPtr
- crypto_box_ZEROBYTES
, 0, crypto_box_ZEROBYTES
);
6327 RandomBytes( inContext
->clientNonce
, kDNSCryptHalfNonceLength
);
6328 memcpy( nonce
, inContext
->clientNonce
, kDNSCryptHalfNonceLength
);
6329 memset( &nonce
[ kDNSCryptHalfNonceLength
], 0, kDNSCryptHalfNonceLength
);
6331 err
= crypto_box_afternm( queryPtr
- crypto_box_ZEROBYTES
, queryPtr
- crypto_box_ZEROBYTES
,
6332 paddedQueryLen
+ crypto_box_ZEROBYTES
, nonce
, inContext
->nmKey
);
6333 require_noerr( err
, exit
);
6335 memcpy( hdr
->clientMagic
, inContext
->clientMagic
, kDNSCryptClientMagicLength
);
6336 memcpy( hdr
->clientPublicKey
, inContext
->clientPublicKey
, crypto_box_PUBLICKEYBYTES
);
6337 memcpy( hdr
->clientNonce
, nonce
, kDNSCryptHalfNonceLength
);
6339 inContext
->msgLen
= (size_t)( &queryPtr
[ paddedQueryLen
] - inContext
->msgBuf
);
6345 static OSStatus
DNSCryptPadQuery( uint8_t *inMsgPtr
, size_t inMsgLen
, size_t inMaxLen
, size_t *outPaddedLen
)
6350 require_action_quiet( ( inMsgLen
+ kDNSCryptMinPadLength
) <= inMaxLen
, exit
, err
= kSizeErr
);
6352 paddedLen
= inMsgLen
+ kDNSCryptMinPadLength
+
6353 arc4random_uniform( (uint32_t)( inMaxLen
- ( inMsgLen
+ kDNSCryptMinPadLength
) + 1 ) );
6354 paddedLen
+= ( kDNSCryptBlockSize
- ( paddedLen
% kDNSCryptBlockSize
) );
6355 if( paddedLen
> inMaxLen
) paddedLen
= inMaxLen
;
6357 inMsgPtr
[ inMsgLen
] = 0x80;
6358 memset( &inMsgPtr
[ inMsgLen
+ 1 ], 0, paddedLen
- ( inMsgLen
+ 1 ) );
6360 if( outPaddedLen
) *outPaddedLen
= paddedLen
;
6367 //===========================================================================================================================
6368 // DNSCryptSendQuery
6369 //===========================================================================================================================
6371 static OSStatus
DNSCryptSendQuery( DNSCryptContext
*inContext
)
6374 SocketContext
* sockCtx
;
6375 SocketRef sock
= kInvalidSocketRef
;
6377 check( inContext
->msgLen
> 0 );
6378 check( !inContext
->readSource
);
6380 err
= UDPClientSocketOpen( AF_UNSPEC
, &inContext
->serverAddr
, 0, -1, NULL
, &sock
);
6381 require_noerr( err
, exit
);
6383 inContext
->sendTicks
= UpTicks();
6384 err
= _SocketWriteAll( sock
, inContext
->msgBuf
, inContext
->msgLen
, 5 );
6385 require_noerr( err
, exit
);
6387 err
= SocketContextCreate( sock
, inContext
, &sockCtx
);
6388 require_noerr( err
, exit
);
6389 sock
= kInvalidSocketRef
;
6391 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, DNSCryptReceiveResponseHandler
, SocketContextCancelHandler
, sockCtx
,
6392 &inContext
->readSource
);
6393 if( err
) ForgetSocketContext( &sockCtx
);
6394 require_noerr( err
, exit
);
6396 dispatch_resume( inContext
->readSource
);
6399 ForgetSocket( &sock
);
6403 //===========================================================================================================================
6404 // DNSCryptPrintCertificate
6405 //===========================================================================================================================
6407 #define kCertTimeStrBufLen 32
6409 static char * CertTimeStr( time_t inTime
, char inBuffer
[ kCertTimeStrBufLen
] );
6411 static void DNSCryptPrintCertificate( const DNSCryptCert
*inCert
, size_t inLen
)
6413 time_t startTime
, endTime
;
6415 char timeBuf
[ kCertTimeStrBufLen
];
6417 check( inLen
>= kDNSCryptCertMinimumLength
);
6419 startTime
= (time_t) ReadBig32( inCert
->startTime
);
6420 endTime
= (time_t) ReadBig32( inCert
->endTime
);
6422 FPrintF( stdout
, "DNSCrypt certificate (%zu bytes):\n", inLen
);
6423 FPrintF( stdout
, "Cert Magic: %#H\n", inCert
->certMagic
, kDNSCryptCertMagicLength
, INT_MAX
);
6424 FPrintF( stdout
, "ES Version: %u\n", ReadBig16( inCert
->esVersion
) );
6425 FPrintF( stdout
, "Minor Version: %u\n", ReadBig16( inCert
->minorVersion
) );
6426 FPrintF( stdout
, "Signature: %H\n", inCert
->signature
, crypto_sign_BYTES
/ 2, INT_MAX
);
6427 FPrintF( stdout
, " %H\n", &inCert
->signature
[ crypto_sign_BYTES
/ 2 ], crypto_sign_BYTES
/ 2, INT_MAX
);
6428 FPrintF( stdout
, "Public Key: %H\n", inCert
->publicKey
, sizeof( inCert
->publicKey
), INT_MAX
);
6429 FPrintF( stdout
, "Client Magic: %H\n", inCert
->clientMagic
, kDNSCryptClientMagicLength
, INT_MAX
);
6430 FPrintF( stdout
, "Serial: %u\n", ReadBig32( inCert
->serial
) );
6431 FPrintF( stdout
, "Start Time: %u (%s)\n", (uint32_t) startTime
, CertTimeStr( startTime
, timeBuf
) );
6432 FPrintF( stdout
, "End Time: %u (%s)\n", (uint32_t) endTime
, CertTimeStr( endTime
, timeBuf
) );
6434 if( inLen
> kDNSCryptCertMinimumLength
)
6436 extLen
= (int)( inLen
- kDNSCryptCertMinimumLength
);
6437 FPrintF( stdout
, "Extensions: %.1H\n", inCert
->extensions
, extLen
, extLen
);
6439 FPrintF( stdout
, "\n" );
6442 static char * CertTimeStr( time_t inTime
, char inBuffer
[ kCertTimeStrBufLen
] )
6446 tm
= localtime( &inTime
);
6449 dlogassert( "localtime() returned a NULL pointer.\n" );
6454 strftime( inBuffer
, kCertTimeStrBufLen
, "%a %b %d %H:%M:%S %Z %Y", tm
);
6460 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
6462 //===========================================================================================================================
6464 //===========================================================================================================================
6468 const char * qnameStr
; // Name (QNAME) of the record being queried as a C string.
6469 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
6470 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
6471 int localPort
; // The port number to which the sockets are bound.
6472 int receiveSecs
; // After send, the amount of time to spend receiving.
6473 uint32_t ifIndex
; // Index of the interface over which to send the query.
6474 uint16_t qtype
; // The type (QTYPE) of the record being queried.
6475 Boolean isQU
; // True if the query is QU, i.e., requests unicast responses.
6476 Boolean allResponses
; // True if all mDNS messages received should be printed.
6477 Boolean printRawRData
; // True if RDATA should be printed as hexdumps.
6478 Boolean useIPv4
; // True if the query should be sent via IPv4 multicast.
6479 Boolean useIPv6
; // True if the query should be sent via IPv6 multicast.
6480 char ifName
[ IF_NAMESIZE
+ 1 ]; // Name of the interface over which to send the query.
6481 uint8_t qname
[ kDomainNameLengthMax
]; // Buffer to hold the QNAME in DNS label format.
6482 uint8_t msgBuf
[ kMDNSMessageSizeMax
]; // mDNS message buffer.
6486 static void MDNSQueryPrintPrologue( const MDNSQueryContext
*inContext
);
6487 static void MDNSQueryReadHandler( void *inContext
);
6489 static void MDNSQueryCmd( void )
6492 MDNSQueryContext
* context
;
6493 SocketRef sockV4
= kInvalidSocketRef
;
6494 SocketRef sockV6
= kInvalidSocketRef
;
6496 const char * ifname
;
6498 unsigned int sendCount
;
6500 // Check command parameters.
6502 if( gMDNSQuery_ReceiveSecs
< -1 )
6504 FPrintF( stdout
, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs
);
6509 context
= (MDNSQueryContext
*) calloc( 1, sizeof( *context
) );
6510 require_action( context
, exit
, err
= kNoMemoryErr
);
6512 context
->qnameStr
= gMDNSQuery_Name
;
6513 context
->receiveSecs
= gMDNSQuery_ReceiveSecs
;
6514 context
->isQU
= gMDNSQuery_IsQU
? true : false;
6515 context
->allResponses
= gMDNSQuery_AllResponses
? true : false;
6516 context
->printRawRData
= gMDNSQuery_RawRData
? true : false;
6517 context
->useIPv4
= ( gMDNSQuery_UseIPv4
|| !gMDNSQuery_UseIPv6
) ? true : false;
6518 context
->useIPv6
= ( gMDNSQuery_UseIPv6
|| !gMDNSQuery_UseIPv4
) ? true : false;
6520 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
6521 require_noerr_quiet( err
, exit
);
6523 ifname
= if_indextoname( context
->ifIndex
, context
->ifName
);
6524 require_action( ifname
, exit
, err
= kNameErr
);
6526 err
= RecordTypeFromArgString( gMDNSQuery_Type
, &context
->qtype
);
6527 require_noerr( err
, exit
);
6529 // Set up IPv4 socket.
6531 if( context
->useIPv4
)
6533 err
= CreateMulticastSocket( GetMDNSMulticastAddrV4(),
6534 gMDNSQuery_SourcePort
? gMDNSQuery_SourcePort
: ( context
->isQU
? context
->localPort
: kMDNSPort
),
6535 ifname
, context
->ifIndex
, !context
->isQU
, &context
->localPort
, &sockV4
);
6536 require_noerr( err
, exit
);
6539 // Set up IPv6 socket.
6541 if( context
->useIPv6
)
6543 err
= CreateMulticastSocket( GetMDNSMulticastAddrV6(),
6544 gMDNSQuery_SourcePort
? gMDNSQuery_SourcePort
: ( context
->isQU
? context
->localPort
: kMDNSPort
),
6545 ifname
, context
->ifIndex
, !context
->isQU
, &context
->localPort
, &sockV6
);
6546 require_noerr( err
, exit
);
6549 // Craft mDNS query message.
6551 check_compile_time_code( sizeof( context
->msgBuf
) >= kDNSQueryMessageMaxLen
);
6552 err
= WriteDNSQueryMessage( context
->msgBuf
, kDefaultMDNSMessageID
, kDefaultMDNSQueryFlags
, context
->qnameStr
,
6553 context
->qtype
, context
->isQU
? ( kDNSServiceClass_IN
| kQClassUnicastResponseBit
) : kDNSServiceClass_IN
, &msgLen
);
6554 require_noerr( err
, exit
);
6558 MDNSQueryPrintPrologue( context
);
6560 // Send mDNS query message.
6563 if( IsValidSocket( sockV4
) )
6565 const struct sockaddr
* const mcastAddr4
= GetMDNSMulticastAddrV4();
6567 n
= sendto( sockV4
, context
->msgBuf
, msgLen
, 0, mcastAddr4
, SockAddrGetSize( mcastAddr4
) );
6568 err
= map_socket_value_errno( sockV4
, n
== (ssize_t
) msgLen
, n
);
6571 FPrintF( stderr
, "*** Failed to send query on IPv4 socket with error %#m\n", err
);
6572 ForgetSocket( &sockV4
);
6579 if( IsValidSocket( sockV6
) )
6581 const struct sockaddr
* const mcastAddr6
= GetMDNSMulticastAddrV6();
6583 n
= sendto( sockV6
, context
->msgBuf
, msgLen
, 0, mcastAddr6
, SockAddrGetSize( mcastAddr6
) );
6584 err
= map_socket_value_errno( sockV6
, n
== (ssize_t
) msgLen
, n
);
6587 FPrintF( stderr
, "*** Failed to send query on IPv6 socket with error %#m\n", err
);
6588 ForgetSocket( &sockV6
);
6595 require_action_quiet( sendCount
> 0, exit
, err
= kUnexpectedErr
);
6597 // If there's no wait period after the send, then exit.
6599 if( context
->receiveSecs
== 0 ) goto exit
;
6601 // Create dispatch read sources for socket(s).
6603 if( IsValidSocket( sockV4
) )
6605 SocketContext
* sockCtx
;
6607 err
= SocketContextCreate( sockV4
, context
, &sockCtx
);
6608 require_noerr( err
, exit
);
6609 sockV4
= kInvalidSocketRef
;
6611 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, MDNSQueryReadHandler
, SocketContextCancelHandler
, sockCtx
,
6612 &context
->readSourceV4
);
6613 if( err
) ForgetSocketContext( &sockCtx
);
6614 require_noerr( err
, exit
);
6616 dispatch_resume( context
->readSourceV4
);
6619 if( IsValidSocket( sockV6
) )
6621 SocketContext
* sockCtx
;
6623 err
= SocketContextCreate( sockV6
, context
, &sockCtx
);
6624 require_noerr( err
, exit
);
6625 sockV6
= kInvalidSocketRef
;
6627 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, MDNSQueryReadHandler
, SocketContextCancelHandler
, sockCtx
,
6628 &context
->readSourceV6
);
6629 if( err
) ForgetSocketContext( &sockCtx
);
6630 require_noerr( err
, exit
);
6632 dispatch_resume( context
->readSourceV6
);
6635 if( context
->receiveSecs
> 0 )
6637 dispatch_after_f( dispatch_time_seconds( context
->receiveSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
6643 ForgetSocket( &sockV4
);
6644 ForgetSocket( &sockV6
);
6645 if( err
) exit( 1 );
6648 //===========================================================================================================================
6650 //===========================================================================================================================
6652 static void _MDNSColliderCmdStopHandler( void *inContext
, OSStatus inError
);
6654 static void MDNSColliderCmd( void )
6657 MDNSColliderRef collider
= NULL
;
6658 uint8_t * rdataPtr
= NULL
;
6659 size_t rdataLen
= 0;
6660 const char * ifname
;
6662 MDNSColliderProtocols protocols
;
6664 char ifName
[ IF_NAMESIZE
+ 1 ];
6665 uint8_t name
[ kDomainNameLengthMax
];
6667 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
6668 require_noerr_quiet( err
, exit
);
6670 ifname
= if_indextoname( ifIndex
, ifName
);
6673 FPrintF( stderr
, "error: Invalid interface name or index: %s\n", gInterface
);
6678 err
= DomainNameFromString( name
, gMDNSCollider_Name
, NULL
);
6681 FPrintF( stderr
, "error: Invalid record name: %s\n", gMDNSCollider_Name
);
6685 err
= RecordTypeFromArgString( gMDNSCollider_Type
, &type
);
6686 require_noerr_quiet( err
, exit
);
6688 if( gMDNSCollider_RecordData
)
6690 err
= RecordDataFromArgString( gMDNSCollider_RecordData
, &rdataPtr
, &rdataLen
);
6691 require_noerr_quiet( err
, exit
);
6694 err
= MDNSColliderCreate( dispatch_get_main_queue(), &collider
);
6695 require_noerr( err
, exit
);
6697 err
= MDNSColliderSetProgram( collider
, gMDNSCollider_Program
);
6700 FPrintF( stderr
, "error: Failed to set program string: '%s'\n", gMDNSCollider_Program
);
6704 err
= MDNSColliderSetRecord( collider
, name
, type
, rdataPtr
, rdataLen
);
6705 require_noerr( err
, exit
);
6706 ForgetMem( &rdataPtr
);
6708 protocols
= kMDNSColliderProtocol_None
;
6709 if( gMDNSCollider_UseIPv4
|| !gMDNSCollider_UseIPv6
) protocols
|= kMDNSColliderProtocol_IPv4
;
6710 if( gMDNSCollider_UseIPv6
|| !gMDNSCollider_UseIPv4
) protocols
|= kMDNSColliderProtocol_IPv6
;
6711 MDNSColliderSetProtocols( collider
, protocols
);
6712 MDNSColliderSetInterfaceIndex( collider
, ifIndex
);
6713 MDNSColliderSetStopHandler( collider
, _MDNSColliderCmdStopHandler
, collider
);
6715 err
= MDNSColliderStart( collider
);
6716 require_noerr( err
, exit
);
6721 FreeNullSafe( rdataPtr
);
6722 CFReleaseNullSafe( collider
);
6723 if( err
) exit( 1 );
6726 static void _MDNSColliderCmdStopHandler( void *inContext
, OSStatus inError
)
6728 MDNSColliderRef
const collider
= (MDNSColliderRef
) inContext
;
6730 CFRelease( collider
);
6731 exit( inError
? 1 : 0 );
6734 //===========================================================================================================================
6735 // MDNSQueryPrintPrologue
6736 //===========================================================================================================================
6738 static void MDNSQueryPrintPrologue( const MDNSQueryContext
*inContext
)
6740 const int receiveSecs
= inContext
->receiveSecs
;
6742 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, inContext
->ifName
);
6743 FPrintF( stdout
, "Name: %s\n", inContext
->qnameStr
);
6744 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->qtype
), inContext
->qtype
);
6745 FPrintF( stdout
, "Class: IN (%s)\n", inContext
->isQU
? "QU" : "QM" );
6746 FPrintF( stdout
, "Local port: %d\n", inContext
->localPort
);
6747 FPrintF( stdout
, "IP protocols: %?s%?s%?s\n",
6748 inContext
->useIPv4
, "IPv4", ( inContext
->useIPv4
&& inContext
->useIPv6
), ", ", inContext
->useIPv6
, "IPv6" );
6749 FPrintF( stdout
, "Receive duration: " );
6750 if( receiveSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", receiveSecs
, receiveSecs
!= 1, 's' );
6751 else FPrintF( stdout
, "∞\n" );
6752 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
6755 //===========================================================================================================================
6756 // MDNSQueryReadHandler
6757 //===========================================================================================================================
6759 static void MDNSQueryReadHandler( void *inContext
)
6763 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6764 MDNSQueryContext
* const context
= (MDNSQueryContext
*) sockCtx
->userContext
;
6766 sockaddr_ip fromAddr
;
6767 Boolean foundAnswer
= false;
6769 gettimeofday( &now
, NULL
);
6771 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &msgLen
, &fromAddr
,
6772 sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6773 require_noerr( err
, exit
);
6775 if( !context
->allResponses
&& ( msgLen
>= kDNSHeaderLength
) )
6777 const uint8_t * ptr
;
6778 const DNSHeader
* const hdr
= (DNSHeader
*) context
->msgBuf
;
6779 unsigned int rrCount
, i
;
6780 uint16_t type
, class;
6781 uint8_t name
[ kDomainNameLengthMax
];
6783 err
= DNSMessageGetAnswerSection( context
->msgBuf
, msgLen
, &ptr
);
6784 require_noerr( err
, exit
);
6786 if( context
->qname
[ 0 ] == 0 )
6788 err
= DomainNameAppendString( context
->qname
, context
->qnameStr
, NULL
);
6789 require_noerr( err
, exit
);
6792 rrCount
= DNSHeaderGetAnswerCount( hdr
) + DNSHeaderGetAuthorityCount( hdr
) + DNSHeaderGetAdditionalCount( hdr
);
6793 for( i
= 0; i
< rrCount
; ++i
)
6795 err
= DNSMessageExtractRecord( context
->msgBuf
, msgLen
, ptr
, name
, &type
, &class, NULL
, NULL
, NULL
, &ptr
);
6796 require_noerr( err
, exit
);
6798 if( ( ( context
->qtype
== kDNSServiceType_ANY
) || ( type
== context
->qtype
) ) &&
6799 DomainNameEqual( name
, context
->qname
) )
6806 if( context
->allResponses
|| foundAnswer
)
6808 FPrintF( stdout
, "---\n" );
6809 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6810 FPrintF( stdout
, "Source: %##a\n", &fromAddr
);
6811 FPrintF( stdout
, "Message size: %zu\n\n%#.*{du:dnsmsg}",
6812 msgLen
, context
->printRawRData
? 1 : 0, context
->msgBuf
, msgLen
);
6816 if( err
) exit( 1 );
6819 //===========================================================================================================================
6821 //===========================================================================================================================
6823 static void PIDToUUIDCmd( void )
6827 struct proc_uniqidentifierinfo info
;
6829 n
= proc_pidinfo( gPIDToUUID_PID
, PROC_PIDUNIQIDENTIFIERINFO
, 1, &info
, sizeof( info
) );
6830 require_action_quiet( n
== (int) sizeof( info
), exit
, err
= kUnknownErr
);
6832 FPrintF( stdout
, "%#U\n", info
.p_uuid
);
6836 if( err
) exit( 1 );
6839 //===========================================================================================================================
6841 //===========================================================================================================================
6843 typedef struct DNSServerPrivate
* DNSServerRef
;
6847 DNSServerRef server
; // Reference to the DNS server.
6848 dispatch_source_t sigIntSource
; // Dispatch SIGINT source.
6849 dispatch_source_t sigTermSource
; // Dispatch SIGTERM source.
6850 const char * domainOverride
; // If non-NULL, the server is to use this domain instead of "d.test.".
6851 #if( TARGET_OS_DARWIN )
6852 dispatch_source_t processMonitor
; // Process monitor source for process being followed, if any.
6853 pid_t followPID
; // PID of process being followed, if any. (If it exits, we exit).
6854 Boolean addedResolver
; // True if system DNS settings contains a resolver entry for server.
6856 Boolean loopbackOnly
; // True if the server should be bound to the loopback interface.
6858 } DNSServerCmdContext
;
6862 kDNSServerEvent_Started
= 1,
6863 kDNSServerEvent_Stopped
= 2
6865 } DNSServerEventType
;
6867 typedef void ( *DNSServerEventHandler_f
)( DNSServerEventType inType
, uintptr_t inEventData
, void *inContext
);
6869 CFTypeID
DNSServerGetTypeID( void );
6872 dispatch_queue_t inQueue
,
6873 DNSServerEventHandler_f inEventHandler
,
6874 void * inEventContext
,
6875 unsigned int inResponseDelayMs
,
6876 uint32_t inDefaultTTL
,
6878 Boolean inLoopbackOnly
,
6879 const char * inDomain
,
6880 Boolean inBadUDPMode
,
6881 DNSServerRef
* outServer
);
6882 static void DNSServerStart( DNSServerRef inServer
);
6883 static void DNSServerStop( DNSServerRef inServer
);
6885 #define ForgetDNSServer( X ) ForgetCustomEx( X, DNSServerStop, CFRelease )
6887 static void DNSServerCmdContextFree( DNSServerCmdContext
*inContext
);
6888 static void DNSServerCmdEventHandler( DNSServerEventType inType
, uintptr_t inEventData
, void *inContext
);
6889 static void DNSServerCmdSigIntHandler( void *inContext
);
6890 static void DNSServerCmdSigTermHandler( void *inContext
);
6891 #if( TARGET_OS_DARWIN )
6892 static void DNSServerCmdFollowedProcessHandler( void *inContext
);
6895 ulog_define_ex( kDNSSDUtilIdentifier
, DNSServer
, kLogLevelInfo
, kLogFlags_None
, "DNSServer", NULL
);
6896 #define ds_ulog( LEVEL, ... ) ulog( &log_category_from_name( DNSServer ), (LEVEL), __VA_ARGS__ )
6898 static void DNSServerCmd( void )
6901 DNSServerCmdContext
* context
= NULL
;
6903 if( gDNSServer_Foreground
)
6905 LogControl( "DNSServer:output=file;stdout,DNSServer:flags=time;prefix" );
6908 err
= CheckIntegerArgument( gDNSServer_ResponseDelayMs
, "response delay (ms)", 0, INT_MAX
);
6909 require_noerr_quiet( err
, exit
);
6911 err
= CheckIntegerArgument( gDNSServer_DefaultTTL
, "default TTL", 0, INT32_MAX
);
6912 require_noerr_quiet( err
, exit
);
6914 err
= CheckIntegerArgument( gDNSServer_Port
, "port number", -UINT16_MAX
, UINT16_MAX
);
6915 require_noerr_quiet( err
, exit
);
6917 context
= (DNSServerCmdContext
*) calloc( 1, sizeof( *context
) );
6918 require_action( context
, exit
, err
= kNoMemoryErr
);
6920 context
->domainOverride
= gDNSServer_DomainOverride
;
6921 context
->loopbackOnly
= gDNSServer_LoopbackOnly
? true : false;
6923 #if( TARGET_OS_DARWIN )
6924 if( gDNSServer_FollowPID
)
6926 context
->followPID
= _StringToPID( gDNSServer_FollowPID
, &err
);
6927 if( err
|| ( context
->followPID
< 0 ) )
6929 FPrintF( stderr
, "error: Invalid follow PID: %s\n", gDNSServer_FollowPID
);
6934 err
= DispatchProcessMonitorCreate( context
->followPID
, DISPATCH_PROC_EXIT
, dispatch_get_main_queue(),
6935 DNSServerCmdFollowedProcessHandler
, NULL
, context
, &context
->processMonitor
);
6936 require_noerr( err
, exit
);
6937 dispatch_resume( context
->processMonitor
);
6941 context
->followPID
= -1;
6945 signal( SIGINT
, SIG_IGN
);
6946 err
= DispatchSignalSourceCreate( SIGINT
, DNSServerCmdSigIntHandler
, context
, &context
->sigIntSource
);
6947 require_noerr( err
, exit
);
6948 dispatch_resume( context
->sigIntSource
);
6950 signal( SIGTERM
, SIG_IGN
);
6951 err
= DispatchSignalSourceCreate( SIGTERM
, DNSServerCmdSigTermHandler
, context
, &context
->sigTermSource
);
6952 require_noerr( err
, exit
);
6953 dispatch_resume( context
->sigTermSource
);
6955 err
= DNSServerCreate( dispatch_get_main_queue(), DNSServerCmdEventHandler
, context
,
6956 (unsigned int) gDNSServer_ResponseDelayMs
, (uint32_t) gDNSServer_DefaultTTL
, gDNSServer_Port
, context
->loopbackOnly
,
6957 context
->domainOverride
, gDNSServer_BadUDPMode
? true : false, &context
->server
);
6958 require_noerr( err
, exit
);
6960 DNSServerStart( context
->server
);
6964 FPrintF( stderr
, "Failed to start DNS server: %#m\n", err
);
6965 if( context
) DNSServerCmdContextFree( context
);
6966 if( err
) exit( 1 );
6969 //===========================================================================================================================
6970 // DNSServerCmdContextFree
6971 //===========================================================================================================================
6973 static void DNSServerCmdContextFree( DNSServerCmdContext
*inContext
)
6975 ForgetCF( &inContext
->server
);
6976 dispatch_source_forget( &inContext
->sigIntSource
);
6977 dispatch_source_forget( &inContext
->sigTermSource
);
6978 #if( TARGET_OS_DARWIN )
6979 dispatch_source_forget( &inContext
->processMonitor
);
6984 //===========================================================================================================================
6985 // DNSServerCmdEventHandler
6986 //===========================================================================================================================
6988 #if( TARGET_OS_DARWIN )
6989 static OSStatus
_DNSServerCmdLoopbackResolverAdd( const char *inDomain
, int inPort
);
6990 static OSStatus
_DNSServerCmdLoopbackResolverRemove( void );
6993 static void DNSServerCmdEventHandler( DNSServerEventType inType
, uintptr_t inEventData
, void *inContext
)
6996 DNSServerCmdContext
* const context
= (DNSServerCmdContext
*) inContext
;
6998 if( inType
== kDNSServerEvent_Started
)
7000 #if( TARGET_OS_DARWIN )
7001 const int port
= (int) inEventData
;
7003 err
= _DNSServerCmdLoopbackResolverAdd( context
->domainOverride
? context
->domainOverride
: "d.test.", port
);
7006 ds_ulog( kLogLevelError
, "Failed to add loopback resolver to DNS configuration for \"d.test.\" domain: %#m\n",
7008 if( context
->loopbackOnly
) ForgetDNSServer( &context
->server
);
7012 context
->addedResolver
= true;
7016 else if( inType
== kDNSServerEvent_Stopped
)
7018 const OSStatus stopError
= (OSStatus
) inEventData
;
7020 if( stopError
) ds_ulog( kLogLevelError
, "The server stopped unexpectedly with error: %#m.\n", stopError
);
7023 #if( TARGET_OS_DARWIN )
7024 if( context
->addedResolver
)
7026 err
= _DNSServerCmdLoopbackResolverRemove();
7029 ds_ulog( kLogLevelError
, "Failed to remove loopback resolver from DNS configuration: %#m\n", err
);
7033 context
->addedResolver
= false;
7036 else if( context
->loopbackOnly
)
7041 DNSServerCmdContextFree( context
);
7042 exit( ( stopError
|| err
) ? 1 : 0 );
7046 #if( TARGET_OS_DARWIN )
7047 //===========================================================================================================================
7048 // _DNSServerCmdLoopbackResolverAdd
7049 //===========================================================================================================================
7051 static OSStatus
_DNSServerCmdLoopbackResolverAdd( const char *inDomain
, int inPort
)
7054 SCDynamicStoreRef store
;
7055 CFPropertyListRef plist
= NULL
;
7056 CFStringRef key
= NULL
;
7057 const uint32_t loopbackV4
= htonl( INADDR_LOOPBACK
);
7060 store
= SCDynamicStoreCreate( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
7061 err
= map_scerror( store
);
7062 require_noerr( err
, exit
);
7064 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
7079 kSCPropNetDNSSupplementalMatchDomains
, inDomain
,
7080 kSCPropNetDNSServerAddresses
, &loopbackV4
, in6addr_loopback
.s6_addr
,
7081 kSCPropNetDNSServerPort
, inPort
,
7082 kSCPropInterfaceName
, CFSTR( "lo0" ),
7083 kSCPropNetDNSConfirmedServiceID
, CFSTR( "com.apple.dnssdutil.server" ) );
7084 require_noerr( err
, exit
);
7086 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
,
7087 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS
);
7088 require_action( key
, exit
, err
= kUnknownErr
);
7090 success
= SCDynamicStoreSetValue( store
, key
, plist
);
7091 require_action( success
, exit
, err
= kUnknownErr
);
7094 CFReleaseNullSafe( store
);
7095 CFReleaseNullSafe( plist
);
7096 CFReleaseNullSafe( key
);
7100 //===========================================================================================================================
7101 // _DNSServerCmdLoopbackResolverRemove
7102 //===========================================================================================================================
7104 static OSStatus
_DNSServerCmdLoopbackResolverRemove( void )
7107 SCDynamicStoreRef store
;
7108 CFStringRef key
= NULL
;
7111 store
= SCDynamicStoreCreate( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
7112 err
= map_scerror( store
);
7113 require_noerr( err
, exit
);
7115 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
,
7116 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS
);
7117 require_action( key
, exit
, err
= kUnknownErr
);
7119 success
= SCDynamicStoreRemoveValue( store
, key
);
7120 require_action( success
, exit
, err
= kUnknownErr
);
7123 CFReleaseNullSafe( store
);
7124 CFReleaseNullSafe( key
);
7129 //===========================================================================================================================
7130 // DNSServerCmdSigIntHandler
7131 //===========================================================================================================================
7133 static void _DNSServerCmdShutdown( DNSServerCmdContext
*inContext
, int inSignal
);
7135 static void DNSServerCmdSigIntHandler( void *inContext
)
7137 _DNSServerCmdShutdown( (DNSServerCmdContext
*) inContext
, SIGINT
);
7140 //===========================================================================================================================
7141 // DNSServerCmdSigTermHandler
7142 //===========================================================================================================================
7144 static void DNSServerCmdSigTermHandler( void *inContext
)
7146 _DNSServerCmdShutdown( (DNSServerCmdContext
*) inContext
, SIGTERM
);
7149 #if( TARGET_OS_DARWIN )
7150 //===========================================================================================================================
7151 // DNSServerCmdFollowedProcessHandler
7152 //===========================================================================================================================
7154 static void DNSServerCmdFollowedProcessHandler( void *inContext
)
7156 DNSServerCmdContext
* const context
= (DNSServerCmdContext
*) inContext
;
7158 if( dispatch_source_get_data( context
->processMonitor
) & DISPATCH_PROC_EXIT
) _DNSServerCmdShutdown( context
, 0 );
7162 //===========================================================================================================================
7163 // _DNSServerCmdExternalExit
7164 //===========================================================================================================================
7166 #define SignalNumberToString( X ) ( \
7167 ( (X) == SIGINT ) ? "SIGINT" : \
7168 ( (X) == SIGTERM ) ? "SIGTERM" : \
7171 static void _DNSServerCmdShutdown( DNSServerCmdContext
*inContext
, int inSignal
)
7173 dispatch_source_forget( &inContext
->sigIntSource
);
7174 dispatch_source_forget( &inContext
->sigTermSource
);
7175 #if( TARGET_OS_DARWIN )
7176 dispatch_source_forget( &inContext
->processMonitor
);
7180 ds_ulog( kLogLevelNotice
, "Exiting: followed process (%lld) exited\n", (int64_t) inContext
->followPID
);
7185 ds_ulog( kLogLevelNotice
, "Exiting: received signal %d (%s)\n", inSignal
, SignalNumberToString( inSignal
) );
7188 ForgetDNSServer( &inContext
->server
);
7191 //===========================================================================================================================
7193 //===========================================================================================================================
7195 #define kDDotTestDomainName (const uint8_t *) "\x01" "d" "\x04" "test"
7197 typedef struct DNSDelayedResponse DNSDelayedResponse
;
7198 struct DNSDelayedResponse
7200 DNSDelayedResponse
* next
;
7201 sockaddr_ip destAddr
;
7202 uint64_t targetTicks
;
7207 struct DNSServerPrivate
7209 CFRuntimeBase base
; // CF object base.
7210 uint8_t * domain
; // Parent domain of server's resource records.
7211 dispatch_queue_t queue
; // Queue for DNS server's events.
7212 dispatch_source_t readSourceUDPv4
; // Read source for IPv4 UDP socket.
7213 dispatch_source_t readSourceUDPv6
; // Read source for IPv6 UDP socket.
7214 dispatch_source_t readSourceTCPv4
; // Read source for IPv4 TCP socket.
7215 dispatch_source_t readSourceTCPv6
; // Read source for IPv6 TCP socket.
7216 SocketRef sockUDPv4
;
7217 SocketRef sockUDPv6
;
7218 DNSServerEventHandler_f eventHandler
;
7219 void * eventContext
;
7220 DNSDelayedResponse
* responseList
;
7221 dispatch_source_t responseTimer
;
7222 unsigned int responseDelayMs
;
7223 uint32_t defaultTTL
;
7224 uint32_t serial
; // Serial number for SOA record.
7225 int port
; // Port to use for receiving and sending DNS messages.
7228 Boolean loopbackOnly
;
7229 Boolean badUDPMode
; // True if the server runs in Bad UDP mode.
7232 static void _DNSServerUDPReadHandler( void *inContext
);
7233 static void _DNSServerTCPReadHandler( void *inContext
);
7234 static void _DNSDelayedResponseFree( DNSDelayedResponse
*inResponse
);
7235 static void _DNSDelayedResponseFreeList( DNSDelayedResponse
*inList
);
7237 CF_CLASS_DEFINE( DNSServer
);
7241 dispatch_queue_t inQueue
,
7242 DNSServerEventHandler_f inEventHandler
,
7243 void * inEventContext
,
7244 unsigned int inResponseDelayMs
,
7245 uint32_t inDefaultTTL
,
7247 Boolean inLoopbackOnly
,
7248 const char * inDomain
,
7249 Boolean inBadUDPMode
,
7250 DNSServerRef
* outServer
)
7253 DNSServerRef obj
= NULL
;
7255 require_action_quiet( inDefaultTTL
<= INT32_MAX
, exit
, err
= kRangeErr
);
7257 CF_OBJECT_CREATE( DNSServer
, obj
, err
, exit
);
7259 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
7260 obj
->eventHandler
= inEventHandler
;
7261 obj
->eventContext
= inEventContext
;
7262 obj
->responseDelayMs
= inResponseDelayMs
;
7263 obj
->defaultTTL
= inDefaultTTL
;
7265 obj
->loopbackOnly
= inLoopbackOnly
;
7266 obj
->badUDPMode
= inBadUDPMode
;
7270 err
= StringToDomainName( inDomain
, &obj
->domain
, NULL
);
7271 require_noerr_quiet( err
, exit
);
7275 err
= DomainNameDup( kDDotTestDomainName
, &obj
->domain
, NULL
);
7276 require_noerr_quiet( err
, exit
);
7284 CFReleaseNullSafe( obj
);
7288 //===========================================================================================================================
7289 // _DNSServerFinalize
7290 //===========================================================================================================================
7292 static void _DNSServerFinalize( CFTypeRef inObj
)
7294 DNSServerRef
const me
= (DNSServerRef
) inObj
;
7296 check( !me
->readSourceUDPv4
);
7297 check( !me
->readSourceUDPv6
);
7298 check( !me
->readSourceTCPv4
);
7299 check( !me
->readSourceTCPv6
);
7300 check( !me
->responseTimer
);
7301 ForgetMem( &me
->domain
);
7302 dispatch_forget( &me
->queue
);
7305 //===========================================================================================================================
7307 //===========================================================================================================================
7309 static void _DNSServerStart( void *inContext
);
7310 static void _DNSServerStop( void *inContext
, OSStatus inError
);
7312 static void DNSServerStart( DNSServerRef me
)
7315 dispatch_async_f( me
->queue
, me
, _DNSServerStart
);
7318 static void _DNSServerStart( void *inContext
)
7322 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7323 SocketRef sock
= kInvalidSocketRef
;
7324 SocketContext
* sockCtx
= NULL
;
7325 const uint32_t loopbackV4
= htonl( INADDR_LOOPBACK
);
7326 int year
, month
, day
;
7328 // Create IPv4 UDP socket.
7329 // Initially, me->port is the port requested by the user. If it's 0, then the user wants any available ephemeral port.
7330 // If it's negative, then the user would like a port number equal to its absolute value, but will settle for any
7331 // available ephemeral port, if it's not available. The actual port number that was used will be stored in me->port and
7332 // used for the remaining sockets.
7334 err
= _ServerSocketOpenEx2( AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
, me
->loopbackOnly
? &loopbackV4
: NULL
,
7335 me
->port
, &me
->port
, kSocketBufferSize_DontSet
, me
->loopbackOnly
? true : false, &sock
);
7336 require_noerr( err
, exit
);
7337 check( me
->port
> 0 );
7339 // Create read source for IPv4 UDP socket.
7341 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7342 require_noerr( err
, exit
);
7343 sock
= kInvalidSocketRef
;
7345 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerUDPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7346 &me
->readSourceUDPv4
);
7347 require_noerr( err
, exit
);
7348 dispatch_resume( me
->readSourceUDPv4
);
7349 me
->sockUDPv4
= sockCtx
->sock
;
7352 // Create IPv6 UDP socket.
7354 err
= _ServerSocketOpenEx2( AF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
, me
->loopbackOnly
? &in6addr_loopback
: NULL
,
7355 me
->port
, NULL
, kSocketBufferSize_DontSet
, me
->loopbackOnly
? true : false, &sock
);
7356 require_noerr( err
, exit
);
7358 // Create read source for IPv6 UDP socket.
7360 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7361 require_noerr( err
, exit
);
7362 sock
= kInvalidSocketRef
;
7364 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerUDPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7365 &me
->readSourceUDPv6
);
7366 require_noerr( err
, exit
);
7367 dispatch_resume( me
->readSourceUDPv6
);
7368 me
->sockUDPv6
= sockCtx
->sock
;
7371 // Create IPv4 TCP socket.
7373 err
= _ServerSocketOpenEx2( AF_INET
, SOCK_STREAM
, IPPROTO_TCP
, me
->loopbackOnly
? &loopbackV4
: NULL
,
7374 me
->port
, NULL
, kSocketBufferSize_DontSet
, false, &sock
);
7375 require_noerr( err
, exit
);
7377 // Create read source for IPv4 TCP socket.
7379 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7380 require_noerr( err
, exit
);
7381 sock
= kInvalidSocketRef
;
7383 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerTCPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7384 &me
->readSourceTCPv4
);
7385 require_noerr( err
, exit
);
7386 dispatch_resume( me
->readSourceTCPv4
);
7389 // Create IPv6 TCP socket.
7391 err
= _ServerSocketOpenEx2( AF_INET6
, SOCK_STREAM
, IPPROTO_TCP
, me
->loopbackOnly
? &in6addr_loopback
: NULL
,
7392 me
->port
, NULL
, kSocketBufferSize_DontSet
, false, &sock
);
7393 require_noerr( err
, exit
);
7395 // Create read source for IPv6 TCP socket.
7397 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7398 require_noerr( err
, exit
);
7399 sock
= kInvalidSocketRef
;
7401 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerTCPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7402 &me
->readSourceTCPv6
);
7403 require_noerr( err
, exit
);
7404 dispatch_resume( me
->readSourceTCPv6
);
7407 ds_ulog( kLogLevelInfo
, "Server is using port %d.\n", me
->port
);
7408 if( me
->eventHandler
) me
->eventHandler( kDNSServerEvent_Started
, (uintptr_t) me
->port
, me
->eventContext
);
7410 // Create the serial number for the server's SOA record in the YYYMMDDnn convention recommended by
7411 // <https://tools.ietf.org/html/rfc1912#section-2.2> using the current time.
7413 gettimeofday( &now
, NULL
);
7414 SecondsToYMD_HMS( ( INT64_C_safe( kDaysToUnixEpoch
) * kSecondsPerDay
) + now
.tv_sec
, &year
, &month
, &day
,
7416 me
->serial
= (uint32_t)( ( year
* 1000000 ) + ( month
* 10000 ) + ( day
* 100 ) + 1 );
7419 ForgetSocket( &sock
);
7420 if( sockCtx
) SocketContextRelease( sockCtx
);
7421 if( err
) _DNSServerStop( me
, err
);
7424 //===========================================================================================================================
7426 //===========================================================================================================================
7428 static void _DNSServerUserStop( void *inContext
);
7429 static void _DNSServerStop2( void *inContext
);
7431 static void DNSServerStop( DNSServerRef me
)
7434 dispatch_async_f( me
->queue
, me
, _DNSServerUserStop
);
7437 static void _DNSServerUserStop( void *inContext
)
7439 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7441 _DNSServerStop( me
, kNoErr
);
7445 static void _DNSServerStop( void *inContext
, OSStatus inError
)
7447 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7449 me
->stopError
= inError
;
7450 dispatch_source_forget( &me
->readSourceUDPv4
);
7451 dispatch_source_forget( &me
->readSourceUDPv6
);
7452 dispatch_source_forget( &me
->readSourceTCPv4
);
7453 dispatch_source_forget( &me
->readSourceTCPv6
);
7454 dispatch_source_forget( &me
->responseTimer
);
7455 me
->sockUDPv4
= kInvalidSocketRef
;
7456 me
->sockUDPv6
= kInvalidSocketRef
;
7458 if( me
->responseList
)
7460 _DNSDelayedResponseFreeList( me
->responseList
);
7461 me
->responseList
= NULL
;
7463 dispatch_async_f( me
->queue
, me
, _DNSServerStop2
);
7466 static void _DNSServerStop2( void *inContext
)
7468 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7473 if( me
->eventHandler
) me
->eventHandler( kDNSServerEvent_Stopped
, (uintptr_t) me
->stopError
, me
->eventContext
);
7479 //===========================================================================================================================
7480 // _DNSDelayedResponseFree
7481 //===========================================================================================================================
7483 static void _DNSDelayedResponseFree( DNSDelayedResponse
*inResponse
)
7485 ForgetMem( &inResponse
->msgPtr
);
7489 //===========================================================================================================================
7490 // _DNSDelayedResponseFreeList
7491 //===========================================================================================================================
7493 static void _DNSDelayedResponseFreeList( DNSDelayedResponse
*inList
)
7495 DNSDelayedResponse
* response
;
7497 while( ( response
= inList
) != NULL
)
7499 inList
= response
->next
;
7500 _DNSDelayedResponseFree( response
);
7504 //===========================================================================================================================
7505 // _DNSServerUDPReadHandler
7506 //===========================================================================================================================
7509 _DNSServerAnswerQuery(
7510 DNSServerRef inServer
,
7511 const uint8_t * inQueryPtr
,
7514 uint8_t ** outResponsePtr
,
7515 size_t * outResponseLen
);
7517 #define _DNSServerAnswerQueryForUDP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7518 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, false, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7520 #define _DNSServerAnswerQueryForTCP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7521 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, true, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7524 _DNSServerScheduleDelayedResponse(
7525 DNSServerRef inServer
,
7526 const struct sockaddr
* inDestAddr
,
7529 static void _DNSServerUDPDelayedSend( void *inContext
);
7531 static void _DNSServerUDPReadHandler( void *inContext
)
7534 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
7535 DNSServerRef
const me
= (DNSServerRef
) sockCtx
->userContext
;
7538 sockaddr_ip clientAddr
;
7539 socklen_t clientAddrLen
;
7540 uint8_t * responsePtr
= NULL
; // malloc'd
7544 gettimeofday( &now
, NULL
);
7548 clientAddrLen
= (socklen_t
) sizeof( clientAddr
);
7549 n
= recvfrom( sockCtx
->sock
, (char *) msg
, sizeof( msg
), 0, &clientAddr
.sa
, &clientAddrLen
);
7550 err
= map_socket_value_errno( sockCtx
->sock
, n
>= 0, n
);
7551 require_noerr( err
, exit
);
7553 ds_ulog( kLogLevelInfo
, "UDP server received %zd bytes from %##a at %{du:time}.\n", n
, &clientAddr
, &now
);
7555 if( n
< kDNSHeaderLength
)
7557 ds_ulog( kLogLevelInfo
, "UDP DNS message is too small (%zd < %d).\n", n
, kDNSHeaderLength
);
7561 ds_ulog( kLogLevelInfo
, "UDP received message:\n\n%1{du:dnsmsg}", msg
, (size_t) n
);
7565 err
= _DNSServerAnswerQueryForUDP( me
, msg
, (size_t) n
, &responsePtr
, &responseLen
);
7566 require_noerr_quiet( err
, exit
);
7568 // Schedule response.
7570 if( me
->responseDelayMs
> 0 )
7572 err
= _DNSServerScheduleDelayedResponse( me
, &clientAddr
.sa
, responsePtr
, responseLen
);
7573 require_noerr( err
, exit
);
7578 ds_ulog( kLogLevelInfo
, "UDP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen
, responsePtr
, responseLen
);
7580 n
= sendto( sockCtx
->sock
, (char *) responsePtr
, responseLen
, 0, &clientAddr
.sa
, clientAddrLen
);
7581 err
= map_socket_value_errno( sockCtx
->sock
, n
== (ssize_t
) responseLen
, n
);
7582 require_noerr( err
, exit
);
7586 FreeNullSafe( responsePtr
);
7591 _DNSServerScheduleDelayedResponse(
7593 const struct sockaddr
* inDestAddr
,
7598 DNSDelayedResponse
* response
;
7599 DNSDelayedResponse
** responsePtr
;
7600 DNSDelayedResponse
* newResponse
;
7601 uint64_t targetTicks
;
7603 targetTicks
= UpTicks() + MillisecondsToUpTicks( me
->responseDelayMs
);
7605 newResponse
= (DNSDelayedResponse
*) calloc( 1, sizeof( *newResponse
) );
7606 require_action( newResponse
, exit
, err
= kNoMemoryErr
);
7608 if( !me
->responseList
|| ( targetTicks
< me
->responseList
->targetTicks
) )
7610 dispatch_source_forget( &me
->responseTimer
);
7612 err
= DispatchTimerCreate( dispatch_time_milliseconds( me
->responseDelayMs
), DISPATCH_TIME_FOREVER
,
7613 ( (uint64_t) me
->responseDelayMs
) * kNanosecondsPerMillisecond
/ 10, me
->queue
, _DNSServerUDPDelayedSend
,
7614 NULL
, me
, &me
->responseTimer
);
7615 require_noerr( err
, exit
);
7616 dispatch_resume( me
->responseTimer
);
7619 SockAddrCopy( inDestAddr
, &newResponse
->destAddr
);
7620 newResponse
->targetTicks
= targetTicks
;
7621 newResponse
->msgPtr
= inMsgPtr
;
7622 newResponse
->msgLen
= inMsgLen
;
7624 for( responsePtr
= &me
->responseList
; ( response
= *responsePtr
) != NULL
; responsePtr
= &response
->next
)
7626 if( newResponse
->targetTicks
< response
->targetTicks
) break;
7628 newResponse
->next
= response
;
7629 *responsePtr
= newResponse
;
7634 if( newResponse
) _DNSDelayedResponseFree( newResponse
);
7638 static void _DNSServerUDPDelayedSend( void *inContext
)
7641 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7642 DNSDelayedResponse
* response
;
7646 uint64_t remainingNs
;
7647 DNSDelayedResponse
* freeList
= NULL
;
7649 dispatch_source_forget( &me
->responseTimer
);
7651 nowTicks
= UpTicks();
7652 while( ( ( response
= me
->responseList
) != NULL
) && ( response
->targetTicks
<= nowTicks
) )
7654 me
->responseList
= response
->next
;
7656 ds_ulog( kLogLevelInfo
, "UDP sending %zu byte response (delayed):\n\n%1{du:dnsmsg}",
7657 response
->msgLen
, response
->msgPtr
, response
->msgLen
);
7659 sock
= ( response
->destAddr
.sa
.sa_family
== AF_INET
) ? me
->sockUDPv4
: me
->sockUDPv6
;
7660 n
= sendto( sock
, (char *) response
->msgPtr
, response
->msgLen
, 0, &response
->destAddr
.sa
,
7661 SockAddrGetSize( &response
->destAddr
) );
7662 err
= map_socket_value_errno( sock
, n
== (ssize_t
) response
->msgLen
, n
);
7665 response
->next
= freeList
;
7666 freeList
= response
;
7667 nowTicks
= UpTicks();
7672 check( response
->targetTicks
> nowTicks
);
7673 remainingNs
= UpTicksToNanoseconds( response
->targetTicks
- nowTicks
);
7674 if( remainingNs
> INT64_MAX
) remainingNs
= INT64_MAX
;
7676 err
= DispatchTimerCreate( dispatch_time( DISPATCH_TIME_NOW
, (int64_t) remainingNs
), DISPATCH_TIME_FOREVER
, 0,
7677 me
->queue
, _DNSServerUDPDelayedSend
, NULL
, me
, &me
->responseTimer
);
7678 require_noerr( err
, exit
);
7679 dispatch_resume( me
->responseTimer
);
7683 if( freeList
) _DNSDelayedResponseFreeList( freeList
);
7686 //===========================================================================================================================
7687 // _DNSServerAnswerQuery
7688 //===========================================================================================================================
7690 #define kLabelPrefix_Alias "alias"
7691 #define kLabelPrefix_AliasTTL "alias-ttl"
7692 #define kLabelPrefix_Count "count-"
7693 #define kLabelPrefix_Tag "tag-"
7694 #define kLabelPrefix_TTL "ttl-"
7695 #define kLabel_IPv4 "ipv4"
7696 #define kLabel_IPv6 "ipv6"
7697 #define kLabelPrefix_SRV "srv-"
7699 #define kMaxAliasTTLCount ( ( kDomainLabelLengthMax - sizeof_string( kLabelPrefix_AliasTTL ) ) / 2 )
7700 #define kMaxParsedSRVCount ( kDomainNameLengthMax / ( 1 + sizeof_string( kLabelPrefix_SRV ) + 5 ) )
7704 uint16_t priority
; // Priority from SRV label.
7705 uint16_t weight
; // Weight from SRV label.
7706 uint16_t port
; // Port number from SRV label.
7707 uint16_t targetLen
; // Total length of the target hostname labels that follow an SRV label.
7708 const uint8_t * targetPtr
; // Pointer to the target hostname embedded in a domain name.
7713 _DNSServerInitializeResponseMessage(
7716 unsigned int inFlags
,
7717 const uint8_t * inQName
,
7718 unsigned int inQType
,
7719 unsigned int inQClass
);
7721 _DNSServerAnswerQueryDynamically(
7722 DNSServerRef inServer
,
7723 const uint8_t * inQName
,
7724 unsigned int inQType
,
7725 unsigned int inQClass
,
7727 DataBuffer
* inDB
);
7729 _DNSServerNameIsSRVName(
7730 DNSServerRef inServer
,
7731 const uint8_t * inName
,
7732 const uint8_t ** outDomainPtr
,
7733 size_t * outDomainLen
,
7734 ParsedSRV inSRVArray
[ kMaxParsedSRVCount
],
7735 size_t * outSRVCount
);
7737 _DNSServerNameIsHostname(
7738 DNSServerRef inServer
,
7739 const uint8_t * inName
,
7740 uint32_t * outAliasCount
,
7741 uint32_t inAliasTTLs
[ kMaxAliasTTLCount
],
7742 size_t * outAliasTTLCount
,
7743 unsigned int * outCount
,
7744 unsigned int * outRandCount
,
7747 Boolean
* outHasAAAA
,
7748 Boolean
* outHasSOA
);
7751 _DNSServerAnswerQuery(
7753 const uint8_t * const inQueryPtr
,
7754 const size_t inQueryLen
,
7756 uint8_t ** outResponsePtr
,
7757 size_t * outResponseLen
)
7761 const uint8_t * ptr
;
7762 const uint8_t * const queryEnd
= &inQueryPtr
[ inQueryLen
];
7763 const DNSHeader
* qhdr
;
7764 const dns_fixed_fields_question
* fields
;
7765 unsigned int msgID
, qflags
, qtype
, qclass
, rflags
;
7766 uint8_t qname
[ kDomainNameLengthMax
];
7768 DataBuffer_Init( &dataBuf
, NULL
, 0, kDNSMaxTCPMessageSize
);
7770 require_action_quiet( inQueryLen
>= kDNSHeaderLength
, exit
, err
= kUnderrunErr
);
7772 qhdr
= (const DNSHeader
*) inQueryPtr
;
7773 msgID
= DNSHeaderGetID( qhdr
);
7774 qflags
= DNSHeaderGetFlags( qhdr
);
7776 // Minimal checking of the query message's header.
7778 if( ( qflags
& kDNSHeaderFlag_Response
) || // The message must be a query, not a response.
7779 ( DNSFlagsGetOpCode( qflags
) != kDNSOpCode_Query
) || // OPCODE must be QUERY (standard query).
7780 ( DNSHeaderGetQuestionCount( qhdr
) != 1 ) ) // There should be a single question.
7788 ptr
= (const uint8_t *) &qhdr
[ 1 ];
7789 err
= DNSMessageExtractDomainName( inQueryPtr
, inQueryLen
, ptr
, qname
, &ptr
);
7790 require_noerr( err
, exit
);
7792 // Get QTYPE and QCLASS.
7794 require_action_quiet( ( (size_t)( queryEnd
- ptr
) ) >= sizeof( *fields
), exit
, err
= kUnderrunErr
);
7795 fields
= (const dns_fixed_fields_question
*) ptr
;
7796 qtype
= dns_fixed_fields_question_get_type( fields
);
7797 qclass
= dns_fixed_fields_question_get_class( fields
);
7799 // Create a tentative response message.
7801 rflags
= kDNSHeaderFlag_Response
;
7802 if( qflags
& kDNSHeaderFlag_RecursionDesired
) rflags
|= kDNSHeaderFlag_RecursionDesired
;
7803 DNSFlagsSetOpCode( rflags
, kDNSOpCode_Query
);
7805 if( me
->badUDPMode
&& !inForTCP
) msgID
= (uint16_t)( msgID
+ 1 );
7806 err
= _DNSServerInitializeResponseMessage( &dataBuf
, msgID
, rflags
, qname
, qtype
, qclass
);
7807 require_noerr( err
, exit
);
7809 err
= _DNSServerAnswerQueryDynamically( me
, qname
, qtype
, qclass
, inForTCP
, &dataBuf
);
7812 DNSFlagsSetRCode( rflags
, kDNSRCode_ServerFailure
);
7813 err
= _DNSServerInitializeResponseMessage( &dataBuf
, msgID
, rflags
, qname
, qtype
, qclass
);
7814 require_noerr( err
, exit
);
7817 err
= DataBuffer_Detach( &dataBuf
, outResponsePtr
, outResponseLen
);
7818 require_noerr( err
, exit
);
7821 DataBuffer_Free( &dataBuf
);
7826 _DNSServerInitializeResponseMessage(
7829 unsigned int inFlags
,
7830 const uint8_t * inQName
,
7831 unsigned int inQType
,
7832 unsigned int inQClass
)
7837 DataBuffer_Reset( inDB
);
7839 memset( &header
, 0, sizeof( header
) );
7840 DNSHeaderSetID( &header
, inID
);
7841 DNSHeaderSetFlags( &header
, inFlags
);
7842 DNSHeaderSetQuestionCount( &header
, 1 );
7844 err
= DataBuffer_Append( inDB
, &header
, sizeof( header
) );
7845 require_noerr( err
, exit
);
7847 err
= _DataBuffer_AppendDNSQuestion( inDB
, inQName
, DomainNameLength( inQName
), (uint16_t) inQType
,
7848 (uint16_t) inQClass
);
7849 require_noerr( err
, exit
);
7856 _DNSServerAnswerQueryDynamically(
7858 const uint8_t * const inQName
,
7859 const unsigned int inQType
,
7860 const unsigned int inQClass
,
7861 const Boolean inForTCP
,
7862 DataBuffer
* const inDB
)
7866 unsigned int flags
, rcode
;
7867 uint32_t aliasCount
, i
;
7868 uint32_t aliasTTLs
[ kMaxAliasTTLCount
];
7869 size_t aliasTTLCount
;
7870 unsigned int addrCount
, randCount
;
7872 ParsedSRV srvArray
[ kMaxParsedSRVCount
];
7874 const uint8_t * srvDomainPtr
;
7875 size_t srvDomainLen
;
7876 unsigned int answerCount
;
7877 Boolean notImplemented
, truncated
;
7878 Boolean useAliasTTLs
, nameExists
, nameHasA
, nameHasAAAA
, nameHasSRV
, nameHasSOA
;
7879 uint8_t namePtr
[ 2 ];
7880 dns_fixed_fields_record fields
;
7885 require_action_quiet( inQClass
== kDNSServiceClass_IN
, done
, notImplemented
= true );
7887 notImplemented
= false;
7890 nameHasAAAA
= false;
7892 useAliasTTLs
= false;
7897 if( _DNSServerNameIsHostname( me
, inQName
, &aliasCount
, aliasTTLs
, &aliasTTLCount
, &addrCount
, &randCount
, &ttl
,
7898 &nameHasA
, &nameHasAAAA
, &nameHasSOA
) )
7900 check( !( ( aliasCount
> 0 ) && ( aliasTTLCount
> 0 ) ) );
7901 check( ( addrCount
>= 1 ) && ( addrCount
<= 255 ) );
7902 check( ( randCount
== 0 ) || ( ( randCount
>= addrCount
) && ( randCount
<= 255 ) ) );
7903 check( nameHasA
|| nameHasAAAA
);
7905 if( aliasTTLCount
> 0 )
7907 aliasCount
= (uint32_t) aliasTTLCount
;
7908 useAliasTTLs
= true;
7912 else if( _DNSServerNameIsSRVName( me
, inQName
, &srvDomainPtr
, &srvDomainLen
, srvArray
, &srvCount
) )
7917 require_quiet( nameExists
, done
);
7919 if( aliasCount
> 0 )
7922 uint8_t rdataLabel
[ 1 + kDomainLabelLengthMax
+ 1 ];
7924 // If aliasCount is non-zero, then the first label of QNAME is either "alias" or "alias-<N>". superPtr is a name
7925 // compression pointer to the second label of QNAME, i.e., the immediate superdomain name of QNAME. It's used for
7926 // the RDATA of CNAME records whose canonical name ends with the superdomain name. It may also be used to construct
7927 // CNAME record names, when the offset to the previous CNAME's RDATA doesn't fit in a compression pointer.
7929 const uint8_t superPtr
[ 2 ] = { 0xC0, (uint8_t)( kDNSHeaderLength
+ 1 + inQName
[ 0 ] ) };
7931 // The name of the first CNAME record is equal to QNAME, so nameOffset is set to offset of QNAME.
7933 nameOffset
= kDNSHeaderLength
;
7935 for( i
= aliasCount
; i
>= 1; --i
)
7941 uint8_t nameLabel
[ 1 + kDomainLabelLengthMax
];
7943 if( nameOffset
<= kDNSCompressionOffsetMax
)
7945 DNSMessageWriteLabelPointer( namePtr
, nameOffset
);
7946 nameLen
= sizeof( namePtr
);
7950 memcpy( nameLabel
, rdataLabel
, 1 + rdataLabel
[ 0 ] );
7951 nameLen
= 1 + nameLabel
[ 0 ] + sizeof( superPtr
);
7956 char * dst
= (char *) &rdataLabel
[ 1 ];
7957 char * const lim
= (char *) &rdataLabel
[ countof( rdataLabel
) ];
7961 err
= SNPrintF_Add( &dst
, lim
, kLabelPrefix_AliasTTL
);
7962 require_noerr( err
, exit
);
7964 for( j
= aliasCount
- ( i
- 1 ); j
< aliasCount
; ++j
)
7966 err
= SNPrintF_Add( &dst
, lim
, "-%u", aliasTTLs
[ j
] );
7967 require_noerr( err
, exit
);
7972 err
= SNPrintF_Add( &dst
, lim
, kLabelPrefix_Alias
"%?{end}-%u", i
== 2, i
- 1 );
7973 require_noerr( err
, exit
);
7975 rdataLabel
[ 0 ] = (uint8_t)( dst
- (char *) &rdataLabel
[ 1 ] );
7976 rdataLen
= 1 + rdataLabel
[ 0 ] + sizeof( superPtr
);
7980 rdataLen
= sizeof( superPtr
);
7985 size_t recordLen
= nameLen
+ sizeof( fields
) + rdataLen
;
7987 if( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
)
7995 // Set CNAME record's NAME.
7997 if( nameOffset
<= kDNSCompressionOffsetMax
)
7999 err
= DataBuffer_Append( inDB
, namePtr
, sizeof( namePtr
) );
8000 require_noerr( err
, exit
);
8004 err
= DataBuffer_Append( inDB
, nameLabel
, 1 + nameLabel
[ 0 ] );
8005 require_noerr( err
, exit
);
8007 err
= DataBuffer_Append( inDB
, superPtr
, sizeof( superPtr
) );
8008 require_noerr( err
, exit
);
8011 // Set CNAME record's TYPE, CLASS, TTL, and RDLENGTH.
8013 aliasTTL
= useAliasTTLs
? aliasTTLs
[ aliasCount
- i
] : me
->defaultTTL
;
8014 dns_fixed_fields_record_init( &fields
, kDNSServiceType_CNAME
, kDNSServiceClass_IN
, aliasTTL
,
8015 (uint16_t) rdataLen
);
8016 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
8017 require_noerr( err
, exit
);
8019 // Save offset of CNAME record's RDATA, which may be used for the name of the next CNAME record.
8021 nameOffset
= DataBuffer_GetLen( inDB
);
8023 // Set CNAME record's RDATA.
8027 err
= DataBuffer_Append( inDB
, rdataLabel
, 1 + rdataLabel
[ 0 ] );
8028 require_noerr( err
, exit
);
8030 err
= DataBuffer_Append( inDB
, superPtr
, sizeof( superPtr
) );
8031 require_noerr( err
, exit
);
8034 namePtr
[ 0 ] = superPtr
[ 0 ];
8035 namePtr
[ 1 ] = superPtr
[ 1 ];
8039 // There are no aliases, so initialize the name compression pointer to point to QNAME.
8041 DNSMessageWriteLabelPointer( namePtr
, kDNSHeaderLength
);
8044 if( ( inQType
== kDNSServiceType_A
) || ( inQType
== kDNSServiceType_AAAA
) )
8046 uint8_t * lsb
; // Pointer to the least significant byte of record data.
8047 size_t recordLen
; // Length of the entire record.
8048 size_t rdataLen
; // Length of record's RDATA.
8049 uint8_t rdata
[ 16 ]; // A buffer that's big enough for either A or AAAA RDATA.
8050 uint8_t randIntegers
[ 255 ]; // Array for random integers in [1, 255].
8051 const int useBadAddrs
= ( me
->badUDPMode
&& !inForTCP
) ? true : false;
8053 if( inQType
== kDNSServiceType_A
)
8055 const uint32_t baseAddrV4
= useBadAddrs
? kDNSServerBadBaseAddrV4
: kDNSServerBaseAddrV4
;
8057 require_quiet( nameHasA
, done
);
8060 WriteBig32( rdata
, baseAddrV4
);
8065 const uint8_t * const baseAddrV6
= useBadAddrs
? kDNSServerBadBaseAddrV6
: kDNSServerBaseAddrV6
;
8067 require_quiet( nameHasAAAA
, done
);
8070 memcpy( rdata
, baseAddrV6
, 16 );
8076 // Populate the array with all integers between 1 and <randCount>, inclusive.
8078 for( i
= 0; i
< randCount
; ++i
) randIntegers
[ i
] = (uint8_t)( i
+ 1 );
8080 // Prevent dubious static analyzer warning.
8081 // Note: _DNSServerNameIsHostname() already enforces randCount >= addrCount. Also, this require_fatal() check
8082 // needs to be placed right before the next for-loop. Any earlier, and the static analyzer warning will persist
8085 require_fatal( addrCount
<= randCount
, "Invalid Count label values: addrCount %u > randCount %u",
8086 addrCount
, randCount
);
8088 // Create a contiguous subarray starting at index 0 that contains <addrCount> randomly chosen integers between
8089 // 1 and <randCount>, inclusive.
8090 // Loop invariant 1: Array elements with indexes in [0, i - 1] have been randomly chosen.
8091 // Loop invariant 2: Array elements with indexes in [i, randCount - 1] are candidates for being chosen.
8093 for( i
= 0; i
< addrCount
; ++i
)
8098 j
= RandomRange( i
, randCount
- 1 );
8101 tmp
= randIntegers
[ i
];
8102 randIntegers
[ i
] = randIntegers
[ j
];
8103 randIntegers
[ j
] = tmp
;
8108 recordLen
= sizeof( namePtr
) + sizeof( fields
) + rdataLen
;
8109 for( i
= 0; i
< addrCount
; ++i
)
8111 if( !inForTCP
&& ( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
) )
8119 err
= DataBuffer_Append( inDB
, namePtr
, sizeof( namePtr
) );
8120 require_noerr( err
, exit
);
8122 // Set record TYPE, CLASS, TTL, and RDLENGTH.
8124 dns_fixed_fields_record_init( &fields
, (uint16_t) inQType
, kDNSServiceClass_IN
, ttl
, (uint16_t) rdataLen
);
8125 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
8126 require_noerr( err
, exit
);
8128 // Set record RDATA.
8130 *lsb
= ( randCount
> 0 ) ? randIntegers
[ i
] : ( *lsb
+ 1 );
8132 err
= DataBuffer_Append( inDB
, rdata
, rdataLen
);
8133 require_noerr( err
, exit
);
8138 else if( inQType
== kDNSServiceType_SRV
)
8140 require_quiet( nameHasSRV
, done
);
8142 dns_fixed_fields_record_init( &fields
, kDNSServiceType_SRV
, kDNSServiceClass_IN
, me
->defaultTTL
, 0 );
8144 for( i
= 0; i
< srvCount
; ++i
)
8146 dns_fixed_fields_srv fieldsSRV
;
8149 const ParsedSRV
* const srv
= &srvArray
[ i
];
8151 rdataLen
= sizeof( fieldsSRV
) + srvDomainLen
+ srv
->targetLen
+ 1;
8152 recordLen
= sizeof( namePtr
) + sizeof( fields
) + rdataLen
;
8154 if( !inForTCP
&& ( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
) )
8160 // Append record NAME.
8162 err
= DataBuffer_Append( inDB
, namePtr
, sizeof( namePtr
) );
8163 require_noerr( err
, exit
);
8165 // Append record TYPE, CLASS, TTL, and RDLENGTH.
8167 WriteBig16( fields
.rdlength
, rdataLen
);
8168 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
8169 require_noerr( err
, exit
);
8171 // Append SRV RDATA.
8173 dns_fixed_fields_srv_init( &fieldsSRV
, srv
->priority
, srv
->weight
, srv
->port
);
8175 err
= DataBuffer_Append( inDB
, &fieldsSRV
, sizeof( fieldsSRV
) );
8176 require_noerr( err
, exit
);
8178 if( srv
->targetLen
> 0 )
8180 err
= DataBuffer_Append( inDB
, srv
->targetPtr
, srv
->targetLen
);
8181 require_noerr( err
, exit
);
8184 if( srvDomainLen
> 0 )
8186 err
= DataBuffer_Append( inDB
, srvDomainPtr
, srvDomainLen
);
8187 require_noerr( err
, exit
);
8190 err
= DataBuffer_Append( inDB
, "", 1 ); // Append root label.
8191 require_noerr( err
, exit
);
8196 else if( inQType
== kDNSServiceType_SOA
)
8198 size_t nameLen
, recordLen
;
8200 require_quiet( nameHasSOA
, done
);
8202 nameLen
= DomainNameLength( me
->domain
);
8205 err
= AppendSOARecord( NULL
, me
->domain
, nameLen
, 0, 0, 0, kRootLabel
, kRootLabel
, 0, 0, 0, 0, 0, &recordLen
);
8206 require_noerr( err
, exit
);
8208 if( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
)
8215 err
= AppendSOARecord( inDB
, me
->domain
, nameLen
, kDNSServiceType_SOA
, kDNSServiceClass_IN
, me
->defaultTTL
,
8216 kRootLabel
, kRootLabel
, me
->serial
, 1 * kSecondsPerDay
, 2 * kSecondsPerHour
, 1000 * kSecondsPerHour
,
8217 me
->defaultTTL
, NULL
);
8218 require_noerr( err
, exit
);
8224 hdr
= (DNSHeader
*) DataBuffer_GetPtr( inDB
);
8225 flags
= DNSHeaderGetFlags( hdr
);
8226 if( notImplemented
)
8228 rcode
= kDNSRCode_NotImplemented
;
8232 flags
|= kDNSHeaderFlag_AuthAnswer
;
8233 if( truncated
) flags
|= kDNSHeaderFlag_Truncation
;
8234 rcode
= nameExists
? kDNSRCode_NoError
: kDNSRCode_NXDomain
;
8236 DNSFlagsSetRCode( flags
, rcode
);
8237 DNSHeaderSetFlags( hdr
, flags
);
8238 DNSHeaderSetAnswerCount( hdr
, answerCount
);
8246 _DNSServerNameIsHostname(
8248 const uint8_t * inName
,
8249 uint32_t * outAliasCount
,
8250 uint32_t inAliasTTLs
[ kMaxAliasTTLCount
],
8251 size_t * outAliasTTLCount
,
8252 unsigned int * outCount
,
8253 unsigned int * outRandCount
,
8256 Boolean
* outHasAAAA
,
8257 Boolean
* outHasSOA
)
8260 const uint8_t * label
;
8261 const uint8_t * nextLabel
;
8262 uint32_t aliasCount
= 0; // Arg from Alias label. Valid values are in [2, 2^31 - 1].
8263 unsigned int count
= 0; // First arg from Count label. Valid values are in [1, 255].
8264 unsigned int randCount
= 0; // Second arg from Count label. Valid values are in [count, 255].
8265 int32_t ttl
= -1; // Arg from TTL label. Valid values are in [0, 2^31 - 1].
8266 size_t aliasTTLCount
= 0; // Count of TTL args from Alias-TTL label.
8267 int hasTagLabel
= false;
8268 int hasIPv4Label
= false;
8269 int hasIPv6Label
= false;
8270 int isNameValid
= false;
8272 for( label
= inName
; label
[ 0 ]; label
= nextLabel
)
8276 nextLabel
= &label
[ 1 + label
[ 0 ] ];
8278 // Check if the first label is a valid alias TTL sequence label.
8280 if( ( label
== inName
) && ( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_AliasTTL
) == 0 ) )
8282 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_AliasTTL
) ];
8283 const char * const end
= (const char *) nextLabel
;
8286 check( label
[ 0 ] <= kDomainLabelLengthMax
);
8290 if( *ptr
!= '-' ) break;
8292 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &next
);
8293 if( err
|| ( arg
> INT32_MAX
) ) break; // TTL must be in [0, 2^31 - 1].
8294 inAliasTTLs
[ aliasTTLCount
++ ] = arg
;
8297 if( ( aliasTTLCount
== 0 ) || ( ptr
!= end
) ) break;
8300 // Check if the first label is a valid alias label.
8302 else if( ( label
== inName
) && ( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_Alias
) == 0 ) )
8304 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_Alias
) ];
8305 const char * const end
= (const char *) nextLabel
;
8309 if( *ptr
++ != '-' ) break;
8310 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8311 if( err
|| ( arg
< 2 ) || ( arg
> INT32_MAX
) ) break; // Alias count must be in [2, 2^31 - 1].
8313 if( ptr
!= end
) break;
8321 // Check if this label is a valid count label.
8323 else if( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_Count
) == 0 )
8325 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_Count
) ];
8326 const char * const end
= (const char *) nextLabel
;
8328 if( count
> 0 ) break; // Count cannot be specified more than once.
8330 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8331 if( err
|| ( arg
< 1 ) || ( arg
> 255 ) ) break; // Count must be in [1, 255].
8332 count
= (unsigned int) arg
;
8336 if( *ptr
++ != '-' ) break;
8337 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8338 if( err
|| ( arg
< (uint32_t) count
) || ( arg
> 255 ) ) break; // Rand count must be in [count, 255].
8339 randCount
= (unsigned int) arg
;
8340 if( ptr
!= end
) break;
8344 // Check if this label is a valid TTL label.
8346 else if( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_TTL
) == 0 )
8348 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_TTL
) ];
8349 const char * const end
= (const char *) nextLabel
;
8351 if( ttl
>= 0 ) break; // TTL cannot be specified more than once.
8353 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8354 if( err
|| ( arg
> INT32_MAX
) ) break; // TTL must be in [0, 2^31 - 1].
8355 ttl
= (int32_t) arg
;
8356 if( ptr
!= end
) break;
8359 // Check if this label is a valid IPv4 label.
8361 else if( strnicmpx( &label
[ 1 ], label
[ 0 ], kLabel_IPv4
) == 0 )
8363 if( hasIPv4Label
|| hasIPv6Label
) break; // Valid names have at most one IPv4 or IPv6 label.
8364 hasIPv4Label
= true;
8367 // Check if this label is a valid IPv6 label.
8369 else if( strnicmpx( &label
[ 1 ], label
[ 0 ], kLabel_IPv6
) == 0 )
8371 if( hasIPv4Label
|| hasIPv6Label
) break; // Valid names have at most one IPv4 or IPv6 label.
8372 hasIPv6Label
= true;
8375 // Check if this label is a valid tag label.
8377 else if( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_Tag
) == 0 )
8382 // If this and the remaining labels are equal to "d.test.", then the name exists. Otherwise, this label is invalid.
8383 // In both cases, there are no more labels to check.
8387 if( DomainNameEqual( label
, me
->domain
) ) isNameValid
= true;
8391 require_quiet( isNameValid
, exit
);
8393 if( outAliasCount
) *outAliasCount
= aliasCount
;
8394 if( outAliasTTLCount
) *outAliasTTLCount
= aliasTTLCount
;
8395 if( outCount
) *outCount
= ( count
> 0 ) ? count
: 1;
8396 if( outRandCount
) *outRandCount
= randCount
;
8397 if( outTTL
) *outTTL
= ( ttl
>= 0 ) ? ( (uint32_t) ttl
) : me
->defaultTTL
;
8398 if( outHasA
) *outHasA
= ( hasIPv4Label
|| !hasIPv6Label
) ? true : false;
8399 if( outHasAAAA
) *outHasAAAA
= ( hasIPv6Label
|| !hasIPv4Label
) ? true : false;
8402 *outHasSOA
= ( !count
&& ( ttl
< 0 ) && !hasIPv4Label
&& !hasIPv6Label
&& !hasTagLabel
) ? true : false;
8406 return( isNameValid
? true : false );
8410 _DNSServerNameIsSRVName(
8412 const uint8_t * inName
,
8413 const uint8_t ** outDomainPtr
,
8414 size_t * outDomainLen
,
8415 ParsedSRV inSRVArray
[ kMaxParsedSRVCount
],
8416 size_t * outSRVCount
)
8419 const uint8_t * label
;
8420 const uint8_t * domainPtr
;
8424 int isNameValid
= false;
8428 // Ensure that first label, i.e, the service label, begins with a '_' character.
8430 require_quiet( ( label
[ 0 ] > 0 ) && ( label
[ 1 ] == '_' ), exit
);
8431 label
= DomainNameGetNextLabel( label
);
8433 // Ensure that the second label, i.e., the proto label, begins with a '_' character (usually _tcp or _udp).
8435 require_quiet( ( label
[ 0 ] > 0 ) && ( label
[ 1 ] == '_' ), exit
);
8436 label
= DomainNameGetNextLabel( label
);
8438 // Parse the domain name, if any.
8443 if( DomainNameEqual( label
, me
->domain
) ||
8444 ( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_SRV
) == 0 ) ) break;
8445 label
= DomainNameGetNextLabel( label
);
8447 require_quiet( *label
, exit
);
8449 domainLen
= (size_t)( label
- domainPtr
);
8451 // Parse SRV labels, if any.
8454 while( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_SRV
) == 0 )
8456 const uint8_t * const nextLabel
= DomainNameGetNextLabel( label
);
8457 const char * ptr
= (const char *) &label
[ 1 + sizeof_string( kLabelPrefix_SRV
) ];
8458 const char * const end
= (const char *) nextLabel
;
8459 const uint8_t * target
;
8460 unsigned int priority
, weight
, port
;
8462 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8463 require_quiet( !err
&& ( arg
<= UINT16_MAX
), exit
);
8464 priority
= (unsigned int) arg
;
8466 require_quiet( ( ptr
< end
) && ( *ptr
== '-' ), exit
);
8469 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8470 require_quiet( !err
&& ( arg
<= UINT16_MAX
), exit
);
8471 weight
= (unsigned int) arg
;
8473 require_quiet( ( ptr
< end
) && ( *ptr
== '-' ), exit
);
8476 err
= DecimalTextToUInt32( ptr
, end
, &arg
, &ptr
);
8477 require_quiet( !err
&& ( arg
<= UINT16_MAX
), exit
);
8478 port
= (unsigned int) arg
;
8480 require_quiet( ptr
== end
, exit
);
8483 for( label
= nextLabel
; *label
; label
= DomainNameGetNextLabel( label
) )
8485 if( DomainNameEqual( label
, me
->domain
) ||
8486 ( strnicmp_prefix( &label
[ 1 ], label
[ 0 ], kLabelPrefix_SRV
) == 0 ) ) break;
8488 require_quiet( *label
, exit
);
8492 inSRVArray
[ srvCount
].priority
= (uint16_t) priority
;
8493 inSRVArray
[ srvCount
].weight
= (uint16_t) weight
;
8494 inSRVArray
[ srvCount
].port
= (uint16_t) port
;
8495 inSRVArray
[ srvCount
].targetPtr
= target
;
8496 inSRVArray
[ srvCount
].targetLen
= (uint16_t)( label
- target
);
8500 require_quiet( DomainNameEqual( label
, me
->domain
), exit
);
8503 if( outDomainPtr
) *outDomainPtr
= domainPtr
;
8504 if( outDomainLen
) *outDomainLen
= domainLen
;
8505 if( outSRVCount
) *outSRVCount
= srvCount
;
8508 return( isNameValid
? true : false );
8511 //===========================================================================================================================
8512 // _DNSServerTCPReadHandler
8513 //===========================================================================================================================
8517 DNSServerRef server
; // Reference to DNS server object.
8518 sockaddr_ip clientAddr
; // Client's address.
8519 dispatch_source_t readSource
; // Dispatch read source for client socket.
8520 dispatch_source_t writeSource
; // Dispatch write source for client socket.
8521 size_t offset
; // Offset into receive buffer.
8522 void * msgPtr
; // Pointer to dynamically allocated message buffer.
8523 size_t msgLen
; // Length of message buffer.
8524 Boolean readSuspended
; // True if the read source is currently suspended.
8525 Boolean writeSuspended
; // True if the write source is currently suspended.
8526 Boolean receivedLength
; // True if receiving DNS message as opposed to the message length.
8527 uint8_t lenBuf
[ 2 ]; // Buffer for two-octet message length field.
8528 iovec_t iov
[ 2 ]; // IO vector for writing response message.
8529 iovec_t
* iovPtr
; // Vector pointer for SocketWriteData().
8530 int iovCount
; // Vector count for SocketWriteData().
8532 } TCPConnectionContext
;
8534 static void TCPConnectionStop( TCPConnectionContext
*inContext
);
8535 static void TCPConnectionContextFree( TCPConnectionContext
*inContext
);
8536 static void TCPConnectionReadHandler( void *inContext
);
8537 static void TCPConnectionWriteHandler( void *inContext
);
8539 #define TCPConnectionForget( X ) ForgetCustomEx( X, TCPConnectionStop, TCPConnectionContextFree )
8541 static void _DNSServerTCPReadHandler( void *inContext
)
8544 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
8545 DNSServerRef
const me
= (DNSServerRef
) sockCtx
->userContext
;
8546 TCPConnectionContext
* connection
;
8547 socklen_t clientAddrLen
;
8548 SocketRef newSock
= kInvalidSocketRef
;
8549 SocketContext
* newSockCtx
= NULL
;
8551 connection
= (TCPConnectionContext
*) calloc( 1, sizeof( *connection
) );
8552 require_action( connection
, exit
, err
= kNoMemoryErr
);
8555 connection
->server
= me
;
8557 clientAddrLen
= (socklen_t
) sizeof( connection
->clientAddr
);
8558 newSock
= accept( sockCtx
->sock
, &connection
->clientAddr
.sa
, &clientAddrLen
);
8559 err
= map_socket_creation_errno( newSock
);
8560 require_noerr( err
, exit
);
8562 err
= SocketContextCreate( newSock
, connection
, &newSockCtx
);
8563 require_noerr( err
, exit
);
8564 newSock
= kInvalidSocketRef
;
8566 err
= DispatchReadSourceCreate( newSockCtx
->sock
, me
->queue
, TCPConnectionReadHandler
, SocketContextCancelHandler
,
8567 newSockCtx
, &connection
->readSource
);
8568 require_noerr( err
, exit
);
8569 SocketContextRetain( newSockCtx
);
8570 dispatch_resume( connection
->readSource
);
8572 err
= DispatchWriteSourceCreate( newSockCtx
->sock
, me
->queue
, TCPConnectionWriteHandler
, SocketContextCancelHandler
,
8573 newSockCtx
, &connection
->writeSource
);
8574 require_noerr( err
, exit
);
8575 SocketContextRetain( newSockCtx
);
8576 connection
->writeSuspended
= true;
8580 ForgetSocket( &newSock
);
8581 SocketContextRelease( newSockCtx
);
8582 TCPConnectionForget( &connection
);
8585 //===========================================================================================================================
8586 // TCPConnectionStop
8587 //===========================================================================================================================
8589 static void TCPConnectionStop( TCPConnectionContext
*inContext
)
8591 dispatch_source_forget_ex( &inContext
->readSource
, &inContext
->readSuspended
);
8592 dispatch_source_forget_ex( &inContext
->writeSource
, &inContext
->writeSuspended
);
8595 //===========================================================================================================================
8596 // TCPConnectionContextFree
8597 //===========================================================================================================================
8599 static void TCPConnectionContextFree( TCPConnectionContext
*inContext
)
8601 check( !inContext
->readSource
);
8602 check( !inContext
->writeSource
);
8603 ForgetCF( &inContext
->server
);
8604 ForgetMem( &inContext
->msgPtr
);
8608 //===========================================================================================================================
8609 // TCPConnectionReadHandler
8610 //===========================================================================================================================
8612 static void TCPConnectionReadHandler( void *inContext
)
8615 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
8616 TCPConnectionContext
* connection
= (TCPConnectionContext
*) sockCtx
->userContext
;
8618 uint8_t * responsePtr
= NULL
; // malloc'd
8621 // Receive message length.
8623 if( !connection
->receivedLength
)
8625 err
= SocketReadData( sockCtx
->sock
, connection
->lenBuf
, sizeof( connection
->lenBuf
), &connection
->offset
);
8626 if( err
== EWOULDBLOCK
) goto exit
;
8627 require_noerr( err
, exit
);
8629 connection
->offset
= 0;
8630 connection
->msgLen
= ReadBig16( connection
->lenBuf
);
8631 connection
->msgPtr
= malloc( connection
->msgLen
);
8632 require_action( connection
->msgPtr
, exit
, err
= kNoMemoryErr
);
8633 connection
->receivedLength
= true;
8638 err
= SocketReadData( sockCtx
->sock
, connection
->msgPtr
, connection
->msgLen
, &connection
->offset
);
8639 if( err
== EWOULDBLOCK
) goto exit
;
8640 require_noerr( err
, exit
);
8642 gettimeofday( &now
, NULL
);
8643 dispatch_suspend( connection
->readSource
);
8644 connection
->readSuspended
= true;
8646 ds_ulog( kLogLevelInfo
, "TCP server received %zu bytes from %##a at %{du:time}.\n",
8647 connection
->msgLen
, &connection
->clientAddr
, &now
);
8649 if( connection
->msgLen
< kDNSHeaderLength
)
8651 ds_ulog( kLogLevelInfo
, "TCP DNS message is too small (%zu < %d).\n", connection
->msgLen
, kDNSHeaderLength
);
8655 ds_ulog( kLogLevelInfo
, "TCP received message:\n\n%1{du:dnsmsg}", connection
->msgPtr
, connection
->msgLen
);
8659 err
= _DNSServerAnswerQueryForTCP( connection
->server
, connection
->msgPtr
, connection
->msgLen
, &responsePtr
,
8661 require_noerr_quiet( err
, exit
);
8665 ds_ulog( kLogLevelInfo
, "TCP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen
, responsePtr
, responseLen
);
8667 free( connection
->msgPtr
);
8668 connection
->msgPtr
= responsePtr
;
8669 connection
->msgLen
= responseLen
;
8672 check( connection
->msgLen
<= UINT16_MAX
);
8673 WriteBig16( connection
->lenBuf
, connection
->msgLen
);
8674 connection
->iov
[ 0 ].iov_base
= connection
->lenBuf
;
8675 connection
->iov
[ 0 ].iov_len
= sizeof( connection
->lenBuf
);
8676 connection
->iov
[ 1 ].iov_base
= connection
->msgPtr
;
8677 connection
->iov
[ 1 ].iov_len
= connection
->msgLen
;
8679 connection
->iovPtr
= connection
->iov
;
8680 connection
->iovCount
= 2;
8682 check( connection
->writeSuspended
);
8683 dispatch_resume( connection
->writeSource
);
8684 connection
->writeSuspended
= false;
8687 FreeNullSafe( responsePtr
);
8688 if( err
&& ( err
!= EWOULDBLOCK
) ) TCPConnectionForget( &connection
);
8691 //===========================================================================================================================
8692 // TCPConnectionWriteHandler
8693 //===========================================================================================================================
8695 static void TCPConnectionWriteHandler( void *inContext
)
8698 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
8699 TCPConnectionContext
* connection
= (TCPConnectionContext
*) sockCtx
->userContext
;
8701 err
= SocketWriteData( sockCtx
->sock
, &connection
->iovPtr
, &connection
->iovCount
);
8702 if( err
== EWOULDBLOCK
) goto exit
;
8705 TCPConnectionForget( &connection
);
8711 //===========================================================================================================================
8713 //===========================================================================================================================
8717 uint8_t * hostname
; // Used as the base name for hostnames and service names.
8718 uint8_t * serviceLabel
; // Label containing the base service name.
8719 unsigned int maxInstanceCount
; // Maximum number of service instances and hostnames.
8720 uint64_t * bitmaps
; // Array of 64-bit bitmaps for keeping track of needed responses.
8721 size_t bitmapCount
; // Number of 64-bit bitmaps.
8722 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
8723 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
8724 uint32_t ifIndex
; // Index of the interface to run on.
8725 unsigned int recordCountA
; // Number of A records per hostname.
8726 unsigned int recordCountAAAA
; // Number of AAAA records per hostname.
8727 unsigned int maxDropCount
; // If > 0, the drop rates apply to only the first <maxDropCount> responses.
8728 double ucastDropRate
; // Probability of dropping a unicast response.
8729 double mcastDropRate
; // Probability of dropping a multicast query or response.
8730 uint8_t * dropCounters
; // If maxDropCount > 0, array of <maxInstanceCount> response drop counters.
8731 Boolean noAdditionals
; // True if responses are to not include additional records.
8732 Boolean useIPv4
; // True if the replier is to use IPv4.
8733 Boolean useIPv6
; // True if the replier is to use IPv6.
8734 uint8_t msgBuf
[ kMDNSMessageSizeMax
]; // Buffer for received mDNS message.
8735 #if( TARGET_OS_DARWIN )
8736 dispatch_source_t processMonitor
; // Process monitor source for process being followed, if any.
8737 pid_t followPID
; // PID of process being followed, if any. (If it exits, we exit).
8740 } MDNSReplierContext
;
8742 typedef struct MRResourceRecord MRResourceRecord
;
8743 struct MRResourceRecord
8745 MRResourceRecord
* next
; // Next item in list.
8746 uint8_t * name
; // Resource record name.
8747 uint16_t type
; // Resource record type.
8748 uint16_t class; // Resource record class.
8749 uint32_t ttl
; // Resource record TTL.
8750 uint16_t rdlength
; // Resource record data length.
8751 uint8_t * rdata
; // Resource record data.
8752 const uint8_t * target
; // For SRV records, pointer to target in RDATA.
8755 typedef struct MRNameOffsetItem MRNameOffsetItem
;
8756 struct MRNameOffsetItem
8758 MRNameOffsetItem
* next
; // Next item in list.
8759 uint16_t offset
; // Offset of domain name in response message.
8760 uint8_t name
[ 1 ]; // Variable-length array for domain name.
8763 #if( TARGET_OS_DARWIN )
8764 static void _MDNSReplierFollowedProcessHandler( void *inContext
);
8766 static void _MDNSReplierReadHandler( void *inContext
);
8768 _MDNSReplierAnswerQuery(
8769 MDNSReplierContext
* inContext
,
8770 const uint8_t * inQueryPtr
,
8772 sockaddr_ip
* inSender
,
8774 unsigned int inIndex
);
8776 _MDNSReplierAnswerListAdd(
8777 MDNSReplierContext
* inContext
,
8778 MRResourceRecord
** inAnswerList
,
8779 unsigned int inIndex
,
8780 const uint8_t * inName
,
8781 unsigned int inType
,
8782 unsigned int inClass
);
8784 _MDNSReplierAnswerListRemovePTR(
8785 MRResourceRecord
** inAnswerListPtr
,
8786 const uint8_t * inName
,
8787 const uint8_t * inRData
);
8789 _MDNSReplierSendOrDropResponse(
8790 MDNSReplierContext
* inContext
,
8791 MRResourceRecord
* inAnswerList
,
8792 sockaddr_ip
* inQuerier
,
8794 unsigned int inIndex
,
8795 Boolean inUnicast
);
8797 _MDNSReplierCreateResponse(
8798 MDNSReplierContext
* inContext
,
8799 MRResourceRecord
* inAnswerList
,
8800 unsigned int inIndex
,
8801 uint8_t ** outResponsePtr
,
8802 size_t * outResponseLen
);
8804 _MDNSReplierAppendNameToResponse(
8805 DataBuffer
* inResponse
,
8806 const uint8_t * inName
,
8807 MRNameOffsetItem
** inNameOffsetListPtr
);
8809 _MDNSReplierServiceTypeMatch(
8810 const MDNSReplierContext
* inContext
,
8811 const uint8_t * inName
,
8812 unsigned int * outTXTSize
,
8813 unsigned int * outCount
);
8815 _MDNSReplierServiceInstanceNameMatch(
8816 const MDNSReplierContext
* inContext
,
8817 const uint8_t * inName
,
8818 unsigned int * outIndex
,
8819 unsigned int * outTXTSize
,
8820 unsigned int * outCount
);
8821 static Boolean
_MDNSReplierAboutRecordNameMatch( const MDNSReplierContext
*inContext
, const uint8_t *inName
);
8823 _MDNSReplierHostnameMatch(
8824 const MDNSReplierContext
* inContext
,
8825 const uint8_t * inName
,
8826 unsigned int * outIndex
);
8827 static OSStatus
_MDNSReplierCreateTXTRecord( const uint8_t *inRecordName
, size_t inSize
, uint8_t **outTXT
);
8829 _MRResourceRecordCreate(
8834 uint16_t inRDLength
,
8836 MRResourceRecord
** outRecord
);
8837 static void _MRResourceRecordFree( MRResourceRecord
*inRecord
);
8838 static void _MRResourceRecordFreeList( MRResourceRecord
*inList
);
8839 static OSStatus
_MRNameOffsetItemCreate( const uint8_t *inName
, uint16_t inOffset
, MRNameOffsetItem
**outItem
);
8840 static void _MRNameOffsetItemFree( MRNameOffsetItem
*inItem
);
8841 static void _MRNameOffsetItemFreeList( MRNameOffsetItem
*inList
);
8843 ulog_define_ex( kDNSSDUtilIdentifier
, MDNSReplier
, kLogLevelInfo
, kLogFlags_None
, "MDNSReplier", NULL
);
8844 #define mr_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSReplier ), (LEVEL), __VA_ARGS__ )
8846 static void MDNSReplierCmd( void )
8849 MDNSReplierContext
* context
;
8850 SocketRef sockV4
= kInvalidSocketRef
;
8851 SocketRef sockV6
= kInvalidSocketRef
;
8852 const char * ifname
;
8854 uint8_t name
[ 1 + kDomainLabelLengthMax
+ 1 ];
8855 char ifnameBuf
[ IF_NAMESIZE
+ 1 ];
8857 err
= CheckIntegerArgument( gMDNSReplier_MaxInstanceCount
, "max instance count", 1, UINT16_MAX
);
8858 require_noerr_quiet( err
, exit
);
8860 err
= CheckIntegerArgument( gMDNSReplier_RecordCountA
, "A record count", 0, 255 );
8861 require_noerr_quiet( err
, exit
);
8863 err
= CheckIntegerArgument( gMDNSReplier_RecordCountAAAA
, "AAAA record count", 0, 255 );
8864 require_noerr_quiet( err
, exit
);
8866 err
= CheckDoubleArgument( gMDNSReplier_UnicastDropRate
, "unicast drop rate", 0.0, 1.0 );
8867 require_noerr_quiet( err
, exit
);
8869 err
= CheckDoubleArgument( gMDNSReplier_MulticastDropRate
, "multicast drop rate", 0.0, 1.0 );
8870 require_noerr_quiet( err
, exit
);
8872 err
= CheckIntegerArgument( gMDNSReplier_MaxDropCount
, "drop count", 0, 255 );
8873 require_noerr_quiet( err
, exit
);
8875 if( gMDNSReplier_Foreground
)
8877 LogControl( "MDNSReplier:output=file;stdout,MDNSReplier:flags=time;prefix" );
8880 context
= (MDNSReplierContext
*) calloc( 1, sizeof( *context
) );
8881 require_action( context
, exit
, err
= kNoMemoryErr
);
8883 context
->maxInstanceCount
= (unsigned int) gMDNSReplier_MaxInstanceCount
;
8884 context
->recordCountA
= (unsigned int) gMDNSReplier_RecordCountA
;
8885 context
->recordCountAAAA
= (unsigned int) gMDNSReplier_RecordCountAAAA
;
8886 context
->maxDropCount
= (unsigned int) gMDNSReplier_MaxDropCount
;
8887 context
->ucastDropRate
= gMDNSReplier_UnicastDropRate
;
8888 context
->mcastDropRate
= gMDNSReplier_MulticastDropRate
;
8889 context
->noAdditionals
= gMDNSReplier_NoAdditionals
? true : false;
8890 context
->useIPv4
= ( gMDNSReplier_UseIPv4
|| !gMDNSReplier_UseIPv6
) ? true : false;
8891 context
->useIPv6
= ( gMDNSReplier_UseIPv6
|| !gMDNSReplier_UseIPv4
) ? true : false;
8892 context
->bitmapCount
= ( context
->maxInstanceCount
+ 63 ) / 64;
8894 #if( TARGET_OS_DARWIN )
8895 if( gMDNSReplier_FollowPID
)
8897 context
->followPID
= _StringToPID( gMDNSReplier_FollowPID
, &err
);
8898 if( err
|| ( context
->followPID
< 0 ) )
8900 FPrintF( stderr
, "error: Invalid follow PID: %s\n", gMDNSReplier_FollowPID
);
8904 err
= DispatchProcessMonitorCreate( context
->followPID
, DISPATCH_PROC_EXIT
, dispatch_get_main_queue(),
8905 _MDNSReplierFollowedProcessHandler
, NULL
, context
, &context
->processMonitor
);
8906 require_noerr( err
, exit
);
8907 dispatch_resume( context
->processMonitor
);
8911 context
->followPID
= -1;
8915 if( context
->maxDropCount
> 0 )
8917 context
->dropCounters
= (uint8_t *) calloc( context
->maxInstanceCount
, sizeof( *context
->dropCounters
) );
8918 require_action( context
->dropCounters
, exit
, err
= kNoMemoryErr
);
8921 context
->bitmaps
= (uint64_t *) calloc( context
->bitmapCount
, sizeof( *context
->bitmaps
) );
8922 require_action( context
->bitmaps
, exit
, err
= kNoMemoryErr
);
8924 // Create the base hostname label.
8926 len
= strlen( gMDNSReplier_Hostname
);
8927 if( context
->maxInstanceCount
> 1 )
8929 unsigned int maxInstanceCount
, digitCount
;
8931 // When there's more than one instance, extra bytes are needed to append " (<instance index>)" or
8932 // "-<instance index>" to the base hostname.
8934 maxInstanceCount
= context
->maxInstanceCount
;
8935 for( digitCount
= 0; maxInstanceCount
> 0; ++digitCount
) maxInstanceCount
/= 10;
8936 len
+= ( 3 + digitCount
);
8939 if( len
<= kDomainLabelLengthMax
)
8941 uint8_t * dst
= &name
[ 1 ];
8942 uint8_t * lim
= &name
[ countof( name
) ];
8944 SNPrintF_Add( (char **) &dst
, (char *) lim
, "%s", gMDNSReplier_Hostname
);
8945 name
[ 0 ] = (uint8_t)( dst
- &name
[ 1 ] );
8947 err
= DomainNameDupLower( name
, &context
->hostname
, NULL
);
8948 require_noerr( err
, exit
);
8952 FPrintF( stderr
, "error: Base name \"%s\" is too long for max instance count of %u.\n",
8953 gMDNSReplier_Hostname
, context
->maxInstanceCount
);
8957 // Create the service label.
8959 len
= strlen( gMDNSReplier_ServiceTypeTag
) + 3; // We need three extra bytes for the service type prefix "_t-".
8960 if( len
<= kDomainLabelLengthMax
)
8962 uint8_t * dst
= &name
[ 1 ];
8963 uint8_t * lim
= &name
[ countof( name
) ];
8965 SNPrintF_Add( (char **) &dst
, (char *) lim
, "_t-%s", gMDNSReplier_ServiceTypeTag
);
8966 name
[ 0 ] = (uint8_t)( dst
- &name
[ 1 ] );
8968 err
= DomainNameDupLower( name
, &context
->serviceLabel
, NULL
);
8969 require_noerr( err
, exit
);
8973 FPrintF( stderr
, "error: Service type tag is too long.\n" );
8977 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
8978 require_noerr_quiet( err
, exit
);
8980 ifname
= if_indextoname( context
->ifIndex
, ifnameBuf
);
8981 require_action( ifname
, exit
, err
= kNameErr
);
8983 // Set up IPv4 socket.
8985 if( context
->useIPv4
)
8987 err
= CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort
, ifname
, context
->ifIndex
, true, NULL
, &sockV4
);
8988 require_noerr( err
, exit
);
8991 // Set up IPv6 socket.
8993 if( context
->useIPv6
)
8995 err
= CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort
, ifname
, context
->ifIndex
, true, NULL
, &sockV6
);
8996 require_noerr( err
, exit
);
8999 // Create dispatch read sources for socket(s).
9001 if( IsValidSocket( sockV4
) )
9003 SocketContext
* sockCtx
;
9005 err
= SocketContextCreate( sockV4
, context
, &sockCtx
);
9006 require_noerr( err
, exit
);
9007 sockV4
= kInvalidSocketRef
;
9009 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, _MDNSReplierReadHandler
, SocketContextCancelHandler
, sockCtx
,
9010 &context
->readSourceV4
);
9011 if( err
) ForgetSocketContext( &sockCtx
);
9012 require_noerr( err
, exit
);
9014 dispatch_resume( context
->readSourceV4
);
9017 if( IsValidSocket( sockV6
) )
9019 SocketContext
* sockCtx
;
9021 err
= SocketContextCreate( sockV6
, context
, &sockCtx
);
9022 require_noerr( err
, exit
);
9023 sockV6
= kInvalidSocketRef
;
9025 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, _MDNSReplierReadHandler
, SocketContextCancelHandler
, sockCtx
,
9026 &context
->readSourceV6
);
9027 if( err
) ForgetSocketContext( &sockCtx
);
9028 require_noerr( err
, exit
);
9030 dispatch_resume( context
->readSourceV6
);
9036 ForgetSocket( &sockV4
);
9037 ForgetSocket( &sockV6
);
9041 #if( TARGET_OS_DARWIN )
9042 //===========================================================================================================================
9043 // _MDNSReplierFollowedProcessHandler
9044 //===========================================================================================================================
9046 static void _MDNSReplierFollowedProcessHandler( void *inContext
)
9048 MDNSReplierContext
* const context
= (MDNSReplierContext
*) inContext
;
9050 if( dispatch_source_get_data( context
->processMonitor
) & DISPATCH_PROC_EXIT
)
9052 mr_ulog( kLogLevelNotice
, "Exiting: followed process (%lld) exited.\n", (int64_t) context
->followPID
);
9058 //===========================================================================================================================
9059 // _MDNSReplierReadHandler
9060 //===========================================================================================================================
9062 #define ShouldDrop( P ) ( ( (P) > 0.0 ) && ( ( (P) >= 1.0 ) || RandomlyTrue( P ) ) )
9064 static void _MDNSReplierReadHandler( void *inContext
)
9067 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
9068 MDNSReplierContext
* const context
= (MDNSReplierContext
*) sockCtx
->userContext
;
9071 const DNSHeader
* hdr
;
9072 unsigned int flags
, questionCount
, i
, j
;
9073 const uint8_t * ptr
;
9074 int drop
, isMetaQuery
;
9076 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &msgLen
, &sender
, sizeof( sender
),
9077 NULL
, NULL
, NULL
, NULL
);
9078 require_noerr( err
, exit
);
9080 if( msgLen
< kDNSHeaderLength
)
9082 mr_ulog( kLogLevelInfo
, "Message is too small (%zu < %d).\n", msgLen
, kDNSHeaderLength
);
9086 // Perform header field checks.
9087 // The message ID and most flag bits are silently ignored (see <https://tools.ietf.org/html/rfc6762#section-18>).
9089 hdr
= (DNSHeader
*) context
->msgBuf
;
9090 flags
= DNSHeaderGetFlags( hdr
);
9091 require_quiet( ( flags
& kDNSHeaderFlag_Response
) == 0, exit
); // Reject responses.
9092 require_quiet( DNSFlagsGetOpCode( flags
) == kDNSOpCode_Query
, exit
); // Reject opcodes other than standard query.
9093 require_quiet( DNSFlagsGetRCode( flags
) == kDNSRCode_NoError
, exit
); // Reject non-zero rcodes.
9095 drop
= ( !context
->maxDropCount
&& ShouldDrop( context
->mcastDropRate
) ) ? true : false;
9097 mr_ulog( kLogLevelInfo
, "Received %zu byte message from %##a%?s:\n\n%#1{du:dnsmsg}",
9098 msgLen
, &sender
, drop
, " (dropping)", context
->msgBuf
, msgLen
);
9100 // Based on the QNAMEs in the query message, determine from which sets of records we may possibly need answers.
9102 questionCount
= DNSHeaderGetQuestionCount( hdr
);
9103 require_quiet( questionCount
> 0, exit
);
9105 memset( context
->bitmaps
, 0, context
->bitmapCount
* sizeof_element( context
->bitmaps
) );
9107 isMetaQuery
= false;
9108 ptr
= (const uint8_t *) &hdr
[ 1 ];
9109 for( i
= 0; i
< questionCount
; ++i
)
9111 unsigned int count
, index
;
9112 uint16_t qtype
, qclass
;
9113 uint8_t qname
[ kDomainNameLengthMax
];
9115 err
= DNSMessageExtractQuestion( context
->msgBuf
, msgLen
, ptr
, qname
, &qtype
, &qclass
, &ptr
);
9116 require_noerr_quiet( err
, exit
);
9118 if( ( qclass
& ~kQClassUnicastResponseBit
) != kDNSServiceClass_IN
) continue;
9120 if( _MDNSReplierHostnameMatch( context
, qname
, &index
) ||
9121 _MDNSReplierServiceInstanceNameMatch( context
, qname
, &index
, NULL
, NULL
) )
9123 if( ( index
>= 1 ) && ( index
<= context
->maxInstanceCount
) )
9125 context
->bitmaps
[ ( index
- 1 ) / 64 ] |= ( UINT64_C( 1 ) << ( ( index
- 1 ) % 64 ) );
9128 else if( _MDNSReplierServiceTypeMatch( context
, qname
, NULL
, &count
) )
9130 if( ( count
>= 1 ) && ( count
<= context
->maxInstanceCount
) )
9132 for( j
= 0; j
< (unsigned int) context
->bitmapCount
; ++j
)
9136 context
->bitmaps
[ j
] |= ( ( UINT64_C( 1 ) << count
) - 1 );
9141 context
->bitmaps
[ j
] = ~UINT64_C( 0 );
9147 else if( _MDNSReplierAboutRecordNameMatch( context
, qname
) )
9153 // Attempt to answer the query message using selected record sets.
9157 err
= _MDNSReplierAnswerQuery( context
, context
->msgBuf
, msgLen
, &sender
, sockCtx
->sock
, 0 );
9160 if( drop
) goto exit
;
9162 for( i
= 0; i
< context
->bitmapCount
; ++i
)
9164 for( j
= 0; ( context
->bitmaps
[ i
] != 0 ) && ( j
< 64 ); ++j
)
9166 const uint64_t bitmask
= UINT64_C( 1 ) << j
;
9168 if( context
->bitmaps
[ i
] & bitmask
)
9170 context
->bitmaps
[ i
] &= ~bitmask
;
9172 err
= _MDNSReplierAnswerQuery( context
, context
->msgBuf
, msgLen
, &sender
, sockCtx
->sock
,
9173 ( i
* 64 ) + j
+ 1 );
9183 //===========================================================================================================================
9184 // _MDNSReplierAnswerQuery
9185 //===========================================================================================================================
9188 _MDNSReplierAnswerQuery(
9189 MDNSReplierContext
* inContext
,
9190 const uint8_t * inQueryPtr
,
9192 sockaddr_ip
* inSender
,
9194 unsigned int inIndex
)
9197 const DNSHeader
* hdr
;
9198 const uint8_t * ptr
;
9199 unsigned int questionCount
, answerCount
, i
;
9200 MRResourceRecord
* ucastAnswerList
= NULL
;
9201 MRResourceRecord
* mcastAnswerList
= NULL
;
9203 require_action( inIndex
<= inContext
->maxInstanceCount
, exit
, err
= kRangeErr
);
9205 // Get answers for questions.
9207 check( inQueryLen
>= kDNSHeaderLength
);
9208 hdr
= (const DNSHeader
*) inQueryPtr
;
9209 questionCount
= DNSHeaderGetQuestionCount( hdr
);
9211 ptr
= (const uint8_t *) &hdr
[ 1 ];
9212 for( i
= 0; i
< questionCount
; ++i
)
9214 MRResourceRecord
** answerListPtr
;
9215 uint16_t qtype
, qclass
;
9216 uint8_t qname
[ kDomainNameLengthMax
];
9218 err
= DNSMessageExtractQuestion( inQueryPtr
, inQueryLen
, ptr
, qname
, &qtype
, &qclass
, &ptr
);
9219 require_noerr_quiet( err
, exit
);
9221 if( qclass
& kQClassUnicastResponseBit
)
9223 qclass
&= ~kQClassUnicastResponseBit
;
9224 answerListPtr
= &ucastAnswerList
;
9228 answerListPtr
= &mcastAnswerList
;
9231 err
= _MDNSReplierAnswerListAdd( inContext
, answerListPtr
, inIndex
, qname
, qtype
, qclass
);
9232 require_noerr( err
, exit
);
9234 require_action_quiet( mcastAnswerList
|| ucastAnswerList
, exit
, err
= kNoErr
);
9236 // Suppress known answers.
9237 // Records in the Answer section of the query message are known answers, so remove them from the answer lists.
9238 // See <https://tools.ietf.org/html/rfc6762#section-7.1>.
9240 answerCount
= DNSHeaderGetAnswerCount( hdr
);
9241 for( i
= 0; i
< answerCount
; ++i
)
9243 const uint8_t * rdataPtr
;
9244 const uint8_t * recordPtr
;
9245 uint16_t type
, class;
9246 uint8_t name
[ kDomainNameLengthMax
];
9247 uint8_t instance
[ kDomainNameLengthMax
];
9250 err
= DNSMessageExtractRecord( inQueryPtr
, inQueryLen
, ptr
, NULL
, &type
, &class, NULL
, NULL
, NULL
, &ptr
);
9251 require_noerr_quiet( err
, exit
);
9253 if( ( type
!= kDNSServiceType_PTR
) || ( class != kDNSServiceClass_IN
) ) continue;
9255 err
= DNSMessageExtractRecord( inQueryPtr
, inQueryLen
, recordPtr
, name
, NULL
, NULL
, NULL
, &rdataPtr
, NULL
, NULL
);
9256 require_noerr( err
, exit
);
9258 err
= DNSMessageExtractDomainName( inQueryPtr
, inQueryLen
, rdataPtr
, instance
, NULL
);
9259 require_noerr_quiet( err
, exit
);
9261 if( ucastAnswerList
) _MDNSReplierAnswerListRemovePTR( &ucastAnswerList
, name
, instance
);
9262 if( mcastAnswerList
) _MDNSReplierAnswerListRemovePTR( &mcastAnswerList
, name
, instance
);
9264 require_action_quiet( mcastAnswerList
|| ucastAnswerList
, exit
, err
= kNoErr
);
9266 // Send or drop responses.
9268 if( ucastAnswerList
)
9270 err
= _MDNSReplierSendOrDropResponse( inContext
, ucastAnswerList
, inSender
, inSock
, inIndex
, true );
9271 require_noerr( err
, exit
);
9274 if( mcastAnswerList
)
9276 err
= _MDNSReplierSendOrDropResponse( inContext
, mcastAnswerList
, inSender
, inSock
, inIndex
, false );
9277 require_noerr( err
, exit
);
9282 _MRResourceRecordFreeList( ucastAnswerList
);
9283 _MRResourceRecordFreeList( mcastAnswerList
);
9287 //===========================================================================================================================
9288 // _MDNSReplierAnswerListAdd
9289 //===========================================================================================================================
9292 _MDNSReplierAnswerListAdd(
9293 MDNSReplierContext
* inContext
,
9294 MRResourceRecord
** inAnswerList
,
9295 unsigned int inIndex
,
9296 const uint8_t * inName
,
9297 unsigned int inType
,
9298 unsigned int inClass
)
9301 uint8_t * recordName
= NULL
;
9302 uint8_t * rdataPtr
= NULL
;
9304 MRResourceRecord
* answer
;
9305 MRResourceRecord
** answerPtr
;
9306 const uint8_t * const hostname
= inContext
->hostname
;
9309 unsigned int count
, txtSize
;
9311 require_action( inIndex
<= inContext
->maxInstanceCount
, exit
, err
= kRangeErr
);
9312 require_action_quiet( inClass
== kDNSServiceClass_IN
, exit
, err
= kNoErr
);
9314 for( answerPtr
= inAnswerList
; ( answer
= *answerPtr
) != NULL
; answerPtr
= &answer
->next
)
9316 if( ( answer
->type
== inType
) && DomainNameEqual( answer
->name
, inName
) )
9323 // Index 0 is reserved for answering queries about the mdnsreplier, while all other index values up to the maximum
9324 // instance count are for answering queries about service instances.
9328 if( _MDNSReplierAboutRecordNameMatch( inContext
, inName
) )
9330 int listHasTXT
= false;
9332 if( inType
== kDNSServiceType_ANY
)
9334 for( answer
= *inAnswerList
; answer
; answer
= answer
->next
)
9336 if( ( answer
->type
== kDNSServiceType_TXT
) && DomainNameEqual( answer
->name
, inName
) )
9344 if( ( inType
== kDNSServiceType_TXT
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasTXT
) )
9346 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9347 require_noerr( err
, exit
);
9349 err
= CreateTXTRecordDataFromString( "ready=yes", ',', &rdataPtr
, &rdataLen
);
9350 require_noerr( err
, exit
);
9352 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
, kMDNSRecordTTL_Other
,
9353 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9354 require_noerr( err
, exit
);
9358 *answerPtr
= answer
;
9360 else if( inType
== kDNSServiceType_NSEC
)
9362 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9363 require_noerr( err
, exit
);
9365 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 1, kDNSServiceType_TXT
);
9366 require_noerr( err
, exit
);
9368 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_NSEC
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9369 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9370 require_noerr( err
, exit
);
9374 *answerPtr
= answer
;
9378 else if( _MDNSReplierHostnameMatch( inContext
, inName
, &index
) && ( index
== inIndex
) )
9380 int listHasA
= false;
9381 int listHasAAAA
= false;
9383 if( inType
== kDNSServiceType_ANY
)
9385 for( answer
= *inAnswerList
; answer
; answer
= answer
->next
)
9387 if( answer
->type
== kDNSServiceType_A
)
9389 if( !listHasA
&& DomainNameEqual( answer
->name
, inName
) ) listHasA
= true;
9391 else if( answer
->type
== kDNSServiceType_AAAA
)
9393 if( !listHasAAAA
&& DomainNameEqual( answer
->name
, inName
) ) listHasAAAA
= true;
9395 if( listHasA
&& listHasAAAA
) break;
9399 if( ( inType
== kDNSServiceType_A
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasA
) )
9401 for( i
= 1; i
<= inContext
->recordCountA
; ++i
)
9403 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9404 require_noerr( err
, exit
);
9407 rdataPtr
= (uint8_t *) malloc( rdataLen
);
9408 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9411 WriteBig16( &rdataPtr
[ 1 ], inIndex
);
9412 rdataPtr
[ 3 ] = (uint8_t) i
;
9414 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_A
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9415 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9416 require_noerr( err
, exit
);
9420 *answerPtr
= answer
;
9421 answerPtr
= &answer
->next
;
9425 if( ( inType
== kDNSServiceType_AAAA
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasAAAA
) )
9427 for( i
= 1; i
<= inContext
->recordCountAAAA
; ++i
)
9429 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9430 require_noerr( err
, exit
);
9433 rdataPtr
= (uint8_t *) _memdup( kMDNSReplierBaseAddrV6
, rdataLen
);
9434 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9436 WriteBig16( &rdataPtr
[ 12 ], inIndex
);
9437 rdataPtr
[ 15 ] = (uint8_t) i
;
9439 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_AAAA
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9440 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9441 require_noerr( err
, exit
);
9445 *answerPtr
= answer
;
9446 answerPtr
= &answer
->next
;
9449 else if( inType
== kDNSServiceType_NSEC
)
9451 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9452 require_noerr( err
, exit
);
9454 if( ( inContext
->recordCountA
> 0 ) && ( inContext
->recordCountAAAA
> 0 ) )
9456 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 2, kDNSServiceType_A
, kDNSServiceType_AAAA
);
9457 require_noerr( err
, exit
);
9459 else if( inContext
->recordCountA
> 0 )
9461 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 1, kDNSServiceType_A
);
9462 require_noerr( err
, exit
);
9464 else if( inContext
->recordCountAAAA
> 0 )
9466 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 1, kDNSServiceType_AAAA
);
9467 require_noerr( err
, exit
);
9471 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 0 );
9472 require_noerr( err
, exit
);
9475 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_NSEC
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9476 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9477 require_noerr( err
, exit
);
9481 *answerPtr
= answer
;
9484 else if( _MDNSReplierServiceTypeMatch( inContext
, inName
, NULL
, &count
) && ( count
>= inIndex
) )
9486 int listHasPTR
= false;
9488 if( inType
== kDNSServiceType_ANY
)
9490 for( answer
= *inAnswerList
; answer
; answer
= answer
->next
)
9492 if( ( answer
->type
== kDNSServiceType_PTR
) && DomainNameEqual( answer
->name
, inName
) )
9500 if( ( inType
== kDNSServiceType_PTR
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasPTR
) )
9502 size_t recordNameLen
;
9506 err
= DomainNameDupLower( inName
, &recordName
, &recordNameLen
);
9507 require_noerr( err
, exit
);
9509 rdataLen
= 1 + hostname
[ 0 ] + 10 + recordNameLen
;
9510 rdataPtr
= (uint8_t *) malloc( rdataLen
);
9511 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9513 lim
= &rdataPtr
[ rdataLen
];
9515 ptr
= &rdataPtr
[ 1 ];
9516 memcpy( ptr
, &hostname
[ 1 ], hostname
[ 0 ] );
9517 ptr
+= hostname
[ 0 ];
9518 if( inIndex
!= 1 ) SNPrintF_Add( (char **) &ptr
, (char *) lim
, " (%u)", inIndex
);
9519 rdataPtr
[ 0 ] = (uint8_t)( ptr
- &rdataPtr
[ 1 ] );
9521 check( (size_t)( lim
- ptr
) >= recordNameLen
);
9522 memcpy( ptr
, recordName
, recordNameLen
);
9523 ptr
+= recordNameLen
;
9525 rdataLen
= (size_t)( ptr
- rdataPtr
);
9527 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_PTR
, kDNSServiceClass_IN
, kMDNSRecordTTL_Other
,
9528 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9529 require_noerr( err
, exit
);
9533 *answerPtr
= answer
;
9536 else if( _MDNSReplierServiceInstanceNameMatch( inContext
, inName
, &index
, &txtSize
, &count
) &&
9537 ( index
== inIndex
) && ( count
>= inIndex
) )
9539 int listHasSRV
= false;
9540 int listHasTXT
= false;
9542 if( inType
== kDNSServiceType_ANY
)
9544 for( answer
= *inAnswerList
; answer
; answer
= answer
->next
)
9546 if( answer
->type
== kDNSServiceType_SRV
)
9548 if( !listHasSRV
&& DomainNameEqual( answer
->name
, inName
) ) listHasSRV
= true;
9550 else if( answer
->type
== kDNSServiceType_TXT
)
9552 if( !listHasTXT
&& DomainNameEqual( answer
->name
, inName
) ) listHasTXT
= true;
9554 if( listHasSRV
&& listHasTXT
) break;
9558 if( ( inType
== kDNSServiceType_SRV
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasSRV
) )
9560 dns_fixed_fields_srv
* fields
;
9563 uint8_t * targetPtr
;
9565 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9566 require_noerr( err
, exit
);
9568 rdataLen
= sizeof( dns_fixed_fields_srv
) + 1 + hostname
[ 0 ] + 10 + kLocalNameLen
;
9569 rdataPtr
= (uint8_t *) malloc( rdataLen
);
9570 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9572 lim
= &rdataPtr
[ rdataLen
];
9574 fields
= (dns_fixed_fields_srv
*) rdataPtr
;
9575 dns_fixed_fields_srv_init( fields
, 0, 0, (uint16_t)( kMDNSReplierPortBase
+ txtSize
) );
9577 targetPtr
= (uint8_t *) &fields
[ 1 ];
9579 ptr
= &targetPtr
[ 1 ];
9580 memcpy( ptr
, &hostname
[ 1 ], hostname
[ 0 ] );
9581 ptr
+= hostname
[ 0 ];
9582 if( inIndex
!= 1 ) SNPrintF_Add( (char **) &ptr
, (char *) lim
, "-%u", inIndex
);
9583 targetPtr
[ 0 ] = (uint8_t)( ptr
- &targetPtr
[ 1 ] );
9585 check( (size_t)( lim
- ptr
) >= kLocalNameLen
);
9586 memcpy( ptr
, kLocalName
, kLocalNameLen
);
9587 ptr
+= kLocalNameLen
;
9589 rdataLen
= (size_t)( ptr
- rdataPtr
);
9591 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_SRV
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9592 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9593 require_noerr( err
, exit
);
9597 *answerPtr
= answer
;
9598 answerPtr
= &answer
->next
;
9601 if( ( inType
== kDNSServiceType_TXT
) || ( ( inType
== kDNSServiceType_ANY
) && !listHasTXT
) )
9603 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9604 require_noerr( err
, exit
);
9607 err
= _MDNSReplierCreateTXTRecord( inName
, rdataLen
, &rdataPtr
);
9608 require_noerr( err
, exit
);
9610 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
, kMDNSRecordTTL_Other
,
9611 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9612 require_noerr( err
, exit
);
9616 *answerPtr
= answer
;
9618 else if( inType
== kDNSServiceType_NSEC
)
9620 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9621 require_noerr( err
, exit
);
9623 err
= CreateNSECRecordData( recordName
, &rdataPtr
, &rdataLen
, 2, kDNSServiceType_TXT
, kDNSServiceType_SRV
);
9624 require_noerr( err
, exit
);
9626 err
= _MRResourceRecordCreate( recordName
, kDNSServiceType_NSEC
, kDNSServiceClass_IN
, kMDNSRecordTTL_Host
,
9627 (uint16_t) rdataLen
, rdataPtr
, &answer
);
9628 require_noerr( err
, exit
);
9632 *answerPtr
= answer
;
9638 FreeNullSafe( recordName
);
9639 FreeNullSafe( rdataPtr
);
9643 //===========================================================================================================================
9644 // _MDNSReplierAnswerListRemovePTR
9645 //===========================================================================================================================
9648 _MDNSReplierAnswerListRemovePTR(
9649 MRResourceRecord
** inAnswerListPtr
,
9650 const uint8_t * inName
,
9651 const uint8_t * inRData
)
9653 MRResourceRecord
* answer
;
9654 MRResourceRecord
** answerPtr
;
9656 for( answerPtr
= inAnswerListPtr
; ( answer
= *answerPtr
) != NULL
; answerPtr
= &answer
->next
)
9658 if( ( answer
->type
== kDNSServiceType_PTR
) && ( answer
->class == kDNSServiceClass_IN
) &&
9659 DomainNameEqual( answer
->name
, inName
) && DomainNameEqual( answer
->rdata
, inRData
) ) break;
9663 *answerPtr
= answer
->next
;
9664 _MRResourceRecordFree( answer
);
9668 //===========================================================================================================================
9669 // _MDNSReplierSendOrDropResponse
9670 //===========================================================================================================================
9673 _MDNSReplierSendOrDropResponse(
9674 MDNSReplierContext
* inContext
,
9675 MRResourceRecord
* inAnswerList
,
9676 sockaddr_ip
* inQuerier
,
9678 unsigned int inIndex
,
9682 uint8_t * responsePtr
= NULL
;
9684 const struct sockaddr
* destAddr
;
9686 const double dropRate
= inUnicast
? inContext
->ucastDropRate
: inContext
->mcastDropRate
;
9689 check( inIndex
<= inContext
->maxInstanceCount
);
9691 // If maxDropCount > 0, then the drop rates apply only to the first maxDropCount responses. Otherwise, all messages are
9692 // subject to their respective drop rate. Also, responses to queries about mDNS replier itself (indicated by index 0),
9693 // as opposed to those for service instance records, are never dropped.
9698 if( inContext
->maxDropCount
> 0 )
9700 uint8_t * const dropCount
= &inContext
->dropCounters
[ inIndex
- 1 ];
9702 if( *dropCount
< inContext
->maxDropCount
)
9704 if( ShouldDrop( dropRate
) ) drop
= true;
9708 else if( ShouldDrop( dropRate
) )
9714 err
= _MDNSReplierCreateResponse( inContext
, inAnswerList
, inIndex
, &responsePtr
, &responseLen
);
9715 require_noerr( err
, exit
);
9719 destAddr
= &inQuerier
->sa
;
9723 destAddr
= ( inQuerier
->sa
.sa_family
== AF_INET
) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
9726 mr_ulog( kLogLevelInfo
, "%s %zu byte response to %##a:\n\n%#1{du:dnsmsg}",
9727 drop
? "Dropping" : "Sending", responseLen
, destAddr
, responsePtr
, responseLen
);
9731 n
= sendto( inSock
, (char *) responsePtr
, responseLen
, 0, destAddr
, SockAddrGetSize( destAddr
) );
9732 err
= map_socket_value_errno( inSock
, n
== (ssize_t
) responseLen
, n
);
9733 require_noerr( err
, exit
);
9737 FreeNullSafe( responsePtr
);
9741 //===========================================================================================================================
9742 // _MDNSReplierCreateResponse
9743 //===========================================================================================================================
9746 _MDNSReplierCreateResponse(
9747 MDNSReplierContext
* inContext
,
9748 MRResourceRecord
* inAnswerList
,
9749 unsigned int inIndex
,
9750 uint8_t ** outResponsePtr
,
9751 size_t * outResponseLen
)
9754 DataBuffer responseDB
;
9756 MRResourceRecord
* answer
;
9757 uint8_t * responsePtr
;
9758 size_t responseLen
, len
;
9759 unsigned int answerCount
, recordCount
;
9760 MRNameOffsetItem
* nameOffsetList
= NULL
;
9762 DataBuffer_Init( &responseDB
, NULL
, 0, SIZE_MAX
);
9764 // The current answers in the answer list will make up the response's Answer Record Section.
9767 for( answer
= inAnswerList
; answer
; answer
= answer
->next
) { ++answerCount
; }
9769 // Unless configured not to, add any additional answers to the answer list for the Additional Record Section.
9771 if( !inContext
->noAdditionals
)
9773 for( answer
= inAnswerList
; answer
; answer
= answer
->next
)
9775 switch( answer
->type
)
9777 case kDNSServiceType_PTR
:
9778 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->rdata
, kDNSServiceType_SRV
,
9780 require_noerr( err
, exit
);
9782 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->rdata
, kDNSServiceType_TXT
,
9784 require_noerr( err
, exit
);
9787 case kDNSServiceType_SRV
:
9788 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->target
, kDNSServiceType_A
,
9790 require_noerr( err
, exit
);
9792 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->target
, 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_TXT
:
9802 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_NSEC
,
9804 require_noerr( err
, exit
);
9807 case kDNSServiceType_A
:
9808 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_AAAA
,
9810 require_noerr( err
, exit
);
9812 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_NSEC
,
9814 require_noerr( err
, exit
);
9817 case kDNSServiceType_AAAA
:
9818 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_A
,
9820 require_noerr( err
, exit
);
9822 err
= _MDNSReplierAnswerListAdd( inContext
, &inAnswerList
, inIndex
, answer
->name
, kDNSServiceType_NSEC
,
9824 require_noerr( err
, exit
);
9833 // Append a provisional header to the response message.
9835 memset( &hdr
, 0, sizeof( hdr
) );
9836 DNSHeaderSetFlags( &hdr
, kDNSHeaderFlag_Response
| kDNSHeaderFlag_AuthAnswer
);
9838 err
= DataBuffer_Append( &responseDB
, &hdr
, sizeof( hdr
) );
9839 require_noerr( err
, exit
);
9841 // Append answers to response message.
9843 responseLen
= DataBuffer_GetLen( &responseDB
);
9845 for( answer
= inAnswerList
; answer
; answer
= answer
->next
)
9847 dns_fixed_fields_record fields
;
9850 // Append record NAME.
9852 err
= _MDNSReplierAppendNameToResponse( &responseDB
, answer
->name
, &nameOffsetList
);
9853 require_noerr( err
, exit
);
9855 // Append record TYPE, CLASS, TTL, and provisional RDLENGTH.
9857 class = answer
->class;
9858 if( ( answer
->type
== kDNSServiceType_SRV
) || ( answer
->type
== kDNSServiceType_TXT
) ||
9859 ( answer
->type
== kDNSServiceType_A
) || ( answer
->type
== kDNSServiceType_AAAA
) ||
9860 ( answer
->type
== kDNSServiceType_NSEC
) )
9862 class |= kRRClassCacheFlushBit
;
9865 dns_fixed_fields_record_init( &fields
, answer
->type
, (uint16_t) class, answer
->ttl
, (uint16_t) answer
->rdlength
);
9866 err
= DataBuffer_Append( &responseDB
, &fields
, sizeof( fields
) );
9867 require_noerr( err
, exit
);
9869 // Append record RDATA.
9870 // The RDATA of PTR, SRV, and NSEC records contain domain names, which are subject to name compression.
9872 if( ( answer
->type
== kDNSServiceType_PTR
) || ( answer
->type
== kDNSServiceType_SRV
) ||
9873 ( answer
->type
== kDNSServiceType_NSEC
) )
9876 uint8_t * rdLengthPtr
;
9877 const size_t rdLengthOffset
= DataBuffer_GetLen( &responseDB
) - 2;
9878 const size_t rdataOffset
= DataBuffer_GetLen( &responseDB
);
9880 if( answer
->type
== kDNSServiceType_PTR
)
9882 err
= _MDNSReplierAppendNameToResponse( &responseDB
, answer
->rdata
, &nameOffsetList
);
9883 require_noerr( err
, exit
);
9885 else if( answer
->type
== kDNSServiceType_SRV
)
9887 require_fatal( answer
->target
== &answer
->rdata
[ 6 ], "Bad SRV record target pointer." );
9889 err
= DataBuffer_Append( &responseDB
, answer
->rdata
, (size_t)( answer
->target
- answer
->rdata
) );
9890 require_noerr( err
, exit
);
9892 err
= _MDNSReplierAppendNameToResponse( &responseDB
, answer
->target
, &nameOffsetList
);
9893 require_noerr( err
, exit
);
9897 const size_t nameLen
= DomainNameLength( answer
->rdata
);
9899 err
= _MDNSReplierAppendNameToResponse( &responseDB
, answer
->rdata
, &nameOffsetList
);
9900 require_noerr( err
, exit
);
9902 require_fatal( answer
->rdlength
> nameLen
, "Bad NSEC record data length." );
9904 err
= DataBuffer_Append( &responseDB
, &answer
->rdata
[ nameLen
], answer
->rdlength
- nameLen
);
9905 require_noerr( err
, exit
);
9908 // Set the actual RDLENGTH, which may be less than the original due to name compression.
9910 rdlength
= DataBuffer_GetLen( &responseDB
) - rdataOffset
;
9911 check( rdlength
<= UINT16_MAX
);
9913 rdLengthPtr
= DataBuffer_GetPtr( &responseDB
) + rdLengthOffset
;
9914 WriteBig16( rdLengthPtr
, rdlength
);
9918 err
= DataBuffer_Append( &responseDB
, answer
->rdata
, answer
->rdlength
);
9919 require_noerr( err
, exit
);
9922 if( DataBuffer_GetLen( &responseDB
) > kMDNSMessageSizeMax
) break;
9923 responseLen
= DataBuffer_GetLen( &responseDB
);
9927 // Set the response header's Answer and Additional record counts.
9928 // Note: recordCount may be less than answerCount if including all answerCount records would cause the size of the
9929 // response message to exceed the maximum mDNS message size.
9931 if( recordCount
<= answerCount
)
9933 DNSHeaderSetAnswerCount( (DNSHeader
*) DataBuffer_GetPtr( &responseDB
), recordCount
);
9937 DNSHeaderSetAnswerCount( (DNSHeader
*) DataBuffer_GetPtr( &responseDB
), answerCount
);
9938 DNSHeaderSetAdditionalCount( (DNSHeader
*) DataBuffer_GetPtr( &responseDB
), recordCount
- answerCount
);
9941 err
= DataBuffer_Detach( &responseDB
, &responsePtr
, &len
);
9942 require_noerr( err
, exit
);
9944 if( outResponsePtr
) *outResponsePtr
= responsePtr
;
9945 if( outResponseLen
) *outResponseLen
= responseLen
;
9948 _MRNameOffsetItemFreeList( nameOffsetList
);
9949 DataBuffer_Free( &responseDB
);
9953 //===========================================================================================================================
9954 // _MDNSReplierAppendNameToResponse
9955 //===========================================================================================================================
9958 _MDNSReplierAppendNameToResponse(
9959 DataBuffer
* inResponse
,
9960 const uint8_t * inName
,
9961 MRNameOffsetItem
** inNameOffsetListPtr
)
9964 const uint8_t * subname
;
9965 const uint8_t * limit
;
9967 MRNameOffsetItem
* item
;
9968 uint8_t compressionPtr
[ 2 ];
9970 nameOffset
= DataBuffer_GetLen( inResponse
);
9972 // Find the name's longest subname (more accurately, its longest sub-FQDN) in the name compression list.
9974 for( subname
= inName
; subname
[ 0 ] != 0; subname
+= ( 1 + subname
[ 0 ] ) )
9976 for( item
= *inNameOffsetListPtr
; item
; item
= item
->next
)
9978 if( DomainNameEqual( item
->name
, subname
) ) break;
9981 // If an item was found for this subname, then append a name compression pointer and we're done. Otherwise, append
9982 // the subname's first label.
9986 DNSMessageWriteLabelPointer( compressionPtr
, item
->offset
);
9988 err
= DataBuffer_Append( inResponse
, compressionPtr
, sizeof( compressionPtr
) );
9989 require_noerr( err
, exit
);
9994 err
= DataBuffer_Append( inResponse
, subname
, 1 + subname
[ 0 ] );
9995 require_noerr( err
, exit
);
9999 // 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
10000 // label were appended to the response message, so a root label is needed to terminate the complete name.
10002 if( subname
[ 0 ] == 0 )
10004 err
= DataBuffer_Append( inResponse
, "", 1 );
10005 require_noerr( err
, exit
);
10008 // Add subnames that weren't able to be compressed and their offsets to the name compression list.
10011 for( subname
= inName
; subname
< limit
; subname
+= ( 1 + subname
[ 0 ] ) )
10013 const size_t subnameOffset
= nameOffset
+ (size_t)( subname
- inName
);
10015 if( subnameOffset
> kDNSCompressionOffsetMax
) break;
10017 err
= _MRNameOffsetItemCreate( subname
, (uint16_t) subnameOffset
, &item
);
10018 require_noerr( err
, exit
);
10020 item
->next
= *inNameOffsetListPtr
;
10021 *inNameOffsetListPtr
= item
;
10029 //===========================================================================================================================
10030 // _MDNSReplierServiceTypeMatch
10031 //===========================================================================================================================
10034 _MDNSReplierServiceTypeMatch(
10035 const MDNSReplierContext
* inContext
,
10036 const uint8_t * inName
,
10037 unsigned int * outTXTSize
,
10038 unsigned int * outCount
)
10043 uint32_t txtSize
, count
;
10044 const uint8_t * const serviceLabel
= inContext
->serviceLabel
;
10045 int nameMatches
= false;
10047 require_quiet( inName
[ 0 ] >= serviceLabel
[ 0 ], exit
);
10048 if( _memicmp( &inName
[ 1 ], &serviceLabel
[ 1 ], serviceLabel
[ 0 ] ) != 0 ) goto exit
;
10050 ptr
= (const char *) &inName
[ 1 + serviceLabel
[ 0 ] ];
10051 end
= (const char *) &inName
[ 1 + inName
[ 0 ] ];
10053 require_quiet( ( ptr
< end
) && ( *ptr
== '-' ), exit
);
10056 err
= DecimalTextToUInt32( ptr
, end
, &txtSize
, &ptr
);
10057 require_noerr_quiet( err
, exit
);
10058 require_quiet( txtSize
<= UINT16_MAX
, exit
);
10060 require_quiet( ( ptr
< end
) && ( *ptr
== '-' ), exit
);
10063 err
= DecimalTextToUInt32( ptr
, end
, &count
, &ptr
);
10064 require_noerr_quiet( err
, exit
);
10065 require_quiet( count
<= UINT16_MAX
, exit
);
10066 require_quiet( ptr
== end
, exit
);
10068 if( !DomainNameEqual( (const uint8_t *) ptr
, (const uint8_t *) "\x04" "_tcp" "\x05" "local" ) ) goto exit
;
10069 nameMatches
= true;
10071 if( outTXTSize
) *outTXTSize
= txtSize
;
10072 if( outCount
) *outCount
= count
;
10075 return( nameMatches
? true : false );
10078 //===========================================================================================================================
10079 // _MDNSReplierServiceInstanceNameMatch
10080 //===========================================================================================================================
10083 _MDNSReplierServiceInstanceNameMatch(
10084 const MDNSReplierContext
* inContext
,
10085 const uint8_t * inName
,
10086 unsigned int * outIndex
,
10087 unsigned int * outTXTSize
,
10088 unsigned int * outCount
)
10091 const uint8_t * ptr
;
10092 const uint8_t * end
;
10094 unsigned int txtSize
, count
;
10095 const uint8_t * const hostname
= inContext
->hostname
;
10096 int nameMatches
= false;
10098 require_quiet( inName
[ 0 ] >= hostname
[ 0 ], exit
);
10099 if( _memicmp( &inName
[ 1 ], &hostname
[ 1 ], hostname
[ 0 ] ) != 0 ) goto exit
;
10101 ptr
= &inName
[ 1 + hostname
[ 0 ] ];
10102 end
= &inName
[ 1 + inName
[ 0 ] ];
10105 require_quiet( ( end
- ptr
) >= 2, exit
);
10106 require_quiet( ( ptr
[ 0 ] == ' ' ) && ( ptr
[ 1 ] == '(' ), exit
);
10109 err
= DecimalTextToUInt32( (const char *) ptr
, (const char *) end
, &index
, (const char **) &ptr
);
10110 require_noerr_quiet( err
, exit
);
10111 require_quiet( ( index
>= 2 ) && ( index
<= UINT16_MAX
), exit
);
10113 require_quiet( ( ( end
- ptr
) == 1 ) && ( *ptr
== ')' ), exit
);
10121 if( !_MDNSReplierServiceTypeMatch( inContext
, ptr
, &txtSize
, &count
) ) goto exit
;
10122 nameMatches
= true;
10124 if( outIndex
) *outIndex
= index
;
10125 if( outTXTSize
) *outTXTSize
= txtSize
;
10126 if( outCount
) *outCount
= count
;
10129 return( nameMatches
? true : false );
10132 //===========================================================================================================================
10133 // _MDNSReplierAboutRecordNameMatch
10134 //===========================================================================================================================
10136 #define _MemIEqual( PTR1, LEN1, PTR2, LEN2 ) \
10137 ( ( ( LEN1 ) == ( LEN2 ) ) && ( _memicmp( ( PTR1 ), ( PTR2 ), ( LEN1 ) ) == 0 ) )
10139 static Boolean
_MDNSReplierAboutRecordNameMatch( const MDNSReplierContext
*inContext
, const uint8_t *inName
)
10141 const uint8_t * subname
;
10142 const uint8_t * const hostname
= inContext
->hostname
;
10143 int nameMatches
= false;
10145 if( strnicmpx( &inName
[ 1 ], inName
[ 0 ], "about" ) != 0 ) goto exit
;
10146 subname
= DomainNameGetNextLabel( inName
);
10148 if( !_MemIEqual( &subname
[ 1 ], subname
[ 0 ], &hostname
[ 1 ], hostname
[ 0 ] ) ) goto exit
;
10149 subname
= DomainNameGetNextLabel( subname
);
10151 if( !DomainNameEqual( subname
, kLocalName
) ) goto exit
;
10152 nameMatches
= true;
10155 return( nameMatches
? true : false );
10158 //===========================================================================================================================
10159 // _MDNSReplierHostnameMatch
10160 //===========================================================================================================================
10163 _MDNSReplierHostnameMatch(
10164 const MDNSReplierContext
* inContext
,
10165 const uint8_t * inName
,
10166 unsigned int * outIndex
)
10169 const uint8_t * ptr
;
10170 const uint8_t * end
;
10172 const uint8_t * const hostname
= inContext
->hostname
;
10173 int nameMatches
= false;
10175 require_quiet( inName
[ 0 ] >= hostname
[ 0 ], exit
);
10176 if( _memicmp( &inName
[ 1 ], &hostname
[ 1 ], hostname
[ 0 ] ) != 0 ) goto exit
;
10178 ptr
= &inName
[ 1 + hostname
[ 0 ] ];
10179 end
= &inName
[ 1 + inName
[ 0 ] ];
10182 require_quiet( *ptr
== '-', exit
);
10185 err
= DecimalTextToUInt32( (const char *) ptr
, (const char *) end
, &index
, (const char **) &ptr
);
10186 require_noerr_quiet( err
, exit
);
10187 require_quiet( ( index
>= 2 ) && ( index
<= UINT16_MAX
), exit
);
10188 require_quiet( ptr
== end
, exit
);
10195 if( !DomainNameEqual( ptr
, kLocalName
) ) goto exit
;
10196 nameMatches
= true;
10198 if( outIndex
) *outIndex
= index
;
10201 return( nameMatches
? true : false );
10204 //===========================================================================================================================
10205 // _MDNSReplierCreateTXTRecord
10206 //===========================================================================================================================
10208 static OSStatus
_MDNSReplierCreateTXTRecord( const uint8_t *inRecordName
, size_t inSize
, uint8_t **outTXT
)
10213 size_t i
, wholeCount
, remCount
;
10216 uint8_t txtStr
[ 16 ];
10218 require_action_quiet( inSize
> 0, exit
, err
= kSizeErr
);
10220 txt
= (uint8_t *) malloc( inSize
);
10221 require_action( txt
, exit
, err
= kNoMemoryErr
);
10223 hash
= _FNV1( inRecordName
, DomainNameLength( inRecordName
) );
10226 n
= MemPrintF( &txtStr
[ 1 ], 15, "hash=0x%08X", hash
);
10230 wholeCount
= inSize
/ 16;
10231 for( i
= 0; i
< wholeCount
; ++i
)
10233 memcpy( ptr
, txtStr
, 16 );
10237 remCount
= inSize
% 16;
10240 txtStr
[ 0 ] = (uint8_t)( remCount
- 1 );
10241 memcpy( ptr
, txtStr
, remCount
);
10244 check( ptr
== &txt
[ inSize
] );
10253 //===========================================================================================================================
10254 // _MRResourceRecordCreate
10255 //===========================================================================================================================
10258 _MRResourceRecordCreate(
10263 uint16_t inRDLength
,
10265 MRResourceRecord
** outRecord
)
10268 MRResourceRecord
* obj
;
10270 obj
= (MRResourceRecord
*) calloc( 1, sizeof( *obj
) );
10271 require_action( obj
, exit
, err
= kNoMemoryErr
);
10273 obj
->name
= inName
;
10274 obj
->type
= inType
;
10275 obj
->class = inClass
;
10277 obj
->rdlength
= inRDLength
;
10278 obj
->rdata
= inRData
;
10280 if( inType
== kDNSServiceType_SRV
)
10282 require_action_quiet( obj
->rdlength
> sizeof( dns_fixed_fields_srv
), exit
, err
= kMalformedErr
);
10283 obj
->target
= obj
->rdata
+ sizeof( dns_fixed_fields_srv
);
10291 FreeNullSafe( obj
);
10295 //===========================================================================================================================
10296 // _MRResourceRecordFree
10297 //===========================================================================================================================
10299 static void _MRResourceRecordFree( MRResourceRecord
*inRecord
)
10301 ForgetMem( &inRecord
->name
);
10302 ForgetMem( &inRecord
->rdata
);
10306 //===========================================================================================================================
10307 // _MRResourceRecordFreeList
10308 //===========================================================================================================================
10310 static void _MRResourceRecordFreeList( MRResourceRecord
*inList
)
10312 MRResourceRecord
* record
;
10314 while( ( record
= inList
) != NULL
)
10316 inList
= record
->next
;
10317 _MRResourceRecordFree( record
);
10321 //===========================================================================================================================
10322 // _MRNameOffsetItemCreate
10323 //===========================================================================================================================
10325 static OSStatus
_MRNameOffsetItemCreate( const uint8_t *inName
, uint16_t inOffset
, MRNameOffsetItem
**outItem
)
10328 MRNameOffsetItem
* obj
;
10331 require_action_quiet( inOffset
<= kDNSCompressionOffsetMax
, exit
, err
= kSizeErr
);
10333 nameLen
= DomainNameLength( inName
);
10334 obj
= (MRNameOffsetItem
*) calloc( 1, offsetof( MRNameOffsetItem
, name
) + nameLen
);
10335 require_action( obj
, exit
, err
= kNoMemoryErr
);
10337 obj
->offset
= inOffset
;
10338 memcpy( obj
->name
, inName
, nameLen
);
10347 //===========================================================================================================================
10348 // _MRNameOffsetItemFree
10349 //===========================================================================================================================
10351 static void _MRNameOffsetItemFree( MRNameOffsetItem
*inItem
)
10356 //===========================================================================================================================
10357 // _MRNameOffsetItemFreeList
10358 //===========================================================================================================================
10360 static void _MRNameOffsetItemFreeList( MRNameOffsetItem
*inList
)
10362 MRNameOffsetItem
* item
;
10364 while( ( item
= inList
) != NULL
)
10366 inList
= item
->next
;
10367 _MRNameOffsetItemFree( item
);
10371 //===========================================================================================================================
10373 //===========================================================================================================================
10375 #define kGAIPerfStandardTTL ( 1 * kSecondsPerHour )
10377 typedef struct GAITesterPrivate
* GAITesterRef
;
10378 typedef struct GAITestCase GAITestCase
;
10382 const char * name
; // Domain name that was resolved.
10383 uint64_t connectionTimeUs
; // Time in microseconds that it took to create a DNS-SD connection.
10384 uint64_t firstTimeUs
; // Time in microseconds that it took to get the first address result.
10385 uint64_t timeUs
; // Time in microseconds that it took to get all expected address results.
10388 } GAITestItemResult
;
10390 typedef void ( *GAITesterStopHandler_f
)( void *inContext
, OSStatus inError
);
10392 ( *GAITesterResultsHandler_f
)(
10393 const char * inCaseTitle
,
10394 NanoTime64 inCaseStartTime
,
10395 NanoTime64 inCaseEndTime
,
10396 const GAITestItemResult
* inResultArray
,
10397 size_t inResultCount
,
10398 void * inContext
);
10400 typedef unsigned int GAITestAddrType
;
10401 #define kGAITestAddrType_None 0
10402 #define kGAITestAddrType_IPv4 ( 1U << 0 )
10403 #define kGAITestAddrType_IPv6 ( 1U << 1 )
10404 #define kGAITestAddrType_Both ( kGAITestAddrType_IPv4 | kGAITestAddrType_IPv6 )
10406 #define GAITestAddrTypeIsValid( X ) \
10407 ( ( (X) & kGAITestAddrType_Both ) && ( ( (X) & ~kGAITestAddrType_Both ) == 0 ) )
10411 GAITesterRef tester
; // GAI tester object.
10412 CFMutableArrayRef testCaseResults
; // Array of test case results.
10413 unsigned int iterTimeLimitMs
; // Amount of time to allow each iteration to complete.
10414 unsigned int callDelayMs
; // Amount of time to wait before calling DNSServiceGetAddrInfo().
10415 unsigned int serverDelayMs
; // Amount of additional time to have server delay its responses.
10416 unsigned int defaultIterCount
; // Default test case iteration count.
10417 dispatch_source_t sigIntSource
; // Dispatch source for SIGINT.
10418 dispatch_source_t sigTermSource
; // Dispatch source for SIGTERM.
10419 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout.
10420 OutputFormatType outputFormat
; // Format of test results output.
10421 Boolean skipPathEval
; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
10422 Boolean badUDPMode
; // True if the test DNS server is to run in Bad UDP mode.
10423 Boolean testFailed
; // True if at least one test case iteration failed.
10427 static void GAIPerfContextFree( GAIPerfContext
*inContext
);
10428 static OSStatus
GAIPerfAddAdvancedTestCases( GAIPerfContext
*inContext
);
10429 static OSStatus
GAIPerfAddBasicTestCases( GAIPerfContext
*inContext
);
10430 static void GAIPerfTesterStopHandler( void *inContext
, OSStatus inError
);
10432 GAIPerfResultsHandler(
10433 const char * inCaseTitle
,
10434 NanoTime64 inCaseStartTime
,
10435 NanoTime64 inCaseEndTime
,
10436 const GAITestItemResult
* inResultArray
,
10437 size_t inResultCount
,
10438 void * inContext
);
10439 static void GAIPerfSignalHandler( void *inContext
);
10441 CFTypeID
GAITesterGetTypeID( void );
10444 dispatch_queue_t inQueue
,
10445 unsigned int inCallDelayMs
,
10446 int inServerDelayMs
,
10447 int inServerDefaultTTL
,
10448 Boolean inSkipPathEvaluation
,
10449 Boolean inBadUDPMode
,
10450 GAITesterRef
* outTester
);
10451 static void GAITesterStart( GAITesterRef inTester
);
10452 static void GAITesterStop( GAITesterRef inTester
);
10453 static OSStatus
GAITesterAddTestCase( GAITesterRef inTester
, GAITestCase
*inCase
);
10455 GAITesterSetStopHandler(
10456 GAITesterRef inTester
,
10457 GAITesterStopHandler_f inEventHandler
,
10458 void * inEventContext
);
10460 GAITesterSetResultsHandler(
10461 GAITesterRef inTester
,
10462 GAITesterResultsHandler_f inResultsHandler
,
10463 void * inResultsContext
);
10465 static OSStatus
GAITestCaseCreate( const char *inTitle
, GAITestCase
**outCase
);
10466 static void GAITestCaseFree( GAITestCase
*inCase
);
10468 GAITestCaseAddItem(
10469 GAITestCase
* inCase
,
10470 unsigned int inAliasCount
,
10471 unsigned int inAddressCount
,
10473 GAITestAddrType inHasAddrs
,
10474 GAITestAddrType inWantAddrs
,
10475 unsigned int inTimeLimitMs
,
10476 unsigned int inItemCount
);
10478 GAITestCaseAddLocalHostItem(
10479 GAITestCase
* inCase
,
10480 GAITestAddrType inWantAddrs
,
10481 unsigned int inTimeLimitMs
,
10482 unsigned int inItemCount
);
10484 static void GAIPerfCmd( void )
10487 GAIPerfContext
* context
= NULL
;
10489 err
= CheckRootUser();
10490 require_noerr_quiet( err
, exit
);
10492 err
= CheckIntegerArgument( gGAIPerf_CallDelayMs
, "call delay (ms)", 0, INT_MAX
);
10493 require_noerr_quiet( err
, exit
);
10495 err
= CheckIntegerArgument( gGAIPerf_ServerDelayMs
, "server delay (ms)", 0, INT_MAX
);
10496 require_noerr_quiet( err
, exit
);
10498 err
= CheckIntegerArgument( gGAIPerf_IterationCount
, "iteration count", 1, INT_MAX
);
10499 require_noerr_quiet( err
, exit
);
10501 err
= CheckIntegerArgument( gGAIPerf_IterationTimeLimitMs
, "iteration time limit (ms)", 0, INT_MAX
);
10502 require_noerr_quiet( err
, exit
);
10504 context
= (GAIPerfContext
*) calloc( 1, sizeof( *context
) );
10505 require_action( context
, exit
, err
= kNoMemoryErr
);
10507 context
->testCaseResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
10508 require_action( context
->testCaseResults
, exit
, err
= kNoMemoryErr
);
10510 context
->iterTimeLimitMs
= (unsigned int) gGAIPerf_IterationTimeLimitMs
;
10511 context
->callDelayMs
= (unsigned int) gGAIPerf_CallDelayMs
;
10512 context
->serverDelayMs
= (unsigned int) gGAIPerf_ServerDelayMs
;
10513 context
->defaultIterCount
= (unsigned int) gGAIPerf_IterationCount
;
10514 context
->skipPathEval
= gGAIPerf_SkipPathEvalulation
? true : false;
10515 context
->badUDPMode
= gGAIPerf_BadUDPMode
? true : false;
10517 if( gGAIPerf_OutputFilePath
)
10519 context
->outputFilePath
= strdup( gGAIPerf_OutputFilePath
);
10520 require_action( context
->outputFilePath
, exit
, err
= kNoMemoryErr
);
10523 err
= OutputFormatFromArgString( gGAIPerf_OutputFormat
, &context
->outputFormat
);
10524 require_noerr_quiet( err
, exit
);
10526 err
= GAITesterCreate( dispatch_get_main_queue(), context
->callDelayMs
, (int) context
->serverDelayMs
,
10527 kGAIPerfStandardTTL
, context
->skipPathEval
, context
->badUDPMode
, &context
->tester
);
10528 require_noerr( err
, exit
);
10530 check( gGAIPerf_TestSuite
);
10531 if( strcasecmp( gGAIPerf_TestSuite
, kGAIPerfTestSuiteName_Basic
) == 0 )
10533 err
= GAIPerfAddBasicTestCases( context
);
10534 require_noerr( err
, exit
);
10536 else if( strcasecmp( gGAIPerf_TestSuite
, kGAIPerfTestSuiteName_Advanced
) == 0 )
10538 err
= GAIPerfAddAdvancedTestCases( context
);
10539 require_noerr( err
, exit
);
10543 FPrintF( stderr
, "error: Invalid test suite name: %s.\n", gGAIPerf_TestSuite
);
10547 GAITesterSetStopHandler( context
->tester
, GAIPerfTesterStopHandler
, context
);
10548 GAITesterSetResultsHandler( context
->tester
, GAIPerfResultsHandler
, context
);
10550 signal( SIGINT
, SIG_IGN
);
10551 err
= DispatchSignalSourceCreate( SIGINT
, GAIPerfSignalHandler
, context
, &context
->sigIntSource
);
10552 require_noerr( err
, exit
);
10553 dispatch_resume( context
->sigIntSource
);
10555 signal( SIGTERM
, SIG_IGN
);
10556 err
= DispatchSignalSourceCreate( SIGTERM
, GAIPerfSignalHandler
, context
, &context
->sigTermSource
);
10557 require_noerr( err
, exit
);
10558 dispatch_resume( context
->sigTermSource
);
10560 GAITesterStart( context
->tester
);
10564 if( context
) GAIPerfContextFree( context
);
10568 //===========================================================================================================================
10569 // GAIPerfContextFree
10570 //===========================================================================================================================
10572 static void GAIPerfContextFree( GAIPerfContext
*inContext
)
10574 ForgetCF( &inContext
->tester
);
10575 ForgetCF( &inContext
->testCaseResults
);
10576 ForgetMem( &inContext
->outputFilePath
);
10577 dispatch_source_forget( &inContext
->sigIntSource
);
10578 dispatch_source_forget( &inContext
->sigTermSource
);
10582 //===========================================================================================================================
10583 // GAIPerfAddAdvancedTestCases
10584 //===========================================================================================================================
10586 #define kTestCaseTitleBufferSize 128
10589 _GAIPerfWriteTestCaseTitle(
10590 char inBuffer
[ kTestCaseTitleBufferSize
],
10591 unsigned int inCNAMERecordCount
,
10592 unsigned int inARecordCount
,
10593 unsigned int inAAAARecordCount
,
10594 GAITestAddrType inRequested
,
10595 unsigned int inIterationCount
,
10596 Boolean inIterationsAreUnique
);
10598 _GAIPerfWriteLocalHostTestCaseTitle(
10599 char inBuffer
[ kTestCaseTitleBufferSize
],
10600 GAITestAddrType inRequested
,
10601 unsigned int inIterationCount
);
10603 #define kGAIPerfAdvancedTestSuite_MaxAliasCount 4
10604 #define kGAIPerfAdvancedTestSuite_MaxAddrCount 8
10606 static OSStatus
GAIPerfAddAdvancedTestCases( GAIPerfContext
*inContext
)
10609 unsigned int aliasCount
, addressCount
, i
;
10610 GAITestCase
* testCase
= NULL
;
10611 char title
[ kTestCaseTitleBufferSize
];
10614 while( aliasCount
<= kGAIPerfAdvancedTestSuite_MaxAliasCount
)
10616 for( addressCount
= 1; addressCount
<= kGAIPerfAdvancedTestSuite_MaxAddrCount
; addressCount
*= 2 )
10618 // Add a test case to resolve a domain name with
10620 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10622 // to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which
10623 // requires server queries.
10625 _GAIPerfWriteTestCaseTitle( title
, aliasCount
, addressCount
, addressCount
, kGAITestAddrType_Both
,
10626 inContext
->defaultIterCount
, true );
10628 err
= GAITestCaseCreate( title
, &testCase
);
10629 require_noerr( err
, exit
);
10631 for( i
= 0; i
< inContext
->defaultIterCount
; ++i
)
10633 err
= GAITestCaseAddItem( testCase
, aliasCount
, addressCount
, kGAIPerfStandardTTL
,
10634 kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
, 1 );
10635 require_noerr( err
, exit
);
10638 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10639 require_noerr( err
, exit
);
10642 // Add a test case to resolve a domain name with
10644 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
10646 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique domain name, which requires a server
10647 // query. The subsequent iterations resolve the same domain name as the preliminary iteration, which should
10648 // ideally require no server queries, i.e., the results should come from the cache.
10650 _GAIPerfWriteTestCaseTitle( title
, aliasCount
, addressCount
, addressCount
, kGAITestAddrType_Both
,
10651 inContext
->defaultIterCount
, false );
10653 err
= GAITestCaseCreate( title
, &testCase
);
10654 require_noerr( err
, exit
);
10656 err
= GAITestCaseAddItem( testCase
, aliasCount
, addressCount
, kGAIPerfStandardTTL
,
10657 kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
, inContext
->defaultIterCount
+ 1 );
10658 require_noerr( err
, exit
);
10660 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10661 require_noerr( err
, exit
);
10665 aliasCount
= ( aliasCount
== 0 ) ? 1 : ( 2 * aliasCount
);
10668 // Finally, add a test case to resolve localhost to its IPv4 and IPv6 addresses.
10670 _GAIPerfWriteLocalHostTestCaseTitle( title
, kGAITestAddrType_Both
, inContext
->defaultIterCount
);
10672 err
= GAITestCaseCreate( title
, &testCase
);
10673 require_noerr( err
, exit
);
10675 err
= GAITestCaseAddLocalHostItem( testCase
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
,
10676 inContext
->defaultIterCount
);
10677 require_noerr( err
, exit
);
10679 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10680 require_noerr( err
, exit
);
10684 if( testCase
) GAITestCaseFree( testCase
);
10688 //===========================================================================================================================
10689 // _GAIPerfWriteTestCaseTitle
10690 //===========================================================================================================================
10692 #define GAITestAddrTypeToRequestKeyValue( X ) ( \
10693 ( (X) == kGAITestAddrType_Both ) ? "ipv4\\,ipv6" : \
10694 ( (X) == kGAITestAddrType_IPv4 ) ? "ipv4" : \
10695 ( (X) == kGAITestAddrType_IPv6 ) ? "ipv6" : \
10699 _GAIPerfWriteTestCaseTitle(
10700 char inBuffer
[ kTestCaseTitleBufferSize
],
10701 unsigned int inCNAMERecordCount
,
10702 unsigned int inARecordCount
,
10703 unsigned int inAAAARecordCount
,
10704 GAITestAddrType inRequested
,
10705 unsigned int inIterationCount
,
10706 Boolean inIterationsAreUnique
)
10708 SNPrintF( inBuffer
, kTestCaseTitleBufferSize
, "name=dynamic,cname=%u,a=%u,aaaa=%u,req=%s,iterations=%u%?s",
10709 inCNAMERecordCount
, inARecordCount
, inAAAARecordCount
, GAITestAddrTypeToRequestKeyValue( inRequested
),
10710 inIterationCount
, inIterationsAreUnique
, ",unique" );
10713 //===========================================================================================================================
10714 // _GAIPerfWriteLocalHostTestCaseTitle
10715 //===========================================================================================================================
10718 _GAIPerfWriteLocalHostTestCaseTitle(
10719 char inBuffer
[ kTestCaseTitleBufferSize
],
10720 GAITestAddrType inRequested
,
10721 unsigned int inIterationCount
)
10723 SNPrintF( inBuffer
, kTestCaseTitleBufferSize
, "name=localhost,req=%s,iterations=%u",
10724 GAITestAddrTypeToRequestKeyValue( inRequested
), inIterationCount
);
10727 //===========================================================================================================================
10728 // GAIPerfAddBasicTestCases
10729 //===========================================================================================================================
10731 #define kGAIPerfBasicTestSuite_AliasCount 2
10732 #define kGAIPerfBasicTestSuite_AddrCount 4
10734 static OSStatus
GAIPerfAddBasicTestCases( GAIPerfContext
*inContext
)
10737 GAITestCase
* testCase
= NULL
;
10738 char title
[ kTestCaseTitleBufferSize
];
10742 // Resolve a domain name with
10744 // 2 CNAME records, 4 A records, and 4 AAAA records
10746 // to its IPv4 and IPv6 addresses. Each of the iterations resolves a unique domain name, which requires server
10749 _GAIPerfWriteTestCaseTitle( title
, kGAIPerfBasicTestSuite_AliasCount
,
10750 kGAIPerfBasicTestSuite_AddrCount
, kGAIPerfBasicTestSuite_AddrCount
, kGAITestAddrType_Both
,
10751 inContext
->defaultIterCount
, true );
10753 err
= GAITestCaseCreate( title
, &testCase
);
10754 require_noerr( err
, exit
);
10756 for( i
= 0; i
< inContext
->defaultIterCount
; ++i
)
10758 err
= GAITestCaseAddItem( testCase
, kGAIPerfBasicTestSuite_AliasCount
, kGAIPerfBasicTestSuite_AddrCount
,
10759 kGAIPerfStandardTTL
, kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
, 1 );
10760 require_noerr( err
, exit
);
10763 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10764 require_noerr( err
, exit
);
10768 // Resolve a domain name with
10770 // 2 CNAME records, 4 A records, and 4 AAAA records
10772 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which
10773 // requires server queries. Each of the subsequent iterations resolves the same domain name as the preliminary
10774 // iteration, which should ideally require no additional server queries, i.e., the results should come from the cache.
10776 _GAIPerfWriteTestCaseTitle( title
, kGAIPerfBasicTestSuite_AliasCount
,
10777 kGAIPerfBasicTestSuite_AddrCount
, kGAIPerfBasicTestSuite_AddrCount
, kGAITestAddrType_Both
,
10778 inContext
->defaultIterCount
, false );
10780 err
= GAITestCaseCreate( title
, &testCase
);
10781 require_noerr( err
, exit
);
10783 err
= GAITestCaseAddItem( testCase
, kGAIPerfBasicTestSuite_AliasCount
, kGAIPerfBasicTestSuite_AddrCount
,
10784 kGAIPerfStandardTTL
, kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
,
10785 inContext
->defaultIterCount
+ 1 );
10786 require_noerr( err
, exit
);
10788 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10789 require_noerr( err
, exit
);
10793 // Each iteration resolves localhost to its IPv4 and IPv6 addresses.
10795 _GAIPerfWriteLocalHostTestCaseTitle( title
, kGAITestAddrType_Both
, inContext
->defaultIterCount
);
10797 err
= GAITestCaseCreate( title
, &testCase
);
10798 require_noerr( err
, exit
);
10800 err
= GAITestCaseAddLocalHostItem( testCase
, kGAITestAddrType_Both
, inContext
->iterTimeLimitMs
,
10801 inContext
->defaultIterCount
);
10802 require_noerr( err
, exit
);
10804 err
= GAITesterAddTestCase( inContext
->tester
, testCase
);
10805 require_noerr( err
, exit
);
10809 if( testCase
) GAITestCaseFree( testCase
);
10813 //===========================================================================================================================
10814 // GAIPerfTesterStopHandler
10815 //===========================================================================================================================
10817 #define kGAIPerfResultsKey_Info CFSTR( "info" )
10818 #define kGAIPerfResultsKey_TestCases CFSTR( "testCases" )
10819 #define kGAIPerfResultsKey_Success CFSTR( "success" )
10821 #define kGAIPerfInfoKey_CallDelay CFSTR( "callDelayMs" )
10822 #define kGAIPerfInfoKey_ServerDelay CFSTR( "serverDelayMs" )
10823 #define kGAIPerfInfoKey_SkippedPathEval CFSTR( "skippedPathEval" )
10824 #define kGAIPerfInfoKey_UsedBadUDPMode CFSTR( "usedBadUPDMode" )
10826 static void GAIPerfTesterStopHandler( void *inContext
, OSStatus inError
)
10829 GAIPerfContext
* const context
= (GAIPerfContext
*) inContext
;
10830 CFPropertyListRef plist
;
10834 require_noerr_quiet( err
, exit
);
10836 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
10840 "%kO=%lli" // callDelayMs
10841 "%kO=%lli" // serverDelayMs
10842 "%kO=%b" // skippedPathEval
10843 "%kO=%b" // usedBadUPDMode
10845 "%kO=%O" // testCases
10846 "%kO=%b" // success
10848 kGAIPerfResultsKey_Info
,
10849 kGAIPerfInfoKey_CallDelay
, (int64_t) context
->callDelayMs
,
10850 kGAIPerfInfoKey_ServerDelay
, (int64_t) context
->serverDelayMs
,
10851 kGAIPerfInfoKey_SkippedPathEval
, context
->skipPathEval
,
10852 kGAIPerfInfoKey_UsedBadUDPMode
, context
->badUDPMode
,
10853 kGAIPerfResultsKey_TestCases
, context
->testCaseResults
,
10854 kGAIPerfResultsKey_Success
, !context
->testFailed
);
10855 require_noerr( err
, exit
);
10857 err
= OutputPropertyList( plist
, context
->outputFormat
, context
->outputFilePath
);
10858 CFRelease( plist
);
10859 require_noerr( err
, exit
);
10862 exitCode
= err
? 1 : ( context
->testFailed
? 2 : 0 );
10863 GAIPerfContextFree( context
);
10867 //===========================================================================================================================
10868 // GAIPerfResultsHandler
10869 //===========================================================================================================================
10871 // Keys for test case dictionary
10873 #define kGAIPerfTestCaseKey_Title CFSTR( "title" )
10874 #define kGAIPerfTestCaseKey_StartTime CFSTR( "startTime" )
10875 #define kGAIPerfTestCaseKey_EndTime CFSTR( "endTime" )
10876 #define kGAIPerfTestCaseKey_Results CFSTR( "results" )
10877 #define kGAIPerfTestCaseKey_FirstStats CFSTR( "firstStats" )
10878 #define kGAIPerfTestCaseKey_ConnectionStats CFSTR( "connectionStats" )
10879 #define kGAIPerfTestCaseKey_Stats CFSTR( "stats" )
10881 // Keys for test case results array entry dictionaries
10883 #define kGAIPerfTestCaseResultKey_Name CFSTR( "name" )
10884 #define kGAIPerfTestCaseResultKey_ConnectionTime CFSTR( "connectionTimeUs" )
10885 #define kGAIPerfTestCaseResultKey_FirstTime CFSTR( "firstTimeUs" )
10886 #define kGAIPerfTestCaseResultKey_Time CFSTR( "timeUs" )
10888 // Keys for test case stats dictionaries
10890 #define kGAIPerfTestCaseStatsKey_Count CFSTR( "count" )
10891 #define kGAIPerfTestCaseStatsKey_Min CFSTR( "min" )
10892 #define kGAIPerfTestCaseStatsKey_Max CFSTR( "max" )
10893 #define kGAIPerfTestCaseStatsKey_Mean CFSTR( "mean" )
10894 #define kGAIPerfTestCaseStatsKey_StdDev CFSTR( "sd" )
10905 #define GAIPerfStatsInit( X ) \
10906 do { (X)->min = DBL_MAX; (X)->max = DBL_MIN; (X)->mean = 0.0; (X)->stdDev = 0.0; } while( 0 )
10909 GAIPerfResultsHandler(
10910 const char * inCaseTitle
,
10911 NanoTime64 inCaseStartTime
,
10912 NanoTime64 inCaseEndTime
,
10913 const GAITestItemResult
* inResultArray
,
10914 size_t inResultCount
,
10918 GAIPerfContext
* const context
= (GAIPerfContext
*) inContext
;
10919 int namesAreDynamic
, namesAreUnique
;
10921 size_t count
, startIndex
;
10922 CFMutableArrayRef results
= NULL
;
10923 GAIPerfStats stats
, firstStats
, connStats
;
10924 double sum
, firstSum
, connSum
;
10925 size_t keyValueLen
, i
;
10926 char keyValue
[ 16 ]; // Size must be at least strlen( "name=dynamic" ) + 1 bytes.
10927 char startTime
[ 32 ];
10928 char endTime
[ 32 ];
10929 const GAITestItemResult
* result
;
10931 // If this test case resolves the same "d.test." name in each iteration (title contains the "name=dynamic" key-value
10932 // pair, but not the "unique" key), then don't count the first iteration, whose purpose is to populate the cache with
10933 // the domain name's CNAME, A, and AAAA records.
10935 namesAreDynamic
= false;
10936 namesAreUnique
= false;
10938 while( _ParseQuotedEscapedString( ptr
, NULL
, ",", keyValue
, sizeof( keyValue
), &keyValueLen
, NULL
, &ptr
) )
10940 if( strnicmpx( keyValue
, keyValueLen
, "name=dynamic" ) == 0 )
10942 namesAreDynamic
= true;
10944 else if( strnicmpx( keyValue
, keyValueLen
, "unique" ) == 0 )
10946 namesAreUnique
= true;
10948 if( namesAreDynamic
&& namesAreUnique
) break;
10951 startIndex
= ( ( inResultCount
> 0 ) && namesAreDynamic
&& !namesAreUnique
) ? 1 : 0;
10952 results
= CFArrayCreateMutable( NULL
, (CFIndex
)( inResultCount
- startIndex
), &kCFTypeArrayCallBacks
);
10953 require_action( results
, exit
, err
= kNoMemoryErr
);
10955 GAIPerfStatsInit( &stats
);
10956 GAIPerfStatsInit( &firstStats
);
10957 GAIPerfStatsInit( &connStats
);
10963 for( i
= startIndex
; i
< inResultCount
; ++i
)
10967 result
= &inResultArray
[ i
];
10969 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, results
,
10972 "%kO=%lli" // connectionTimeUs
10973 "%kO=%lli" // firstTimeUs
10974 "%kO=%lli" // timeUs
10975 "%kO=%lli" // error
10977 kGAIPerfTestCaseResultKey_Name
, result
->name
,
10978 kGAIPerfTestCaseResultKey_ConnectionTime
, (int64_t) result
->connectionTimeUs
,
10979 kGAIPerfTestCaseResultKey_FirstTime
, (int64_t) result
->firstTimeUs
,
10980 kGAIPerfTestCaseResultKey_Time
, (int64_t) result
->timeUs
,
10981 CFSTR( "error" ), (int64_t) result
->error
);
10982 require_noerr( err
, exit
);
10984 if( !result
->error
)
10986 value
= (double) result
->timeUs
;
10987 if( value
< stats
.min
) stats
.min
= value
;
10988 if( value
> stats
.max
) stats
.max
= value
;
10991 value
= (double) result
->firstTimeUs
;
10992 if( value
< firstStats
.min
) firstStats
.min
= value
;
10993 if( value
> firstStats
.max
) firstStats
.max
= value
;
10996 value
= (double) result
->connectionTimeUs
;
10997 if( value
< connStats
.min
) connStats
.min
= value
;
10998 if( value
> connStats
.max
) connStats
.max
= value
;
11005 context
->testFailed
= true;
11011 stats
.mean
= sum
/ count
;
11012 firstStats
.mean
= firstSum
/ count
;
11013 connStats
.mean
= connSum
/ count
;
11018 for( i
= startIndex
; i
< inResultCount
; ++i
)
11022 result
= &inResultArray
[ i
];
11023 if( result
->error
) continue;
11025 diff
= stats
.mean
- (double) result
->timeUs
;
11026 sum
+= ( diff
* diff
);
11028 diff
= firstStats
.mean
- (double) result
->firstTimeUs
;
11029 firstSum
+= ( diff
* diff
);
11031 diff
= connStats
.mean
- (double) result
->connectionTimeUs
;
11032 connSum
+= ( diff
* diff
);
11034 stats
.stdDev
= sqrt( sum
/ count
);
11035 firstStats
.stdDev
= sqrt( firstSum
/ count
);
11036 connStats
.stdDev
= sqrt( connSum
/ count
);
11039 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->testCaseResults
,
11070 kGAIPerfTestCaseKey_Title
, inCaseTitle
,
11071 kGAIPerfTestCaseKey_StartTime
, _NanoTime64ToTimestamp( inCaseStartTime
, startTime
, sizeof( startTime
) ),
11072 kGAIPerfTestCaseKey_EndTime
, _NanoTime64ToTimestamp( inCaseEndTime
, endTime
, sizeof( endTime
) ),
11073 kGAIPerfTestCaseKey_Results
, results
,
11074 kGAIPerfTestCaseKey_Stats
,
11075 kGAIPerfTestCaseStatsKey_Count
, (int64_t) count
,
11076 kGAIPerfTestCaseStatsKey_Min
, stats
.min
,
11077 kGAIPerfTestCaseStatsKey_Max
, stats
.max
,
11078 kGAIPerfTestCaseStatsKey_Mean
, stats
.mean
,
11079 kGAIPerfTestCaseStatsKey_StdDev
, stats
.stdDev
,
11080 kGAIPerfTestCaseKey_FirstStats
,
11081 kGAIPerfTestCaseStatsKey_Count
, (int64_t) count
,
11082 kGAIPerfTestCaseStatsKey_Min
, firstStats
.min
,
11083 kGAIPerfTestCaseStatsKey_Max
, firstStats
.max
,
11084 kGAIPerfTestCaseStatsKey_Mean
, firstStats
.mean
,
11085 kGAIPerfTestCaseStatsKey_StdDev
, firstStats
.stdDev
,
11086 kGAIPerfTestCaseKey_ConnectionStats
,
11087 kGAIPerfTestCaseStatsKey_Count
, (int64_t) count
,
11088 kGAIPerfTestCaseStatsKey_Min
, connStats
.min
,
11089 kGAIPerfTestCaseStatsKey_Max
, connStats
.max
,
11090 kGAIPerfTestCaseStatsKey_Mean
, connStats
.mean
,
11091 kGAIPerfTestCaseStatsKey_StdDev
, connStats
.stdDev
);
11092 require_noerr( err
, exit
);
11095 CFReleaseNullSafe( results
);
11096 if( err
) exit( 1 );
11099 //===========================================================================================================================
11100 // GAIPerfSignalHandler
11101 //===========================================================================================================================
11103 static void GAIPerfSignalHandler( void *inContext
)
11105 GAIPerfContext
* const context
= (GAIPerfContext
*) inContext
;
11107 if( !context
->tester
) exit( 1 );
11108 GAITesterStop( context
->tester
);
11109 context
->tester
= NULL
;
11112 //===========================================================================================================================
11114 //===========================================================================================================================
11116 // A character set of lower-case alphabet characters and digits and a string length of six allows for 36^6 = 2,176,782,336
11117 // possible strings to use in the Tag label.
11119 #define kGAITesterTagStringLen 6
11121 typedef struct GAITestItem GAITestItem
;
11124 GAITestItem
* next
; // Next test item in list.
11125 char * name
; // Domain name to resolve.
11126 uint64_t connectionTimeUs
; // Time in microseconds that it took to create a DNS-SD connection.
11127 uint64_t firstTimeUs
; // Time in microseconds that it took to get the first address result.
11128 uint64_t timeUs
; // Time in microseconds that it took to get all expected address results.
11129 unsigned int addressCount
; // Address count of the domain name, i.e., the Count label argument.
11130 OSStatus error
; // Current status/error.
11131 unsigned int timeLimitMs
; // Time limit in milliseconds for the test item's completion.
11132 Boolean hasV4
; // True if the domain name has one or more IPv4 addresses.
11133 Boolean hasV6
; // True if the domain name has one or more IPv6 addresses.
11134 Boolean wantV4
; // True if DNSServiceGetAddrInfo() should be called to get IPv4 addresses.
11135 Boolean wantV6
; // True if DNSServiceGetAddrInfo() should be called to get IPv6 addresses.
11140 GAITestCase
* next
; // Next test case in list.
11141 GAITestItem
* itemList
; // List of test items.
11142 char * title
; // Title of the test case.
11145 struct GAITesterPrivate
11147 CFRuntimeBase base
; // CF object base.
11148 dispatch_queue_t queue
; // Serial work queue.
11149 DNSServiceRef connection
; // Reference to the shared DNS-SD connection.
11150 DNSServiceRef getAddrInfo
; // Reference to the current DNSServiceGetAddrInfo operation.
11151 GAITestCase
* caseList
; // List of test cases.
11152 GAITestCase
* currentCase
; // Pointer to the current test case.
11153 GAITestItem
* currentItem
; // Pointer to the current test item.
11154 NanoTime64 caseStartTime
; // Start time of current test case in Unix time as nanoseconds.
11155 NanoTime64 caseEndTime
; // End time of current test case in Unix time as nanoseconds.
11156 unsigned int callDelayMs
; // Amount of time to wait before calling DNSServiceGetAddrInfo().
11157 Boolean skipPathEval
; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
11158 Boolean stopped
; // True if the tester has been stopped.
11159 Boolean badUDPMode
; // True if the test DNS server is to run in Bad UDP mode.
11160 dispatch_source_t timer
; // Timer for enforcing a test item's time limit.
11161 pcap_t
* pcap
; // Captures traffic between mDNSResponder and test DNS server.
11162 pid_t serverPID
; // PID of the test DNS server.
11163 int serverDelayMs
; // Additional time to have the server delay its responses by.
11164 int serverDefaultTTL
; // Default TTL for the server's records.
11165 GAITesterStopHandler_f stopHandler
; // User's stop handler.
11166 void * stopContext
; // User's event handler context.
11167 GAITesterResultsHandler_f resultsHandler
; // User's results handler.
11168 void * resultsContext
; // User's results handler context.
11170 // Variables for current test item.
11172 uint64_t bitmapV4
; // Bitmap of IPv4 results that have yet to be received.
11173 uint64_t bitmapV6
; // Bitmap of IPv6 results that have yet to be received.
11174 uint64_t startTicks
; // Start ticks of DNSServiceGetAddrInfo().
11175 uint64_t connTicks
; // Ticks when the connection was created.
11176 uint64_t firstTicks
; // Ticks when the first DNSServiceGetAddrInfo result was received.
11177 uint64_t endTicks
; // Ticks when the last DNSServiceGetAddrInfo result was received.
11178 Boolean gotFirstResult
; // True if the first result has been received.
11181 CF_CLASS_DEFINE( GAITester
);
11183 static void _GAITesterStartNextTest( GAITesterRef inTester
);
11184 static OSStatus
_GAITesterCreatePacketCapture( pcap_t
**outPCap
);
11185 static void _GAITesterFirstGAITimeout( void *inContext
);
11186 static void _GAITesterTimeout( void *inContext
);
11187 static void DNSSD_API
11188 _GAITesterFirstGAICallback(
11189 DNSServiceRef inSDRef
,
11190 DNSServiceFlags inFlags
,
11191 uint32_t inInterfaceIndex
,
11192 DNSServiceErrorType inError
,
11193 const char * inHostname
,
11194 const struct sockaddr
* inSockAddr
,
11196 void * inContext
);
11197 static void DNSSD_API
11198 _GAITesterGetAddrInfoCallback(
11199 DNSServiceRef inSDRef
,
11200 DNSServiceFlags inFlags
,
11201 uint32_t inInterfaceIndex
,
11202 DNSServiceErrorType inError
,
11203 const char * inHostname
,
11204 const struct sockaddr
* inSockAddr
,
11206 void * inContext
);
11207 static void _GAITesterCompleteCurrentTest( GAITesterRef inTester
, OSStatus inError
);
11209 #define ForgetPacketCapture( X ) ForgetCustom( X, pcap_close )
11213 const char * inName
,
11214 unsigned int inAddressCount
,
11215 GAITestAddrType inHasAddrs
,
11216 GAITestAddrType inWantAddrs
,
11217 unsigned int inTimeLimitMs
,
11218 GAITestItem
** outItem
);
11219 static OSStatus
GAITestItemDup( const GAITestItem
*inItem
, GAITestItem
**outItem
);
11220 static void GAITestItemFree( GAITestItem
*inItem
);
11224 dispatch_queue_t inQueue
,
11225 unsigned int inCallDelayMs
,
11226 int inServerDelayMs
,
11227 int inServerDefaultTTL
,
11228 Boolean inSkipPathEvaluation
,
11229 Boolean inBadUDPMode
,
11230 GAITesterRef
* outTester
)
11233 GAITesterRef obj
= NULL
;
11235 CF_OBJECT_CREATE( GAITester
, obj
, err
, exit
);
11237 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
11238 obj
->callDelayMs
= inCallDelayMs
;
11239 obj
->serverPID
= -1;
11240 obj
->serverDelayMs
= inServerDelayMs
;
11241 obj
->serverDefaultTTL
= inServerDefaultTTL
;
11242 obj
->skipPathEval
= inSkipPathEvaluation
;
11243 obj
->badUDPMode
= inBadUDPMode
;
11250 CFReleaseNullSafe( obj
);
11254 //===========================================================================================================================
11255 // _GAITesterFinalize
11256 //===========================================================================================================================
11258 static void _GAITesterFinalize( CFTypeRef inObj
)
11260 GAITesterRef
const me
= (GAITesterRef
) inObj
;
11261 GAITestCase
* testCase
;
11263 check( !me
->getAddrInfo
);
11264 check( !me
->connection
);
11265 check( !me
->timer
);
11266 dispatch_forget( &me
->queue
);
11267 while( ( testCase
= me
->caseList
) != NULL
)
11269 me
->caseList
= testCase
->next
;
11270 GAITestCaseFree( testCase
);
11274 //===========================================================================================================================
11276 //===========================================================================================================================
11278 static void _GAITesterStart( void *inContext
);
11279 static void _GAITesterStop( GAITesterRef me
, OSStatus inError
);
11281 static void GAITesterStart( GAITesterRef me
)
11284 dispatch_async_f( me
->queue
, me
, _GAITesterStart
);
11287 #define kGAITesterFirstGAITimeoutSecs 4
11289 static void _GAITesterStart( void *inContext
)
11292 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11293 DNSServiceFlags flags
;
11295 char tag
[ kGAITesterTagStringLen
+ 1 ];
11297 err
= SpawnCommand( &me
->serverPID
, "dnssdutil server --loopback --follow %lld%?s%?d%?s%?d%?s",
11298 (int64_t) getpid(),
11299 me
->serverDefaultTTL
>= 0, " --defaultTTL ",
11300 me
->serverDefaultTTL
>= 0, me
->serverDefaultTTL
,
11301 me
->serverDelayMs
>= 0, " --responseDelay ",
11302 me
->serverDelayMs
>= 0, me
->serverDelayMs
,
11303 me
->badUDPMode
, " --badUDPMode" );
11304 require_noerr_quiet( err
, exit
);
11306 SNPrintF( name
, sizeof( name
), "tag-gaitester-probe-%s.ipv4.d.test",
11307 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
11310 if( me
->skipPathEval
) flags
|= kDNSServiceFlagsPathEvaluationDone
;
11312 err
= DNSServiceGetAddrInfo( &me
->getAddrInfo
, flags
, kDNSServiceInterfaceIndexAny
, kDNSServiceProtocol_IPv4
, name
,
11313 _GAITesterFirstGAICallback
, me
);
11314 require_noerr( err
, exit
);
11316 err
= DNSServiceSetDispatchQueue( me
->getAddrInfo
, me
->queue
);
11317 require_noerr( err
, exit
);
11319 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kGAITesterFirstGAITimeoutSecs
),
11320 UINT64_C_safe( kGAITesterFirstGAITimeoutSecs
) * kNanosecondsPerSecond
/ 10, me
->queue
,
11321 _GAITesterFirstGAITimeout
, me
, &me
->timer
);
11322 require_noerr( err
, exit
);
11323 dispatch_resume( me
->timer
);
11326 if( err
) _GAITesterStop( me
, err
);
11329 //===========================================================================================================================
11331 //===========================================================================================================================
11333 static void _GAITesterUserStop( void *inContext
);
11335 static void GAITesterStop( GAITesterRef me
)
11338 dispatch_async_f( me
->queue
, me
, _GAITesterUserStop
);
11341 static void _GAITesterUserStop( void *inContext
)
11343 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11345 _GAITesterStop( me
, kCanceledErr
);
11349 static void _GAITesterStop( GAITesterRef me
, OSStatus inError
)
11353 ForgetPacketCapture( &me
->pcap
);
11354 dispatch_source_forget( &me
->timer
);
11355 DNSServiceForget( &me
->getAddrInfo
);
11356 DNSServiceForget( &me
->connection
);
11357 if( me
->serverPID
!= -1 )
11359 err
= kill( me
->serverPID
, SIGTERM
);
11360 err
= map_global_noerr_errno( err
);
11361 check_noerr( err
);
11362 me
->serverPID
= -1;
11367 me
->stopped
= true;
11368 if( me
->stopHandler
) me
->stopHandler( me
->stopContext
, inError
);
11373 //===========================================================================================================================
11374 // GAITesterAddTestCase
11375 //===========================================================================================================================
11377 static OSStatus
GAITesterAddTestCase( GAITesterRef me
, GAITestCase
*inCase
)
11380 GAITestCase
** ptr
;
11382 require_action_quiet( inCase
->itemList
, exit
, err
= kCountErr
);
11384 for( ptr
= &me
->caseList
; *ptr
; ptr
= &( *ptr
)->next
) {}
11392 //===========================================================================================================================
11393 // GAITesterSetStopHandler
11394 //===========================================================================================================================
11396 static void GAITesterSetStopHandler( GAITesterRef me
, GAITesterStopHandler_f inStopHandler
, void *inStopContext
)
11398 me
->stopHandler
= inStopHandler
;
11399 me
->stopContext
= inStopContext
;
11402 //===========================================================================================================================
11403 // GAITesterSetResultsHandler
11404 //===========================================================================================================================
11406 static void GAITesterSetResultsHandler( GAITesterRef me
, GAITesterResultsHandler_f inResultsHandler
, void *inResultsContext
)
11408 me
->resultsHandler
= inResultsHandler
;
11409 me
->resultsContext
= inResultsContext
;
11412 //===========================================================================================================================
11413 // _GAITesterStartNextTest
11414 //===========================================================================================================================
11416 static void _GAITesterStartNextTest( GAITesterRef me
)
11419 GAITestItem
* item
;
11420 DNSServiceFlags flags
;
11421 DNSServiceProtocol protocols
;
11424 if( me
->currentItem
) me
->currentItem
= me
->currentItem
->next
;
11426 if( !me
->currentItem
)
11428 if( me
->currentCase
)
11430 // No more test items means that the current test case has completed.
11432 me
->caseEndTime
= NanoTimeGetCurrent();
11434 if( me
->resultsHandler
)
11436 size_t resultCount
, i
;
11437 GAITestItemResult
* resultArray
;
11440 for( item
= me
->currentCase
->itemList
; item
; item
= item
->next
) ++resultCount
;
11441 check( resultCount
> 0 );
11443 resultArray
= (GAITestItemResult
*) calloc( resultCount
, sizeof( *resultArray
) );
11444 require_action( resultArray
, exit
, err
= kNoMemoryErr
);
11446 item
= me
->currentCase
->itemList
;
11447 for( i
= 0; i
< resultCount
; ++i
)
11449 resultArray
[ i
].name
= item
->name
;
11450 resultArray
[ i
].connectionTimeUs
= item
->connectionTimeUs
;
11451 resultArray
[ i
].firstTimeUs
= item
->firstTimeUs
;
11452 resultArray
[ i
].timeUs
= item
->timeUs
;
11453 resultArray
[ i
].error
= item
->error
;
11456 me
->resultsHandler( me
->currentCase
->title
, me
->caseStartTime
, me
->caseEndTime
, resultArray
, resultCount
,
11457 me
->resultsContext
);
11458 ForgetMem( &resultArray
);
11461 me
->currentCase
= me
->currentCase
->next
;
11462 if( !me
->currentCase
)
11471 me
->currentCase
= me
->caseList
;
11473 require_action_quiet( me
->currentCase
->itemList
, exit
, err
= kInternalErr
);
11474 me
->currentItem
= me
->currentCase
->itemList
;
11477 item
= me
->currentItem
;
11478 check( ( item
->addressCount
>= 1 ) && ( item
->addressCount
<= 64 ) );
11480 if( !item
->wantV4
) me
->bitmapV4
= 0;
11481 else if( !item
->hasV4
) me
->bitmapV4
= 1;
11482 else if( item
->addressCount
< 64 ) me
->bitmapV4
= ( UINT64_C( 1 ) << item
->addressCount
) - 1;
11483 else me
->bitmapV4
= ~UINT64_C( 0 );
11485 if( !item
->wantV6
) me
->bitmapV6
= 0;
11486 else if( !item
->hasV6
) me
->bitmapV6
= 1;
11487 else if( item
->addressCount
< 64 ) me
->bitmapV6
= ( UINT64_C( 1 ) << item
->addressCount
) - 1;
11488 else me
->bitmapV6
= ~UINT64_C( 0 );
11489 check( ( me
->bitmapV4
!= 0 ) || ( me
->bitmapV6
!= 0 ) );
11490 me
->gotFirstResult
= false;
11492 // Perform preliminary tasks if this is the start of a new test case.
11494 if( item
== me
->currentCase
->itemList
)
11496 // Flush mDNSResponder's cache.
11498 err
= systemf( NULL
, "killall -HUP mDNSResponder" );
11499 require_noerr( err
, exit
);
11502 me
->caseStartTime
= NanoTimeGetCurrent();
11503 me
->caseEndTime
= kNanoTime_Invalid
;
11506 // Start a packet capture.
11508 check( !me
->pcap
);
11509 err
= _GAITesterCreatePacketCapture( &me
->pcap
);
11510 require_noerr( err
, exit
);
11512 // Start timer for test item's time limit.
11514 check( !me
->timer
);
11515 if( item
->timeLimitMs
> 0 )
11517 unsigned int timeLimitMs
;
11519 timeLimitMs
= item
->timeLimitMs
;
11520 if( me
->callDelayMs
> 0 ) timeLimitMs
+= (unsigned int) me
->callDelayMs
;
11521 if( me
->serverDelayMs
> 0 ) timeLimitMs
+= (unsigned int) me
->serverDelayMs
;
11523 err
= DispatchTimerCreate( dispatch_time_milliseconds( timeLimitMs
), DISPATCH_TIME_FOREVER
,
11524 ( (uint64_t) timeLimitMs
) * kNanosecondsPerMillisecond
/ 10,
11525 me
->queue
, _GAITesterTimeout
, NULL
, me
, &me
->timer
);
11526 require_noerr( err
, exit
);
11527 dispatch_resume( me
->timer
);
11530 // Call DNSServiceGetAddrInfo().
11532 if( me
->callDelayMs
> 0 ) usleep( ( (useconds_t
) me
->callDelayMs
) * kMicrosecondsPerMillisecond
);
11534 flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsReturnIntermediates
;
11535 if( me
->skipPathEval
) flags
|= kDNSServiceFlagsPathEvaluationDone
;
11538 if( item
->wantV4
) protocols
|= kDNSServiceProtocol_IPv4
;
11539 if( item
->wantV6
) protocols
|= kDNSServiceProtocol_IPv6
;
11541 me
->startTicks
= UpTicks();
11543 check( !me
->connection
);
11544 err
= DNSServiceCreateConnection( &me
->connection
);
11545 require_noerr( err
, exit
);
11547 err
= DNSServiceSetDispatchQueue( me
->connection
, me
->queue
);
11548 require_noerr( err
, exit
);
11550 me
->connTicks
= UpTicks();
11552 check( !me
->getAddrInfo
);
11553 me
->getAddrInfo
= me
->connection
;
11554 err
= DNSServiceGetAddrInfo( &me
->getAddrInfo
, flags
, kDNSServiceInterfaceIndexAny
, protocols
, item
->name
,
11555 _GAITesterGetAddrInfoCallback
, me
);
11556 require_noerr( err
, exit
);
11559 if( err
|| done
) _GAITesterStop( me
, err
);
11562 //===========================================================================================================================
11563 // _GAITesterCreatePacketCapture
11564 //===========================================================================================================================
11566 static OSStatus
_GAITesterCreatePacketCapture( pcap_t
**outPCap
)
11570 struct bpf_program program
;
11571 char errBuf
[ PCAP_ERRBUF_SIZE
];
11573 pcap
= pcap_create( "lo0", errBuf
);
11574 require_action_string( pcap
, exit
, err
= kUnknownErr
, errBuf
);
11576 err
= pcap_set_buffer_size( pcap
, 512 * kBytesPerKiloByte
);
11577 require_noerr_action( err
, exit
, err
= kUnknownErr
);
11579 err
= pcap_set_snaplen( pcap
, 512 );
11580 require_noerr_action( err
, exit
, err
= kUnknownErr
);
11582 err
= pcap_set_immediate_mode( pcap
, 0 );
11583 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11585 err
= pcap_activate( pcap
);
11586 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11588 err
= pcap_setdirection( pcap
, PCAP_D_INOUT
);
11589 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11591 err
= pcap_setnonblock( pcap
, 1, errBuf
);
11592 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, errBuf
);
11594 err
= pcap_compile( pcap
, &program
, "udp port 53", 1, PCAP_NETMASK_UNKNOWN
);
11595 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11597 err
= pcap_setfilter( pcap
, &program
);
11598 pcap_freecode( &program
);
11599 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
11605 if( pcap
) pcap_close( pcap
);
11609 //===========================================================================================================================
11610 // _GAITesterFirstGAITimeout
11611 //===========================================================================================================================
11613 static void _GAITesterFirstGAITimeout( void *inContext
)
11615 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11617 _GAITesterStop( me
, kNoResourcesErr
);
11620 //===========================================================================================================================
11621 // _GAITesterTimeout
11622 //===========================================================================================================================
11624 static void _GAITesterTimeout( void *inContext
)
11626 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11628 _GAITesterCompleteCurrentTest( me
, kTimeoutErr
);
11631 //===========================================================================================================================
11632 // _GAITesterFirstGAICallback
11633 //===========================================================================================================================
11635 static void DNSSD_API
11636 _GAITesterFirstGAICallback(
11637 DNSServiceRef inSDRef
,
11638 DNSServiceFlags inFlags
,
11639 uint32_t inInterfaceIndex
,
11640 DNSServiceErrorType inError
,
11641 const char * inHostname
,
11642 const struct sockaddr
* inSockAddr
,
11646 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11649 Unused( inInterfaceIndex
);
11650 Unused( inHostname
);
11651 Unused( inSockAddr
);
11654 if( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
)
11656 dispatch_source_forget( &me
->timer
);
11657 DNSServiceForget( &me
->getAddrInfo
);
11659 _GAITesterStartNextTest( me
);
11663 //===========================================================================================================================
11664 // _GAITesterGetAddrInfoCallback
11665 //===========================================================================================================================
11667 static void DNSSD_API
11668 _GAITesterGetAddrInfoCallback(
11669 DNSServiceRef inSDRef
,
11670 DNSServiceFlags inFlags
,
11671 uint32_t inInterfaceIndex
,
11672 DNSServiceErrorType inError
,
11673 const char * inHostname
,
11674 const struct sockaddr
* inSockAddr
,
11679 GAITesterRef
const me
= (GAITesterRef
) inContext
;
11680 GAITestItem
* const item
= me
->currentItem
;
11681 const sockaddr_ip
* const sip
= (const sockaddr_ip
*) inSockAddr
;
11683 uint64_t * bitmapPtr
;
11688 Unused( inInterfaceIndex
);
11689 Unused( inHostname
);
11692 nowTicks
= UpTicks();
11694 require_action_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
, err
= kFlagErr
);
11696 // Check if we were expecting an IP address result of this type.
11698 if( sip
->sa
.sa_family
== AF_INET
)
11700 bitmapPtr
= &me
->bitmapV4
;
11701 hasAddr
= item
->hasV4
;
11703 else if( sip
->sa
.sa_family
== AF_INET6
)
11705 bitmapPtr
= &me
->bitmapV6
;
11706 hasAddr
= item
->hasV6
;
11717 uint32_t addrOffset
;
11719 require_noerr_action_quiet( inError
, exit
, err
= inError
);
11721 if( sip
->sa
.sa_family
== AF_INET
)
11723 const uint32_t addrV4
= ntohl( sip
->v4
.sin_addr
.s_addr
);
11725 if( strcasecmp( item
->name
, "localhost." ) == 0 )
11727 if( addrV4
== INADDR_LOOPBACK
) bitmask
= 1;
11731 addrOffset
= addrV4
- kDNSServerBaseAddrV4
;
11732 if( ( addrOffset
>= 1 ) && ( addrOffset
<= item
->addressCount
) )
11734 bitmask
= UINT64_C( 1 ) << ( addrOffset
- 1 );
11740 const uint8_t * const addrV6
= sip
->v6
.sin6_addr
.s6_addr
;
11742 if( strcasecmp( item
->name
, "localhost." ) == 0 )
11744 if( memcmp( addrV6
, in6addr_loopback
.s6_addr
, 16 ) == 0 ) bitmask
= 1;
11746 else if( memcmp( addrV6
, kDNSServerBaseAddrV6
, 15 ) == 0 )
11748 addrOffset
= addrV6
[ 15 ];
11749 if( ( addrOffset
>= 1 ) && ( addrOffset
<= item
->addressCount
) )
11751 bitmask
= UINT64_C( 1 ) << ( addrOffset
- 1 );
11758 require_action_quiet( inError
== kDNSServiceErr_NoSuchRecord
, exit
, err
= inError
? inError
: kUnexpectedErr
);
11761 require_action_quiet( bitmask
!= 0, exit
, err
= kValueErr
);
11762 require_action_quiet( *bitmapPtr
& bitmask
, exit
, err
= kDuplicateErr
);
11764 *bitmapPtr
&= ~bitmask
;
11765 if( !me
->gotFirstResult
)
11767 me
->firstTicks
= nowTicks
;
11768 me
->gotFirstResult
= true;
11773 if( err
|| ( ( me
->bitmapV4
== 0 ) && ( me
->bitmapV6
== 0 ) ) )
11775 me
->endTicks
= nowTicks
;
11776 _GAITesterCompleteCurrentTest( me
, err
);
11780 //===========================================================================================================================
11781 // _GAITesterCompleteCurrentTest
11782 //===========================================================================================================================
11785 _GAITesterGetDNSMessageFromPacket(
11786 const uint8_t * inPacketPtr
,
11787 size_t inPacketLen
,
11788 const uint8_t ** outMsgPtr
,
11789 size_t * outMsgLen
);
11791 static void _GAITesterCompleteCurrentTest( GAITesterRef me
, OSStatus inError
)
11794 GAITestItem
* const item
= me
->currentItem
;
11795 struct timeval timeStamps
[ 4 ];
11796 struct timeval
* tsPtr
;
11797 struct timeval
* tsQA
= NULL
;
11798 struct timeval
* tsQAAAA
= NULL
;
11799 struct timeval
* tsRA
= NULL
;
11800 struct timeval
* tsRAAAA
= NULL
;
11801 struct timeval
* t1
;
11802 struct timeval
* t2
;
11803 int64_t idleTimeUs
;
11804 uint8_t name
[ kDomainNameLengthMax
];
11806 dispatch_source_forget( &me
->timer
);
11807 DNSServiceForget( &me
->getAddrInfo
);
11808 DNSServiceForget( &me
->connection
);
11810 item
->error
= inError
;
11817 err
= DomainNameFromString( name
, item
->name
, NULL
);
11818 require_noerr( err
, exit
);
11820 tsPtr
= &timeStamps
[ 0 ];
11824 struct pcap_pkthdr
* pktHdr
;
11825 const uint8_t * packet
;
11826 const uint8_t * msgPtr
;
11828 const DNSHeader
* hdr
;
11829 unsigned int flags
;
11830 const uint8_t * ptr
;
11831 uint16_t qtype
, qclass
;
11832 uint8_t qname
[ kDomainNameLengthMax
];
11834 status
= pcap_next_ex( me
->pcap
, &pktHdr
, &packet
);
11835 if( status
!= 1 ) break;
11836 if( _GAITesterGetDNSMessageFromPacket( packet
, pktHdr
->caplen
, &msgPtr
, &msgLen
) != kNoErr
) continue;
11837 if( msgLen
< kDNSHeaderLength
) continue;
11839 hdr
= (const DNSHeader
*) msgPtr
;
11840 flags
= DNSHeaderGetFlags( hdr
);
11841 if( DNSFlagsGetOpCode( flags
) != kDNSOpCode_Query
) continue;
11842 if( DNSHeaderGetQuestionCount( hdr
) < 1 ) continue;
11844 ptr
= (const uint8_t *) &hdr
[ 1 ];
11845 if( DNSMessageExtractQuestion( msgPtr
, msgLen
, ptr
, qname
, &qtype
, &qclass
, NULL
) != kNoErr
) continue;
11846 if( qclass
!= kDNSServiceClass_IN
) continue;
11847 if( !DomainNameEqual( qname
, name
) ) continue;
11849 if( item
->wantV4
&& ( qtype
== kDNSServiceType_A
) )
11851 if( flags
& kDNSHeaderFlag_Response
)
11853 if( tsQA
&& !tsRA
)
11856 *tsRA
= pktHdr
->ts
;
11862 *tsQA
= pktHdr
->ts
;
11865 else if( item
->wantV6
&& ( qtype
== kDNSServiceType_AAAA
) )
11867 if( flags
& kDNSHeaderFlag_Response
)
11869 if( tsQAAAA
&& !tsRAAAA
)
11872 *tsRAAAA
= pktHdr
->ts
;
11875 else if( !tsQAAAA
)
11878 *tsQAAAA
= pktHdr
->ts
;
11883 // t1 is the time when the last query was sent.
11885 if( tsQA
&& tsQAAAA
) t1
= TIMEVAL_GT( *tsQA
, *tsQAAAA
) ? tsQA
: tsQAAAA
;
11886 else t1
= tsQA
? tsQA
: tsQAAAA
;
11888 // t2 is when the first response was received.
11890 if( tsRA
&& tsRAAAA
) t2
= TIMEVAL_LT( *tsRA
, *tsRAAAA
) ? tsRA
: tsRAAAA
;
11891 else t2
= tsRA
? tsRA
: tsRAAAA
;
11895 idleTimeUs
= TIMEVAL_USEC64_DIFF( *t2
, *t1
);
11896 if( idleTimeUs
< 0 ) idleTimeUs
= 0;
11903 item
->connectionTimeUs
= UpTicksToMicroseconds( me
->connTicks
- me
->startTicks
);
11904 item
->firstTimeUs
= UpTicksToMicroseconds( me
->firstTicks
- me
->connTicks
) - (uint64_t) idleTimeUs
;
11905 item
->timeUs
= UpTicksToMicroseconds( me
->endTicks
- me
->connTicks
) - (uint64_t) idleTimeUs
;
11908 ForgetPacketCapture( &me
->pcap
);
11909 if( err
) _GAITesterStop( me
, err
);
11910 else _GAITesterStartNextTest( me
);
11913 //===========================================================================================================================
11914 // _GAITesterGetDNSMessageFromPacket
11915 //===========================================================================================================================
11917 #define kHeaderSizeNullLink 4
11918 #define kHeaderSizeIPv4Min 20
11919 #define kHeaderSizeIPv6 40
11920 #define kHeaderSizeUDP 8
11922 #define kIPProtocolUDP 0x11
11925 _GAITesterGetDNSMessageFromPacket(
11926 const uint8_t * inPacketPtr
,
11927 size_t inPacketLen
,
11928 const uint8_t ** outMsgPtr
,
11929 size_t * outMsgLen
)
11932 const uint8_t * nullLink
;
11933 uint32_t addressFamily
;
11934 const uint8_t * ip
;
11937 const uint8_t * msg
;
11938 const uint8_t * const end
= &inPacketPtr
[ inPacketLen
];
11940 nullLink
= &inPacketPtr
[ 0 ];
11941 require_action_quiet( ( end
- nullLink
) >= kHeaderSizeNullLink
, exit
, err
= kUnderrunErr
);
11942 addressFamily
= ReadHost32( &nullLink
[ 0 ] );
11944 ip
= &nullLink
[ kHeaderSizeNullLink
];
11945 if( addressFamily
== AF_INET
)
11947 require_action_quiet( ( end
- ip
) >= kHeaderSizeIPv4Min
, exit
, err
= kUnderrunErr
);
11948 ipHeaderLen
= ( ip
[ 0 ] & 0x0F ) * 4;
11949 protocol
= ip
[ 9 ];
11951 else if( addressFamily
== AF_INET6
)
11953 require_action_quiet( ( end
- ip
) >= kHeaderSizeIPv6
, exit
, err
= kUnderrunErr
);
11954 ipHeaderLen
= kHeaderSizeIPv6
;
11955 protocol
= ip
[ 6 ];
11962 require_action_quiet( protocol
== kIPProtocolUDP
, exit
, err
= kTypeErr
);
11963 require_action_quiet( ( end
- ip
) >= ( ipHeaderLen
+ kHeaderSizeUDP
), exit
, err
= kUnderrunErr
);
11965 msg
= &ip
[ ipHeaderLen
+ kHeaderSizeUDP
];
11968 *outMsgLen
= (size_t)( end
- msg
);
11975 //===========================================================================================================================
11976 // GAITestCaseCreate
11977 //===========================================================================================================================
11979 static OSStatus
GAITestCaseCreate( const char *inTitle
, GAITestCase
**outCase
)
11984 obj
= (GAITestCase
*) calloc( 1, sizeof( *obj
) );
11985 require_action( obj
, exit
, err
= kNoMemoryErr
);
11987 obj
->title
= strdup( inTitle
);
11988 require_action( obj
->title
, exit
, err
= kNoMemoryErr
);
11995 if( obj
) GAITestCaseFree( obj
);
11999 //===========================================================================================================================
12001 //===========================================================================================================================
12003 static void GAITestCaseFree( GAITestCase
*inCase
)
12005 GAITestItem
* item
;
12007 while( ( item
= inCase
->itemList
) != NULL
)
12009 inCase
->itemList
= item
->next
;
12010 GAITestItemFree( item
);
12012 ForgetMem( &inCase
->title
);
12016 //===========================================================================================================================
12017 // GAITestCaseAddItem
12018 //===========================================================================================================================
12021 GAITestCaseAddItem(
12022 GAITestCase
* inCase
,
12023 unsigned int inAliasCount
,
12024 unsigned int inAddressCount
,
12026 GAITestAddrType inHasAddrs
,
12027 GAITestAddrType inWantAddrs
,
12028 unsigned int inTimeLimitMs
,
12029 unsigned int inItemCount
)
12032 GAITestItem
* item
;
12033 GAITestItem
* item2
;
12034 GAITestItem
* newItemList
= NULL
;
12035 GAITestItem
** itemPtr
;
12040 char tag
[ kGAITesterTagStringLen
+ 1 ];
12042 require_action_quiet( inItemCount
> 0, exit
, err
= kNoErr
);
12044 // Limit address count to 64 because we use 64-bit bitmaps for keeping track of addresses.
12046 require_action_quiet( ( inAddressCount
>= 1 ) && ( inAddressCount
<= 64 ), exit
, err
= kCountErr
);
12047 require_action_quiet( ( inAliasCount
>= 0 ) && ( inAliasCount
<= INT32_MAX
), exit
, err
= kCountErr
);
12048 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs
), exit
, err
= kValueErr
);
12051 end
= &name
[ countof( name
) ];
12053 // Add Alias label.
12055 if( inAliasCount
== 1 ) SNPrintF_Add( &ptr
, end
, "alias." );
12056 else if( inAliasCount
>= 2 ) SNPrintF_Add( &ptr
, end
, "alias-%u.", inAliasCount
);
12058 // Add Count label.
12060 SNPrintF_Add( &ptr
, end
, "count-%u.", inAddressCount
);
12064 if( inTTL
>= 0 ) SNPrintF_Add( &ptr
, end
, "ttl-%d.", inTTL
);
12068 SNPrintF_Add( &ptr
, end
, "tag-%s.",
12069 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
12071 // Add IPv4 or IPv6 label if necessary.
12073 if( inHasAddrs
== kGAITestAddrType_IPv4
) SNPrintF_Add( &ptr
, end
, "ipv4." );
12074 else if( inHasAddrs
== kGAITestAddrType_IPv6
) SNPrintF_Add( &ptr
, end
, "ipv6." );
12076 // Finally, add the d.test. labels.
12078 SNPrintF_Add( &ptr
, end
, "d.test." );
12082 err
= GAITestItemCreate( name
, inAddressCount
, inHasAddrs
, inWantAddrs
, inTimeLimitMs
, &item
);
12083 require_noerr( err
, exit
);
12085 newItemList
= item
;
12086 itemPtr
= &item
->next
;
12088 // Create repeat items.
12090 for( i
= 1; i
< inItemCount
; ++i
)
12092 err
= GAITestItemDup( item
, &item2
);
12093 require_noerr( err
, exit
);
12096 itemPtr
= &item2
->next
;
12099 // Append to test case's item list.
12101 for( itemPtr
= &inCase
->itemList
; *itemPtr
; itemPtr
= &( *itemPtr
)->next
) {}
12102 *itemPtr
= newItemList
;
12103 newItemList
= NULL
;
12106 while( ( item
= newItemList
) != NULL
)
12108 newItemList
= item
->next
;
12109 GAITestItemFree( item
);
12114 //===========================================================================================================================
12115 // GAITestCaseAddLocalHostItem
12116 //===========================================================================================================================
12119 GAITestCaseAddLocalHostItem(
12120 GAITestCase
* inCase
,
12121 GAITestAddrType inWantAddrs
,
12122 unsigned int inTimeLimitMs
,
12123 unsigned int inItemCount
)
12126 GAITestItem
* item
;
12127 GAITestItem
* item2
;
12128 GAITestItem
* newItemList
= NULL
;
12129 GAITestItem
** itemPtr
;
12132 require_action_quiet( inItemCount
> 1, exit
, err
= kNoErr
);
12134 err
= GAITestItemCreate( "localhost.", 1, kGAITestAddrType_Both
, inWantAddrs
, inTimeLimitMs
, &item
);
12135 require_noerr( err
, exit
);
12137 newItemList
= item
;
12138 itemPtr
= &item
->next
;
12140 // Create repeat items.
12142 for( i
= 1; i
< inItemCount
; ++i
)
12144 err
= GAITestItemDup( item
, &item2
);
12145 require_noerr( err
, exit
);
12148 itemPtr
= &item2
->next
;
12151 for( itemPtr
= &inCase
->itemList
; *itemPtr
; itemPtr
= &( *itemPtr
)->next
) {}
12152 *itemPtr
= newItemList
;
12153 newItemList
= NULL
;
12156 while( ( item
= newItemList
) != NULL
)
12158 newItemList
= item
->next
;
12159 GAITestItemFree( item
);
12164 //===========================================================================================================================
12165 // GAITestItemCreate
12166 //===========================================================================================================================
12170 const char * inName
,
12171 unsigned int inAddressCount
,
12172 GAITestAddrType inHasAddrs
,
12173 GAITestAddrType inWantAddrs
,
12174 unsigned int inTimeLimitMs
,
12175 GAITestItem
** outItem
)
12178 GAITestItem
* obj
= NULL
;
12180 require_action_quiet( inAddressCount
>= 1, exit
, err
= kCountErr
);
12181 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs
), exit
, err
= kValueErr
);
12182 require_action_quiet( GAITestAddrTypeIsValid( inWantAddrs
), exit
, err
= kValueErr
);
12184 obj
= (GAITestItem
*) calloc( 1, sizeof( *obj
) );
12185 require_action( obj
, exit
, err
= kNoMemoryErr
);
12187 obj
->name
= strdup( inName
);
12188 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
12190 obj
->addressCount
= inAddressCount
;
12191 obj
->hasV4
= ( inHasAddrs
& kGAITestAddrType_IPv4
) ? true : false;
12192 obj
->hasV6
= ( inHasAddrs
& kGAITestAddrType_IPv6
) ? true : false;
12193 obj
->wantV4
= ( inWantAddrs
& kGAITestAddrType_IPv4
) ? true : false;
12194 obj
->wantV6
= ( inWantAddrs
& kGAITestAddrType_IPv6
) ? true : false;
12195 obj
->error
= kInProgressErr
;
12196 obj
->timeLimitMs
= inTimeLimitMs
;
12203 if( obj
) GAITestItemFree( obj
);
12207 //===========================================================================================================================
12209 //===========================================================================================================================
12211 static OSStatus
GAITestItemDup( const GAITestItem
*inItem
, GAITestItem
**outItem
)
12216 obj
= (GAITestItem
*) calloc( 1, sizeof( *obj
) );
12217 require_action( obj
, exit
, err
= kNoMemoryErr
);
12223 obj
->name
= strdup( inItem
->name
);
12224 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
12232 if( obj
) GAITestItemFree( obj
);
12236 //===========================================================================================================================
12238 //===========================================================================================================================
12240 static void GAITestItemFree( GAITestItem
*inItem
)
12242 ForgetMem( &inItem
->name
);
12246 //===========================================================================================================================
12247 // MDNSDiscoveryTestCmd
12248 //===========================================================================================================================
12250 #define kMDNSDiscoveryTestFirstQueryTimeoutSecs 4
12254 DNSServiceRef query
; // Reference to DNSServiceQueryRecord for replier's "about" TXT record.
12255 dispatch_source_t queryTimer
; // Used to time out the "about" TXT record query.
12256 NanoTime64 startTime
; // When the test started.
12257 NanoTime64 endTime
; // When the test ended.
12258 pid_t replierPID
; // PID of mDNS replier.
12259 uint32_t ifIndex
; // Index of interface to run the replier on.
12260 unsigned int instanceCount
; // Desired number of service instances.
12261 unsigned int txtSize
; // Desired size of each service instance's TXT record data.
12262 unsigned int recordCountA
; // Desired number of A records per replier hostname.
12263 unsigned int recordCountAAAA
; // Desired number of AAAA records per replier hostname.
12264 unsigned int maxDropCount
; // Replier's --maxDropCount option argument.
12265 double ucastDropRate
; // Replier's probability of dropping a unicast response.
12266 double mcastDropRate
; // Replier's probability of dropping a multicast query or response.
12267 Boolean noAdditionals
; // True if the replier is to not include additional records in responses.
12268 Boolean useIPv4
; // True if the replier is to use IPv4.
12269 Boolean useIPv6
; // True if the replier is to use IPv6.
12270 Boolean flushedCache
; // True if mDNSResponder's record cache was flushed before testing.
12271 char * replierCommand
; // Command used to run the replier.
12272 char * serviceType
; // Type of services to browse for.
12273 ServiceBrowserRef browser
; // Service browser.
12274 unsigned int browseTimeSecs
; // Amount of time to spend browsing in seconds.
12275 const char * outputFilePath
; // File to write test results to. If NULL, then write to stdout.
12276 OutputFormatType outputFormat
; // Format of test results output.
12277 Boolean outputAppendNewline
; // True if a newline character should be appended to JSON output.
12278 char hostname
[ 32 + 1 ]; // Base hostname that the replier is to use for instance and host names.
12279 char tag
[ 4 + 1 ]; // Tag that the replier is to use in its service types.
12281 } MDNSDiscoveryTestContext
;
12283 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext
);
12284 static void DNSSD_API
12285 _MDNSDiscoveryTestAboutQueryCallback(
12286 DNSServiceRef inSDRef
,
12287 DNSServiceFlags inFlags
,
12288 uint32_t inInterfaceIndex
,
12289 DNSServiceErrorType inError
,
12290 const char * inFullName
,
12293 uint16_t inRDataLen
,
12294 const void * inRDataPtr
,
12296 void * inContext
);
12298 _MDNSDiscoveryTestServiceBrowserCallback(
12299 ServiceBrowserResults
* inResults
,
12301 void * inContext
);
12302 static Boolean
_MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName
, const uint8_t *inTXTPtr
, size_t inTXTLen
);
12304 static void MDNSDiscoveryTestCmd( void )
12307 MDNSDiscoveryTestContext
* context
;
12308 char queryName
[ sizeof_field( MDNSDiscoveryTestContext
, hostname
) + 15 ];
12310 context
= (MDNSDiscoveryTestContext
*) calloc( 1, sizeof( *context
) );
12311 require_action( context
, exit
, err
= kNoMemoryErr
);
12313 err
= CheckIntegerArgument( gMDNSDiscoveryTest_InstanceCount
, "instance count", 1, UINT16_MAX
);
12314 require_noerr_quiet( err
, exit
);
12316 err
= CheckIntegerArgument( gMDNSDiscoveryTest_TXTSize
, "TXT size", 1, UINT16_MAX
);
12317 require_noerr_quiet( err
, exit
);
12319 err
= CheckIntegerArgument( gMDNSDiscoveryTest_BrowseTimeSecs
, "browse time (seconds)", 1, INT_MAX
);
12320 require_noerr_quiet( err
, exit
);
12322 err
= CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountA
, "A record count", 0, 64 );
12323 require_noerr_quiet( err
, exit
);
12325 err
= CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountAAAA
, "AAAA record count", 0, 64 );
12326 require_noerr_quiet( err
, exit
);
12328 err
= CheckDoubleArgument( gMDNSDiscoveryTest_UnicastDropRate
, "unicast drop rate", 0.0, 1.0 );
12329 require_noerr_quiet( err
, exit
);
12331 err
= CheckDoubleArgument( gMDNSDiscoveryTest_MulticastDropRate
, "multicast drop rate", 0.0, 1.0 );
12332 require_noerr_quiet( err
, exit
);
12334 err
= CheckIntegerArgument( gMDNSDiscoveryTest_MaxDropCount
, "drop count", 0, 255 );
12335 require_noerr_quiet( err
, exit
);
12337 context
->replierPID
= -1;
12338 context
->instanceCount
= (unsigned int) gMDNSDiscoveryTest_InstanceCount
;
12339 context
->txtSize
= (unsigned int) gMDNSDiscoveryTest_TXTSize
;
12340 context
->browseTimeSecs
= (unsigned int) gMDNSDiscoveryTest_BrowseTimeSecs
;
12341 context
->recordCountA
= (unsigned int) gMDNSDiscoveryTest_RecordCountA
;
12342 context
->recordCountAAAA
= (unsigned int) gMDNSDiscoveryTest_RecordCountAAAA
;
12343 context
->ucastDropRate
= gMDNSDiscoveryTest_UnicastDropRate
;
12344 context
->mcastDropRate
= gMDNSDiscoveryTest_MulticastDropRate
;
12345 context
->maxDropCount
= (unsigned int) gMDNSDiscoveryTest_MaxDropCount
;
12346 context
->outputFilePath
= gMDNSDiscoveryTest_OutputFilePath
;
12347 context
->outputAppendNewline
= gMDNSDiscoveryTest_OutputAppendNewline
? true : false;
12348 context
->noAdditionals
= gMDNSDiscoveryTest_NoAdditionals
? true : false;
12349 context
->useIPv4
= ( gMDNSDiscoveryTest_UseIPv4
|| !gMDNSDiscoveryTest_UseIPv6
) ? true : false;
12350 context
->useIPv6
= ( gMDNSDiscoveryTest_UseIPv6
|| !gMDNSDiscoveryTest_UseIPv4
) ? true : false;
12352 if( gMDNSDiscoveryTest_Interface
)
12354 err
= InterfaceIndexFromArgString( gMDNSDiscoveryTest_Interface
, &context
->ifIndex
);
12355 require_noerr_quiet( err
, exit
);
12359 err
= _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All
, NULL
, &context
->ifIndex
);
12360 require_noerr_quiet( err
, exit
);
12363 err
= OutputFormatFromArgString( gMDNSDiscoveryTest_OutputFormat
, &context
->outputFormat
);
12364 require_noerr_quiet( err
, exit
);
12366 if( gMDNSDiscoveryTest_FlushCache
)
12368 err
= CheckRootUser();
12369 require_noerr_quiet( err
, exit
);
12371 err
= systemf( NULL
, "killall -HUP mDNSResponder" );
12372 require_noerr( err
, exit
);
12374 context
->flushedCache
= true;
12377 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( context
->hostname
) - 1,
12378 context
->hostname
);
12379 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( context
->tag
) - 1, context
->tag
);
12381 ASPrintF( &context
->serviceType
, "_t-%s-%u-%u._tcp", context
->tag
, context
->txtSize
, context
->instanceCount
);
12382 require_action( context
->serviceType
, exit
, err
= kUnknownErr
);
12384 ASPrintF( &context
->replierCommand
,
12385 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount %u "
12386 "--countA %u --countAAAA %u --udrop %.1f --mdrop %.1f --maxDropCount %u %?s%?s%?s",
12387 (int64_t) getpid(),
12391 context
->instanceCount
,
12392 context
->recordCountA
,
12393 context
->recordCountAAAA
,
12394 context
->ucastDropRate
,
12395 context
->mcastDropRate
,
12396 context
->maxDropCount
,
12397 context
->noAdditionals
, " --noAdditionals",
12398 context
->useIPv4
, " --ipv4",
12399 context
->useIPv6
, " --ipv6" );
12400 require_action_quiet( context
->replierCommand
, exit
, err
= kUnknownErr
);
12402 err
= SpawnCommand( &context
->replierPID
, "%s", context
->replierCommand
);
12403 require_noerr_quiet( err
, exit
);
12405 // Query for the replier's about TXT record. A response means it's fully up and running.
12407 SNPrintF( queryName
, sizeof( queryName
), "about.%s.local.", context
->hostname
);
12408 err
= DNSServiceQueryRecord( &context
->query
, kDNSServiceFlagsForceMulticast
, context
->ifIndex
, queryName
,
12409 kDNSServiceType_TXT
, kDNSServiceClass_IN
, _MDNSDiscoveryTestAboutQueryCallback
, context
);
12410 require_noerr( err
, exit
);
12412 err
= DNSServiceSetDispatchQueue( context
->query
, dispatch_get_main_queue() );
12413 require_noerr( err
, exit
);
12415 err
= DispatchTimerCreate( dispatch_time_seconds( kMDNSDiscoveryTestFirstQueryTimeoutSecs
),
12416 DISPATCH_TIME_FOREVER
, UINT64_C_safe( kMDNSDiscoveryTestFirstQueryTimeoutSecs
) * kNanosecondsPerSecond
/ 10, NULL
,
12417 _MDNSDiscoveryTestFirstQueryTimeout
, NULL
, context
, &context
->queryTimer
);
12418 require_noerr( err
, exit
);
12419 dispatch_resume( context
->queryTimer
);
12421 context
->startTime
= NanoTimeGetCurrent();
12428 //===========================================================================================================================
12429 // _MDNSDiscoveryTestFirstQueryTimeout
12430 //===========================================================================================================================
12432 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext
)
12434 MDNSDiscoveryTestContext
* const context
= (MDNSDiscoveryTestContext
*) inContext
;
12436 dispatch_source_forget( &context
->queryTimer
);
12438 FPrintF( stderr
, "error: Query for mdnsreplier's \"about\" TXT record timed out.\n" );
12442 //===========================================================================================================================
12443 // _MDNSDiscoveryTestAboutQueryCallback
12444 //===========================================================================================================================
12446 static void DNSSD_API
12447 _MDNSDiscoveryTestAboutQueryCallback(
12448 DNSServiceRef inSDRef
,
12449 DNSServiceFlags inFlags
,
12450 uint32_t inInterfaceIndex
,
12451 DNSServiceErrorType inError
,
12452 const char * inFullName
,
12455 uint16_t inRDataLen
,
12456 const void * inRDataPtr
,
12461 MDNSDiscoveryTestContext
* const context
= (MDNSDiscoveryTestContext
*) inContext
;
12464 Unused( inInterfaceIndex
);
12465 Unused( inFullName
);
12468 Unused( inRDataLen
);
12469 Unused( inRDataPtr
);
12473 require_noerr( err
, exit
);
12474 require_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
);
12476 DNSServiceForget( &context
->query
);
12477 dispatch_source_forget( &context
->queryTimer
);
12479 err
= ServiceBrowserCreate( dispatch_get_main_queue(), 0, "local.", context
->browseTimeSecs
, false, &context
->browser
);
12480 require_noerr( err
, exit
);
12482 err
= ServiceBrowserAddServiceType( context
->browser
, context
->serviceType
);
12483 require_noerr( err
, exit
);
12485 ServiceBrowserSetCallback( context
->browser
, _MDNSDiscoveryTestServiceBrowserCallback
, context
);
12486 ServiceBrowserStart( context
->browser
);
12489 if( err
) exit( 1 );
12492 //===========================================================================================================================
12493 // _MDNSDiscoveryTestServiceBrowserCallback
12494 //===========================================================================================================================
12496 #define kMDNSDiscoveryTestResultsKey_ReplierInfo CFSTR( "replierInfo" )
12497 #define kMDNSDiscoveryTestResultsKey_StartTime CFSTR( "startTime" )
12498 #define kMDNSDiscoveryTestResultsKey_EndTime CFSTR( "endTime" )
12499 #define kMDNSDiscoveryTestResultsKey_BrowseTimeSecs CFSTR( "browseTimeSecs" )
12500 #define kMDNSDiscoveryTestResultsKey_ServiceType CFSTR( "serviceType" )
12501 #define kMDNSDiscoveryTestResultsKey_FlushedCache CFSTR( "flushedCache" )
12502 #define kMDNSDiscoveryTestResultsKey_UnexpectedInstances CFSTR( "unexpectedInstances" )
12503 #define kMDNSDiscoveryTestResultsKey_MissingInstances CFSTR( "missingInstances" )
12504 #define kMDNSDiscoveryTestResultsKey_IncorrectInstances CFSTR( "incorrectInstances" )
12505 #define kMDNSDiscoveryTestResultsKey_Success CFSTR( "success" )
12506 #define kMDNSDiscoveryTestResultsKey_TotalResolveTime CFSTR( "totalResolveTimeUs" )
12508 #define kMDNSDiscoveryTestReplierInfoKey_Command CFSTR( "command" )
12509 #define kMDNSDiscoveryTestReplierInfoKey_InstanceCount CFSTR( "instanceCount" )
12510 #define kMDNSDiscoveryTestReplierInfoKey_TXTSize CFSTR( "txtSize" )
12511 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountA CFSTR( "recordCountA" )
12512 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA CFSTR( "recordCountAAAA" )
12513 #define kMDNSDiscoveryTestReplierInfoKey_Hostname CFSTR( "hostname" )
12514 #define kMDNSDiscoveryTestReplierInfoKey_NoAdditionals CFSTR( "noAdditionals" )
12515 #define kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate CFSTR( "ucastDropRate" )
12516 #define kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate CFSTR( "mcastDropRate" )
12517 #define kMDNSDiscoveryTestReplierInfoKey_MaxDropCount CFSTR( "maxDropCount" )
12519 #define kMDNSDiscoveryTestUnexpectedInstanceKey_Name CFSTR( "name" )
12520 #define kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex CFSTR( "interfaceIndex" )
12522 #define kMDNSDiscoveryTestIncorrectInstanceKey_Name CFSTR( "name" )
12523 #define kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve CFSTR( "didResolve" )
12524 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname CFSTR( "badHostname" )
12525 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadPort CFSTR( "badPort" )
12526 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT CFSTR( "badTXT" )
12527 #define kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs CFSTR( "unexpectedAddrs" )
12528 #define kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs CFSTR( "missingAddrs" )
12530 static void _MDNSDiscoveryTestServiceBrowserCallback( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
)
12533 MDNSDiscoveryTestContext
* const context
= (MDNSDiscoveryTestContext
*) inContext
;
12534 const SBRDomain
* domain
;
12535 const SBRServiceType
* type
;
12536 const SBRServiceInstance
* instance
;
12537 const SBRServiceInstance
** instanceArray
= NULL
;
12538 const SBRIPAddress
* ipaddr
;
12539 size_t hostnameLen
;
12544 CFMutableArrayRef unexpectedInstances
;
12545 CFMutableArrayRef missingInstances
;
12546 CFMutableArrayRef incorrectInstances
;
12547 CFMutableDictionaryRef plist
= NULL
;
12548 CFMutableDictionaryRef badDict
= NULL
;
12549 CFMutableArrayRef unexpectedAddrs
= NULL
;
12550 CFMutableArrayRef missingAddrs
= NULL
;
12551 uint64_t maxResolveTimeUs
;
12552 int success
= false;
12553 char startTime
[ 32 ];
12554 char endTime
[ 32 ];
12556 context
->endTime
= NanoTimeGetCurrent();
12559 require_noerr( err
, exit
);
12561 _NanoTime64ToTimestamp( context
->startTime
, startTime
, sizeof( startTime
) );
12562 _NanoTime64ToTimestamp( context
->endTime
, endTime
, sizeof( endTime
) );
12563 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
12567 "%kO=%s" // replierCommand
12568 "%kO=%lli" // txtSize
12569 "%kO=%lli" // instanceCount
12570 "%kO=%lli" // recordCountA
12571 "%kO=%lli" // recordCountAAAA
12572 "%kO=%s" // hostname
12573 "%kO=%b" // noAdditionals
12574 "%kO=%f" // ucastDropRate
12575 "%kO=%f" // mcastDropRate
12576 "%kO=%i" // maxDropCount
12578 "%kO=%s" // startTime
12579 "%kO=%s" // endTime
12580 "%kO=%lli" // browseTimeSecs
12581 "%kO=%s" // serviceType
12582 "%kO=%b" // flushedCache
12583 "%kO=[%@]" // unexpectedInstances
12584 "%kO=[%@]" // missingInstances
12585 "%kO=[%@]" // incorrectInstances
12587 kMDNSDiscoveryTestResultsKey_ReplierInfo
,
12588 kMDNSDiscoveryTestReplierInfoKey_Command
, context
->replierCommand
,
12589 kMDNSDiscoveryTestReplierInfoKey_InstanceCount
, (int64_t) context
->instanceCount
,
12590 kMDNSDiscoveryTestReplierInfoKey_TXTSize
, (int64_t) context
->txtSize
,
12591 kMDNSDiscoveryTestReplierInfoKey_RecordCountA
, (int64_t) context
->recordCountA
,
12592 kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA
, (int64_t) context
->recordCountAAAA
,
12593 kMDNSDiscoveryTestReplierInfoKey_Hostname
, context
->hostname
,
12594 kMDNSDiscoveryTestReplierInfoKey_NoAdditionals
, context
->noAdditionals
,
12595 kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate
, context
->ucastDropRate
,
12596 kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate
, context
->mcastDropRate
,
12597 kMDNSDiscoveryTestReplierInfoKey_MaxDropCount
, context
->maxDropCount
,
12598 kMDNSDiscoveryTestResultsKey_StartTime
, startTime
,
12599 kMDNSDiscoveryTestResultsKey_EndTime
, endTime
,
12600 kMDNSDiscoveryTestResultsKey_BrowseTimeSecs
, (int64_t) context
->browseTimeSecs
,
12601 kMDNSDiscoveryTestResultsKey_ServiceType
, context
->serviceType
,
12602 kMDNSDiscoveryTestResultsKey_FlushedCache
, context
->flushedCache
,
12603 kMDNSDiscoveryTestResultsKey_UnexpectedInstances
, &unexpectedInstances
,
12604 kMDNSDiscoveryTestResultsKey_MissingInstances
, &missingInstances
,
12605 kMDNSDiscoveryTestResultsKey_IncorrectInstances
, &incorrectInstances
);
12606 require_noerr( err
, exit
);
12608 for( domain
= inResults
->domainList
; domain
&& ( strcasecmp( domain
->name
, "local." ) != 0 ); domain
= domain
->next
) {}
12609 require_action( domain
, exit
, err
= kInternalErr
);
12611 for( type
= domain
->typeList
; type
&& ( strcasecmp( type
->name
, context
->serviceType
) != 0 ); type
= type
->next
) {}
12612 require_action( type
, exit
, err
= kInternalErr
);
12614 instanceArray
= (const SBRServiceInstance
**) calloc( context
->instanceCount
, sizeof( *instanceArray
) );
12615 require_action( instanceArray
, exit
, err
= kNoMemoryErr
);
12617 hostnameLen
= strlen( context
->hostname
);
12618 for( instance
= type
->instanceList
; instance
; instance
= instance
->next
)
12620 unsigned int instanceNumber
= 0;
12622 if( strcmp_prefix( instance
->name
, context
->hostname
) == 0 )
12624 ptr
= &instance
->name
[ hostnameLen
];
12625 if( ( ptr
[ 0 ] == ' ' ) && ( ptr
[ 1 ] == '(' ) )
12628 for( end
= ptr
; isdigit_safe( *end
); ++end
) {}
12629 if( DecimalTextToUInt32( ptr
, end
, &u32
, &ptr
) == kNoErr
)
12631 if( ( u32
>= 2 ) && ( u32
<= context
->instanceCount
) && ( ptr
[ 0 ] == ')' ) && ( ptr
[ 1 ] == '\0' ) )
12633 instanceNumber
= u32
;
12637 else if( *ptr
== '\0' )
12639 instanceNumber
= 1;
12642 if( ( instanceNumber
!= 0 ) && ( instance
->ifIndex
== context
->ifIndex
) )
12644 check( !instanceArray
[ instanceNumber
- 1 ] );
12645 instanceArray
[ instanceNumber
- 1 ] = instance
;
12649 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, unexpectedInstances
,
12654 kMDNSDiscoveryTestUnexpectedInstanceKey_Name
, instance
->name
,
12655 kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex
, (int64_t) instance
->ifIndex
);
12656 require_noerr( err
, exit
);
12660 maxResolveTimeUs
= 0;
12661 for( i
= 1; i
<= context
->instanceCount
; ++i
)
12663 int isHostnameValid
;
12666 instance
= instanceArray
[ i
- 1 ];
12671 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingInstances
, "%s", context
->hostname
);
12672 require_noerr( err
, exit
);
12676 char * instanceName
= NULL
;
12678 ASPrintF( &instanceName
, "%s (%u)", context
->hostname
, i
);
12679 require_action( instanceName
, exit
, err
= kUnknownErr
);
12681 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingInstances
, "%s", instanceName
);
12682 free( instanceName
);
12683 require_noerr( err
, exit
);
12688 if( !instance
->hostname
)
12690 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, incorrectInstances
,
12695 kMDNSDiscoveryTestIncorrectInstanceKey_Name
, instance
->name
,
12696 kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve
, false );
12697 require_noerr( err
, exit
);
12701 badDict
= CFDictionaryCreateMutable( NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
12702 require_action( badDict
, exit
, err
= kNoMemoryErr
);
12704 isHostnameValid
= false;
12705 if( strcmp_prefix( instance
->hostname
, context
->hostname
) == 0 )
12707 ptr
= &instance
->hostname
[ hostnameLen
];
12710 if( strcmp( ptr
, ".local." ) == 0 ) isHostnameValid
= true;
12712 else if( *ptr
== '-' )
12715 for( end
= ptr
; isdigit_safe( *end
); ++end
) {}
12716 if( DecimalTextToUInt32( ptr
, end
, &u32
, &ptr
) == kNoErr
)
12718 if( ( u32
== i
) && ( strcmp( ptr
, ".local." ) == 0 ) ) isHostnameValid
= true;
12722 if( !isHostnameValid
)
12724 err
= CFDictionarySetCString( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname
, instance
->hostname
,
12726 require_noerr( err
, exit
);
12729 if( instance
->port
!= (uint16_t)( kMDNSReplierPortBase
+ context
->txtSize
) )
12731 err
= CFDictionarySetInt64( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_BadPort
, instance
->port
);
12732 require_noerr( err
, exit
);
12735 isTXTValid
= false;
12736 if( instance
->txtLen
== context
->txtSize
)
12738 uint8_t name
[ kDomainNameLengthMax
];
12740 err
= DomainNameFromString( name
, instance
->name
, NULL
);
12741 require_noerr( err
, exit
);
12743 err
= DomainNameAppendString( name
, type
->name
, NULL
);
12744 require_noerr( err
, exit
);
12746 err
= DomainNameAppendString( name
, "local", NULL
);
12747 require_noerr( err
, exit
);
12749 if( _MDNSDiscoveryTestTXTRecordIsValid( name
, instance
->txtPtr
, instance
->txtLen
) ) isTXTValid
= true;
12753 char * hexStr
= NULL
;
12755 ASPrintF( &hexStr
, "%.4H", instance
->txtPtr
, (int) instance
->txtLen
, (int) instance
->txtLen
);
12756 require_action( hexStr
, exit
, err
= kUnknownErr
);
12758 err
= CFDictionarySetCString( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT
, hexStr
, kSizeCString
);
12760 require_noerr( err
, exit
);
12763 if( isHostnameValid
)
12765 uint64_t addrV4Bitmap
, addrV6Bitmap
, bitmask
, resolveTimeUs
;
12767 uint8_t addrV4
[ 4 ];
12768 uint8_t addrV6
[ 16 ];
12770 if( context
->recordCountA
< 64 ) addrV4Bitmap
= ( UINT64_C( 1 ) << context
->recordCountA
) - 1;
12771 else addrV4Bitmap
= ~UINT64_C( 0 );
12773 if( context
->recordCountAAAA
< 64 ) addrV6Bitmap
= ( UINT64_C( 1 ) << context
->recordCountAAAA
) - 1;
12774 else addrV6Bitmap
= ~UINT64_C( 0 );
12777 WriteBig16( &addrV4
[ 1 ], i
);
12780 memcpy( addrV6
, kMDNSReplierBaseAddrV6
, 16 );
12781 WriteBig16( &addrV6
[ 12 ], i
);
12783 unexpectedAddrs
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
12784 require_action( unexpectedAddrs
, exit
, err
= kNoMemoryErr
);
12787 for( ipaddr
= instance
->ipaddrList
; ipaddr
; ipaddr
= ipaddr
->next
)
12789 const uint8_t * addrPtr
;
12791 int isAddrValid
= false;
12793 if( ipaddr
->sip
.sa
.sa_family
== AF_INET
)
12795 addrPtr
= (const uint8_t *) &ipaddr
->sip
.v4
.sin_addr
.s_addr
;
12796 lsb
= addrPtr
[ 3 ];
12797 if( ( memcmp( addrPtr
, addrV4
, 3 ) == 0 ) && ( lsb
>= 1 ) && ( lsb
<= context
->recordCountA
) )
12799 bitmask
= UINT64_C( 1 ) << ( lsb
- 1 );
12800 addrV4Bitmap
&= ~bitmask
;
12801 isAddrValid
= true;
12804 else if( ipaddr
->sip
.sa
.sa_family
== AF_INET6
)
12806 addrPtr
= ipaddr
->sip
.v6
.sin6_addr
.s6_addr
;
12807 lsb
= addrPtr
[ 15 ];
12808 if( ( memcmp( addrPtr
, addrV6
, 15 ) == 0 ) && ( lsb
>= 1 ) && ( lsb
<= context
->recordCountAAAA
) )
12810 bitmask
= UINT64_C( 1 ) << ( lsb
- 1 );
12811 addrV6Bitmap
&= ~bitmask
;
12812 isAddrValid
= true;
12817 if( ipaddr
->resolveTimeUs
> resolveTimeUs
) resolveTimeUs
= ipaddr
->resolveTimeUs
;
12821 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, unexpectedAddrs
, "%##a", &ipaddr
->sip
);
12822 require_noerr( err
, exit
);
12826 resolveTimeUs
+= ( instance
->discoverTimeUs
+ instance
->resolveTimeUs
);
12827 if( resolveTimeUs
> maxResolveTimeUs
) maxResolveTimeUs
= resolveTimeUs
;
12829 if( CFArrayGetCount( unexpectedAddrs
) > 0 )
12831 CFDictionarySetValue( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs
, unexpectedAddrs
);
12833 ForgetCF( &unexpectedAddrs
);
12835 missingAddrs
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
12836 require_action( missingAddrs
, exit
, err
= kNoMemoryErr
);
12838 for( j
= 1; addrV4Bitmap
!= 0; ++j
)
12840 bitmask
= UINT64_C( 1 ) << ( j
- 1 );
12841 if( addrV4Bitmap
& bitmask
)
12843 addrV4Bitmap
&= ~bitmask
;
12844 addrV4
[ 3 ] = (uint8_t) j
;
12845 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingAddrs
, "%.4a", addrV4
);
12846 require_noerr( err
, exit
);
12849 for( j
= 1; addrV6Bitmap
!= 0; ++j
)
12851 bitmask
= UINT64_C( 1 ) << ( j
- 1 );
12852 if( addrV6Bitmap
& bitmask
)
12854 addrV6Bitmap
&= ~bitmask
;
12855 addrV6
[ 15 ] = (uint8_t) j
;
12856 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingAddrs
, "%.16a", addrV6
);
12857 require_noerr( err
, exit
);
12861 if( CFArrayGetCount( missingAddrs
) > 0 )
12863 CFDictionarySetValue( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs
, missingAddrs
);
12865 ForgetCF( &missingAddrs
);
12868 if( CFDictionaryGetCount( badDict
) > 0 )
12870 err
= CFDictionarySetCString( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_Name
, instance
->name
,
12872 require_noerr( err
, exit
);
12874 CFDictionarySetBoolean( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve
, true );
12875 CFArrayAppendValue( incorrectInstances
, badDict
);
12877 ForgetCF( &badDict
);
12880 if( ( CFArrayGetCount( unexpectedInstances
) == 0 ) &&
12881 ( CFArrayGetCount( missingInstances
) == 0 ) &&
12882 ( CFArrayGetCount( incorrectInstances
) == 0 ) )
12884 err
= CFDictionarySetInt64( plist
, kMDNSDiscoveryTestResultsKey_TotalResolveTime
, (int64_t) maxResolveTimeUs
);
12885 require_noerr( err
, exit
);
12892 CFDictionarySetBoolean( plist
, kMDNSDiscoveryTestResultsKey_Success
, success
);
12894 err
= OutputPropertyList( plist
, context
->outputFormat
, context
->outputFilePath
);
12895 require_noerr_quiet( err
, exit
);
12898 ForgetCF( &context
->browser
);
12899 if( context
->replierPID
!= -1 )
12901 kill( context
->replierPID
, SIGTERM
);
12902 context
->replierPID
= -1;
12904 FreeNullSafe( instanceArray
);
12905 CFReleaseNullSafe( plist
);
12906 CFReleaseNullSafe( badDict
);
12907 CFReleaseNullSafe( unexpectedAddrs
);
12908 CFReleaseNullSafe( missingAddrs
);
12909 exit( err
? 1 : ( success
? 0 : 2 ) );
12912 //===========================================================================================================================
12913 // _MDNSDiscoveryTestTXTRecordIsValid
12914 //===========================================================================================================================
12916 static Boolean
_MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName
, const uint8_t *inTXTPtr
, size_t inTXTLen
)
12920 const uint8_t * ptr
;
12921 size_t i
, wholeCount
, remCount
;
12922 uint8_t txtStr
[ 16 ];
12924 if( inTXTLen
== 0 ) return( false );
12926 hash
= _FNV1( inRecordName
, DomainNameLength( inRecordName
) );
12929 n
= MemPrintF( &txtStr
[ 1 ], 15, "hash=0x%08X", hash
);
12933 wholeCount
= inTXTLen
/ 16;
12934 for( i
= 0; i
< wholeCount
; ++i
)
12936 if( memcmp( ptr
, txtStr
, 16 ) != 0 ) return( false );
12940 remCount
= inTXTLen
% 16;
12943 txtStr
[ 0 ] = (uint8_t)( remCount
- 1 );
12944 if( memcmp( ptr
, txtStr
, remCount
) != 0 ) return( false );
12947 check( ptr
== &inTXTPtr
[ inTXTLen
] );
12951 //===========================================================================================================================
12953 //===========================================================================================================================
12955 #define kDotLocalTestPreparationTimeLimitSecs 5
12956 #define kDotLocalTestSubtestDurationSecs 5
12958 // Constants for SRV record query subtest.
12960 #define kDotLocalTestSRV_Priority 1
12961 #define kDotLocalTestSRV_Weight 0
12962 #define kDotLocalTestSRV_Port 80
12963 #define kDotLocalTestSRV_TargetName ( (const uint8_t *) "\x03" "www" "\x07" "example" "\x03" "com" )
12964 #define kDotLocalTestSRV_TargetStr "www.example.com."
12965 #define kDotLocalTestSRV_ResultStr "1 0 80 " kDotLocalTestSRV_TargetStr
12969 kDotLocalTestState_Unset
= 0,
12970 kDotLocalTestState_Preparing
= 1,
12971 kDotLocalTestState_GAIMDNSOnly
= 2,
12972 kDotLocalTestState_GAIDNSOnly
= 3,
12973 kDotLocalTestState_GAIBoth
= 4,
12974 kDotLocalTestState_GAINeither
= 5,
12975 kDotLocalTestState_GAINoSuchRecord
= 6,
12976 kDotLocalTestState_QuerySRV
= 7,
12977 kDotLocalTestState_Done
= 8
12979 } DotLocalTestState
;
12983 const char * testDesc
; // Description of the current subtest.
12984 char * queryName
; // Query name for GetAddrInfo or QueryRecord operation.
12985 dispatch_source_t timer
; // Timer used for limiting the time for each subtest.
12986 NanoTime64 startTime
; // Timestamp of when the subtest started.
12987 NanoTime64 endTime
; // Timestamp of when the subtest ended.
12988 CFMutableArrayRef correctResults
; // Operation results that were expected.
12989 CFMutableArrayRef duplicateResults
; // Operation results that were expected, but were already received.
12990 CFMutableArrayRef unexpectedResults
; // Operation results that were unexpected.
12991 OSStatus error
; // Subtest's error code.
12992 uint32_t addrDNSv4
; // If hasDNSv4 is true, the expected DNS IPv4 address for queryName.
12993 uint32_t addrMDNSv4
; // If hasMDNSv4 is true, the expected MDNS IPv4 address for queryName.
12994 uint8_t addrDNSv6
[ 16 ]; // If hasDNSv6 is true, the expected DNS IPv6 address for queryName.
12995 uint8_t addrMDNSv6
[ 16 ]; // If hasMDNSv6 is true, the expected MDNS IPv6 address for queryName.
12996 Boolean hasDNSv4
; // True if queryName has a DNS IPv4 address.
12997 Boolean hasDNSv6
; // True if queryName has a DNS IPv6 address.
12998 Boolean hasMDNSv4
; // True if queryName has an MDNS IPv4 address.
12999 Boolean hasMDNSv6
; // True if queryName has an MDNS IPv6 address.
13000 Boolean needDNSv4
; // True if operation is expecting, but hasn't received a DNS IPv4 result.
13001 Boolean needDNSv6
; // True if operation is expecting, but hasn't received a DNS IPv6 result.
13002 Boolean needMDNSv4
; // True if operation is expecting, but hasn't received an MDNS IPv4 result.
13003 Boolean needMDNSv6
; // True if operation is expecting, but hasn't received an MDNS IPv6 result.
13004 Boolean needSRV
; // True if operation is expecting, but hasn't received an SRV result.
13010 dispatch_source_t timer
; // Timer used for limiting the time for each state/subtest.
13011 DotLocalSubtest
* subtest
; // Current subtest's state.
13012 DNSServiceRef connection
; // Shared connection for DNS-SD operations.
13013 DNSServiceRef op
; // Reference for the current DNS-SD operation.
13014 DNSServiceRef op2
; // Reference for mdnsreplier probe query used during preparing state.
13015 DNSRecordRef localSOARef
; // Reference returned by DNSServiceRegisterRecord() for local. SOA record.
13016 char * replierCmd
; // Command used to invoke the mdnsreplier.
13017 char * serverCmd
; // Command used to invoke the test DNS server.
13018 CFMutableArrayRef reportsGAI
; // Reports for subtests that use DNSServiceGetAddrInfo.
13019 CFMutableArrayRef reportsQuerySRV
; // Reports for subtests that use DNSServiceQueryRecord for SRV records.
13020 NanoTime64 startTime
; // Timestamp for when the test started.
13021 NanoTime64 endTime
; // Timestamp for when the test ended.
13022 DotLocalTestState state
; // The test's current state.
13023 pid_t replierPID
; // PID of spawned mdnsreplier.
13024 pid_t serverPID
; // PID of spawned test DNS server.
13025 uint32_t ifIndex
; // Interface index used for mdnsreplier.
13026 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout.
13027 OutputFormatType outputFormat
; // Format of test results output.
13028 Boolean registeredSOA
; // True if the dummy local. SOA record was successfully registered.
13029 Boolean serverIsReady
; // True if response was received for test DNS server probe query.
13030 Boolean replierIsReady
; // True if response was received for mdnsreplier probe query.
13031 Boolean testFailed
; // True if at least one subtest failed.
13032 char labelStr
[ 20 + 1 ]; // Unique label string used for for making the query names used by subtests.
13033 // The format of this string is "dotlocal-test-<six random chars>".
13034 } DotLocalTestContext
;
13036 static void _DotLocalTestStateMachine( DotLocalTestContext
*inContext
);
13037 static void DNSSD_API
13038 _DotLocalTestProbeQueryRecordCallback(
13039 DNSServiceRef inSDRef
,
13040 DNSServiceFlags inFlags
,
13041 uint32_t inInterfaceIndex
,
13042 DNSServiceErrorType inError
,
13043 const char * inFullName
,
13046 uint16_t inRDataLen
,
13047 const void * inRDataPtr
,
13049 void * inContext
);
13050 static void DNSSD_API
13051 _DotLocalTestRegisterRecordCallback(
13052 DNSServiceRef inSDRef
,
13053 DNSRecordRef inRecordRef
,
13054 DNSServiceFlags inFlags
,
13055 DNSServiceErrorType inError
,
13056 void * inContext
);
13057 static void _DotLocalTestTimerHandler( void *inContext
);
13058 static void DNSSD_API
13059 _DotLocalTestGAICallback(
13060 DNSServiceRef inSDRef
,
13061 DNSServiceFlags inFlags
,
13062 uint32_t inInterfaceIndex
,
13063 DNSServiceErrorType inError
,
13064 const char * inHostname
,
13065 const struct sockaddr
* inSockAddr
,
13067 void * inContext
);
13068 static void DNSSD_API
13069 _DotLocalTestQueryRecordCallback(
13070 DNSServiceRef inSDRef
,
13071 DNSServiceFlags inFlags
,
13072 uint32_t inInterfaceIndex
,
13073 DNSServiceErrorType inError
,
13074 const char * inFullName
,
13077 uint16_t inRDataLen
,
13078 const void * inRDataPtr
,
13080 void * inContext
);
13082 static void DotLocalTestCmd( void )
13085 DotLocalTestContext
* context
;
13086 uint8_t * rdataPtr
;
13088 DNSServiceFlags flags
;
13089 char queryName
[ 64 ];
13090 char randBuf
[ 6 + 1 ]; // Large enough for four and six character random strings below.
13092 context
= (DotLocalTestContext
*) calloc( 1, sizeof( *context
) );
13093 require_action( context
, exit
, err
= kNoMemoryErr
);
13095 context
->startTime
= NanoTimeGetCurrent();
13096 context
->endTime
= kNanoTime_Invalid
;
13098 context
->state
= kDotLocalTestState_Preparing
;
13100 if( gDotLocalTest_Interface
)
13102 err
= InterfaceIndexFromArgString( gDotLocalTest_Interface
, &context
->ifIndex
);
13103 require_noerr_quiet( err
, exit
);
13107 err
= _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All
, NULL
, &context
->ifIndex
);
13108 require_noerr_quiet( err
, exit
);
13111 if( gDotLocalTest_OutputFilePath
)
13113 context
->outputFilePath
= strdup( gDotLocalTest_OutputFilePath
);
13114 require_action( context
->outputFilePath
, exit
, err
= kNoMemoryErr
);
13117 err
= OutputFormatFromArgString( gDotLocalTest_OutputFormat
, &context
->outputFormat
);
13118 require_noerr_quiet( err
, exit
);
13120 context
->reportsGAI
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13121 require_action( context
->reportsGAI
, exit
, err
= kNoMemoryErr
);
13123 context
->reportsQuerySRV
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13124 require_action( context
->reportsQuerySRV
, exit
, err
= kNoMemoryErr
);
13126 SNPrintF( context
->labelStr
, sizeof( context
->labelStr
), "dotlocal-test-%s",
13127 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, 6, randBuf
) );
13129 // Spawn an mdnsreplier.
13131 ASPrintF( &context
->replierCmd
,
13132 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount 2 --countA 1"
13134 (int64_t) getpid(), context
->ifIndex
, context
->labelStr
,
13135 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, 4, randBuf
) );
13136 require_action_quiet( context
->replierCmd
, exit
, err
= kUnknownErr
);
13138 err
= SpawnCommand( &context
->replierPID
, "%s", context
->replierCmd
);
13139 require_noerr( err
, exit
);
13141 // Spawn a test DNS server
13143 ASPrintF( &context
->serverCmd
,
13144 "dnssdutil server --loopback --follow %lld --port 0 --defaultTTL 300 --domain %s.local.",
13145 (int64_t) getpid(), context
->labelStr
);
13146 require_action_quiet( context
->serverCmd
, exit
, err
= kUnknownErr
);
13148 err
= SpawnCommand( &context
->serverPID
, "%s", context
->serverCmd
);
13149 require_noerr( err
, exit
);
13151 // Create a shared DNS-SD connection.
13153 err
= DNSServiceCreateConnection( &context
->connection
);
13154 require_noerr( err
, exit
);
13156 err
= DNSServiceSetDispatchQueue( context
->connection
, dispatch_get_main_queue() );
13157 require_noerr( err
, exit
);
13159 // Create probe query for DNS server, i.e., query for any name that has an A record.
13161 SNPrintF( queryName
, sizeof( queryName
), "tag-dotlocal-test-probe.ipv4.%s.local.", context
->labelStr
);
13163 flags
= kDNSServiceFlagsShareConnection
;
13164 #if( TARGET_OS_WATCH )
13165 flags
|= kDNSServiceFlagsPathEvaluationDone
;
13168 context
->op
= context
->connection
;
13169 err
= DNSServiceQueryRecord( &context
->op
, flags
, kDNSServiceInterfaceIndexAny
, queryName
, kDNSServiceType_A
,
13170 kDNSServiceClass_IN
, _DotLocalTestProbeQueryRecordCallback
, context
);
13171 require_noerr( err
, exit
);
13173 // Create probe query for mdnsreplier's "about" TXT record.
13175 SNPrintF( queryName
, sizeof( queryName
), "about.%s.local.", context
->labelStr
);
13177 flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsForceMulticast
;
13178 #if( TARGET_OS_WATCH )
13179 flags
|= kDNSServiceFlagsPathEvaluationDone
;
13182 context
->op2
= context
->connection
;
13183 err
= DNSServiceQueryRecord( &context
->op2
, flags
, context
->ifIndex
, queryName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
,
13184 _DotLocalTestProbeQueryRecordCallback
, context
);
13185 require_noerr( err
, exit
);
13187 // Register a dummy local. SOA record.
13189 err
= CreateSOARecordData( kRootLabel
, kRootLabel
, 1976040101, 1 * kSecondsPerDay
, 2 * kSecondsPerHour
,
13190 1000 * kSecondsPerHour
, 2 * kSecondsPerDay
, &rdataPtr
, &rdataLen
);
13191 require_noerr( err
, exit
);
13193 err
= DNSServiceRegisterRecord( context
->connection
, &context
->localSOARef
, kDNSServiceFlagsUnique
,
13194 kDNSServiceInterfaceIndexLocalOnly
, "local.", kDNSServiceType_SOA
, kDNSServiceClass_IN
, 1,
13195 rdataPtr
, 1 * kSecondsPerHour
, _DotLocalTestRegisterRecordCallback
, context
);
13196 require_noerr( err
, exit
);
13198 // Start timer for probe responses and SOA record registration.
13200 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestPreparationTimeLimitSecs
),
13201 INT64_C_safe( kDotLocalTestPreparationTimeLimitSecs
) * kNanosecondsPerSecond
/ 10, dispatch_get_main_queue(),
13202 _DotLocalTestTimerHandler
, context
, &context
->timer
);
13203 require_noerr( err
, exit
);
13204 dispatch_resume( context
->timer
);
13209 if( err
) ErrQuit( 1, "error: %#m\n", err
);
13212 //===========================================================================================================================
13213 // _DotLocalTestStateMachine
13214 //===========================================================================================================================
13216 static OSStatus
_DotLocalSubtestCreate( DotLocalSubtest
**outSubtest
);
13217 static void _DotLocalSubtestFree( DotLocalSubtest
*inSubtest
);
13218 static OSStatus
_DotLocalTestStartSubtest( DotLocalTestContext
*inContext
);
13219 static OSStatus
_DotLocalTestFinalizeSubtest( DotLocalTestContext
*inContext
);
13220 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext
*inContext
) ATTRIBUTE_NORETURN
;
13222 static void _DotLocalTestStateMachine( DotLocalTestContext
*inContext
)
13225 DotLocalTestState nextState
;
13227 DNSServiceForget( &inContext
->op
);
13228 DNSServiceForget( &inContext
->op2
);
13229 dispatch_source_forget( &inContext
->timer
);
13231 switch( inContext
->state
)
13233 case kDotLocalTestState_Preparing
: nextState
= kDotLocalTestState_GAIMDNSOnly
; break;
13234 case kDotLocalTestState_GAIMDNSOnly
: nextState
= kDotLocalTestState_GAIDNSOnly
; break;
13235 case kDotLocalTestState_GAIDNSOnly
: nextState
= kDotLocalTestState_GAIBoth
; break;
13236 case kDotLocalTestState_GAIBoth
: nextState
= kDotLocalTestState_GAINeither
; break;
13237 case kDotLocalTestState_GAINeither
: nextState
= kDotLocalTestState_GAINoSuchRecord
; break;
13238 case kDotLocalTestState_GAINoSuchRecord
: nextState
= kDotLocalTestState_QuerySRV
; break;
13239 case kDotLocalTestState_QuerySRV
: nextState
= kDotLocalTestState_Done
; break;
13240 default: err
= kStateErr
; goto exit
;
13243 if( inContext
->state
== kDotLocalTestState_Preparing
)
13245 if( !inContext
->registeredSOA
|| !inContext
->serverIsReady
|| !inContext
->replierIsReady
)
13247 FPrintF( stderr
, "Preparation timed out: Registered SOA? %s. Server ready? %s. mdnsreplier ready? %s.\n",
13248 YesNoStr( inContext
->registeredSOA
),
13249 YesNoStr( inContext
->serverIsReady
),
13250 YesNoStr( inContext
->replierIsReady
) );
13251 err
= kNotPreparedErr
;
13257 err
= _DotLocalTestFinalizeSubtest( inContext
);
13258 require_noerr( err
, exit
);
13261 inContext
->state
= nextState
;
13262 if( inContext
->state
== kDotLocalTestState_Done
) _DotLocalTestFinalizeAndExit( inContext
);
13263 err
= _DotLocalTestStartSubtest( inContext
);
13266 if( err
) ErrQuit( 1, "error: %#m\n", err
);
13269 //===========================================================================================================================
13270 // _DotLocalSubtestCreate
13271 //===========================================================================================================================
13273 static OSStatus
_DotLocalSubtestCreate( DotLocalSubtest
**outSubtest
)
13276 DotLocalSubtest
* obj
;
13278 obj
= (DotLocalSubtest
*) calloc( 1, sizeof( *obj
) );
13279 require_action( obj
, exit
, err
= kNoMemoryErr
);
13281 obj
->correctResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13282 require_action( obj
->correctResults
, exit
, err
= kNoMemoryErr
);
13284 obj
->duplicateResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13285 require_action( obj
->duplicateResults
, exit
, err
= kNoMemoryErr
);
13287 obj
->unexpectedResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13288 require_action( obj
->unexpectedResults
, exit
, err
= kNoMemoryErr
);
13295 if( obj
) _DotLocalSubtestFree( obj
);
13299 //===========================================================================================================================
13300 // _DotLocalSubtestFree
13301 //===========================================================================================================================
13303 static void _DotLocalSubtestFree( DotLocalSubtest
*inSubtest
)
13305 ForgetMem( &inSubtest
->queryName
);
13306 ForgetCF( &inSubtest
->correctResults
);
13307 ForgetCF( &inSubtest
->duplicateResults
);
13308 ForgetCF( &inSubtest
->unexpectedResults
);
13312 //===========================================================================================================================
13313 // _DotLocalTestStartSubtest
13314 //===========================================================================================================================
13316 static OSStatus
_DotLocalTestStartSubtest( DotLocalTestContext
*inContext
)
13319 DotLocalSubtest
* subtest
= NULL
;
13320 DNSServiceRef op
= NULL
;
13321 DNSServiceFlags flags
;
13323 err
= _DotLocalSubtestCreate( &subtest
);
13324 require_noerr( err
, exit
);
13326 if( inContext
->state
== kDotLocalTestState_GAIMDNSOnly
)
13328 ASPrintF( &subtest
->queryName
, "%s-2.local.", inContext
->labelStr
);
13329 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13331 subtest
->hasMDNSv4
= subtest
->needMDNSv4
= true;
13332 subtest
->hasMDNSv6
= subtest
->needMDNSv6
= true;
13334 subtest
->addrMDNSv4
= htonl( 0x00000201 ); // 0.0.2.1
13335 memcpy( subtest
->addrMDNSv6
, kMDNSReplierBaseAddrV6
, 16 ); // 2001:db8:2::2:1
13336 subtest
->addrMDNSv6
[ 13 ] = 2;
13337 subtest
->addrMDNSv6
[ 15 ] = 1;
13339 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAIMDNSOnly
;
13342 else if( inContext
->state
== kDotLocalTestState_GAIDNSOnly
)
13344 ASPrintF( &subtest
->queryName
, "tag-dns-only.%s.local.", inContext
->labelStr
);
13345 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13347 subtest
->hasDNSv4
= subtest
->needDNSv4
= true;
13348 subtest
->hasDNSv6
= subtest
->needDNSv6
= true;
13350 subtest
->addrDNSv4
= htonl( kDNSServerBaseAddrV4
+ 1 ); // 203.0.113.1
13351 memcpy( subtest
->addrDNSv6
, kDNSServerBaseAddrV6
, 16 ); // 2001:db8:1::1
13352 subtest
->addrDNSv6
[ 15 ] = 1;
13354 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAIDNSOnly
;
13357 else if( inContext
->state
== kDotLocalTestState_GAIBoth
)
13359 ASPrintF( &subtest
->queryName
, "%s.local.", inContext
->labelStr
);
13360 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13362 subtest
->hasDNSv4
= subtest
->needDNSv4
= true;
13363 subtest
->hasDNSv6
= subtest
->needDNSv6
= true;
13364 subtest
->hasMDNSv4
= subtest
->needMDNSv4
= true;
13365 subtest
->hasMDNSv6
= subtest
->needMDNSv6
= true;
13367 subtest
->addrDNSv4
= htonl( kDNSServerBaseAddrV4
+ 1 ); // 203.0.113.1
13368 memcpy( subtest
->addrDNSv6
, kDNSServerBaseAddrV6
, 16 ); // 2001:db8:1::1
13369 subtest
->addrDNSv6
[ 15 ] = 1;
13371 subtest
->addrMDNSv4
= htonl( 0x00000101 ); // 0.0.1.1
13372 memcpy( subtest
->addrMDNSv6
, kMDNSReplierBaseAddrV6
, 16 ); // 2001:db8:2::1:1
13373 subtest
->addrMDNSv6
[ 13 ] = 1;
13374 subtest
->addrMDNSv6
[ 15 ] = 1;
13376 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAIBoth
;
13379 else if( inContext
->state
== kDotLocalTestState_GAINeither
)
13381 ASPrintF( &subtest
->queryName
, "doesnotexit-%s.local.", inContext
->labelStr
);
13382 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13384 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAINeither
;
13387 else if( inContext
->state
== kDotLocalTestState_GAINoSuchRecord
)
13389 ASPrintF( &subtest
->queryName
, "doesnotexit-dns.%s.local.", inContext
->labelStr
);
13390 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13392 subtest
->hasDNSv4
= subtest
->needDNSv4
= true;
13393 subtest
->hasDNSv6
= subtest
->needDNSv6
= true;
13394 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAINoSuchRecord
;
13397 else if( inContext
->state
== kDotLocalTestState_QuerySRV
)
13399 ASPrintF( &subtest
->queryName
, "_http._tcp.srv-%u-%u-%u.%s%s.local.",
13400 kDotLocalTestSRV_Priority
, kDotLocalTestSRV_Weight
, kDotLocalTestSRV_Port
, kDotLocalTestSRV_TargetStr
,
13401 inContext
->labelStr
);
13402 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13404 subtest
->needSRV
= true;
13405 subtest
->testDesc
= kDotLocalTestSubtestDesc_QuerySRV
;
13414 // Start new operation.
13416 flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsReturnIntermediates
;
13417 #if( TARGET_OS_WATCH )
13418 flags
|= kDNSServiceFlagsPathEvaluationDone
;
13421 subtest
->startTime
= NanoTimeGetCurrent();
13422 subtest
->endTime
= kNanoTime_Invalid
;
13424 if( inContext
->state
== kDotLocalTestState_QuerySRV
)
13426 op
= inContext
->connection
;
13427 err
= DNSServiceQueryRecord( &op
, flags
, kDNSServiceInterfaceIndexAny
, subtest
->queryName
,
13428 kDNSServiceType_SRV
, kDNSServiceClass_IN
, _DotLocalTestQueryRecordCallback
, inContext
);
13429 require_noerr( err
, exit
);
13433 op
= inContext
->connection
;
13434 err
= DNSServiceGetAddrInfo( &op
, flags
, kDNSServiceInterfaceIndexAny
,
13435 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, subtest
->queryName
, _DotLocalTestGAICallback
, inContext
);
13436 require_noerr( err
, exit
);
13441 check( !inContext
->timer
);
13442 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestSubtestDurationSecs
),
13443 INT64_C_safe( kDotLocalTestSubtestDurationSecs
) * kNanosecondsPerSecond
/ 10, dispatch_get_main_queue(),
13444 _DotLocalTestTimerHandler
, inContext
, &inContext
->timer
);
13445 require_noerr( err
, exit
);
13446 dispatch_resume( inContext
->timer
);
13448 check( !inContext
->op
);
13449 inContext
->op
= op
;
13452 check( !inContext
->subtest
);
13453 inContext
->subtest
= subtest
;
13457 if( subtest
) _DotLocalSubtestFree( subtest
);
13458 if( op
) DNSServiceRefDeallocate( op
);
13462 //===========================================================================================================================
13463 // _DotLocalTestFinalizeSubtest
13464 //===========================================================================================================================
13466 #define kDotLocalTestReportKey_StartTime CFSTR( "startTime" ) // String.
13467 #define kDotLocalTestReportKey_EndTime CFSTR( "endTime" ) // String.
13468 #define kDotLocalTestReportKey_Success CFSTR( "success" ) // Boolean.
13469 #define kDotLocalTestReportKey_MDNSReplierCmd CFSTR( "replierCmd" ) // String.
13470 #define kDotLocalTestReportKey_DNSServerCmd CFSTR( "serverCmd" ) // String.
13471 #define kDotLocalTestReportKey_GetAddrInfoTests CFSTR( "testsGAI" ) // Array of Dictionaries.
13472 #define kDotLocalTestReportKey_QuerySRVTests CFSTR( "testsQuerySRV" ) // Array of Dictionaries.
13473 #define kDotLocalTestReportKey_Description CFSTR( "description" ) // String.
13474 #define kDotLocalTestReportKey_QueryName CFSTR( "queryName" ) // String.
13475 #define kDotLocalTestReportKey_Error CFSTR( "error" ) // Integer.
13476 #define kDotLocalTestReportKey_Results CFSTR( "results" ) // Dictionary of Arrays.
13477 #define kDotLocalTestReportKey_CorrectResults CFSTR( "correct" ) // Array of Strings
13478 #define kDotLocalTestReportKey_DuplicateResults CFSTR( "duplicates" ) // Array of Strings.
13479 #define kDotLocalTestReportKey_UnexpectedResults CFSTR( "unexpected" ) // Array of Strings.
13480 #define kDotLocalTestReportKey_MissingResults CFSTR( "missing" ) // Array of Strings.
13482 static OSStatus
_DotLocalTestFinalizeSubtest( DotLocalTestContext
*inContext
)
13485 DotLocalSubtest
* subtest
;
13486 CFMutableDictionaryRef reportDict
;
13487 CFMutableDictionaryRef resultsDict
;
13488 CFMutableArrayRef missingResults
, reportArray
;
13489 char startTime
[ 32 ];
13490 char endTime
[ 32 ];
13492 subtest
= inContext
->subtest
;
13493 inContext
->subtest
= NULL
;
13495 subtest
->endTime
= NanoTimeGetCurrent();
13496 _NanoTime64ToTimestamp( subtest
->startTime
, startTime
, sizeof( startTime
) );
13497 _NanoTime64ToTimestamp( subtest
->endTime
, endTime
, sizeof( endTime
) );
13500 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &reportDict
,
13502 "%kO=%s" // startTime
13503 "%kO=%s" // endTime
13504 "%kO=%s" // queryName
13505 "%kO=%s" // description
13506 "%kO={%@}" // results
13508 kDotLocalTestReportKey_StartTime
, startTime
,
13509 kDotLocalTestReportKey_EndTime
, endTime
,
13510 kDotLocalTestReportKey_QueryName
, subtest
->queryName
,
13511 kDotLocalTestReportKey_Description
, subtest
->testDesc
,
13512 kDotLocalTestReportKey_Results
, &resultsDict
);
13513 require_noerr( err
, exit
);
13515 missingResults
= NULL
;
13516 switch( inContext
->state
)
13518 case kDotLocalTestState_GAIMDNSOnly
:
13519 case kDotLocalTestState_GAIDNSOnly
:
13520 case kDotLocalTestState_GAIBoth
:
13521 case kDotLocalTestState_GAINeither
:
13522 if( subtest
->needDNSv4
|| subtest
->needDNSv6
|| subtest
->needMDNSv4
|| subtest
->needMDNSv6
)
13524 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &missingResults
,
13526 "%.4a" // Expected DNS IPv4 address
13527 "%.16a" // Expected DNS IPv6 address
13528 "%.4a" // Expected MDNS IPv4 address
13529 "%.16a" // Expected MDNS IPv6 address
13531 subtest
->needDNSv4
? &subtest
->addrDNSv4
: NULL
,
13532 subtest
->needDNSv6
? subtest
->addrDNSv6
: NULL
,
13533 subtest
->needMDNSv4
? &subtest
->addrMDNSv4
: NULL
,
13534 subtest
->needMDNSv6
? subtest
->addrMDNSv6
: NULL
);
13535 require_noerr( err
, exit
);
13539 case kDotLocalTestState_QuerySRV
:
13540 if( subtest
->needSRV
)
13542 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &missingResults
,
13544 "%s" // Expected SRV record data as a string.
13546 kDotLocalTestSRV_ResultStr
);
13547 require_noerr( err
, exit
);
13551 case kDotLocalTestState_GAINoSuchRecord
:
13552 if( subtest
->needDNSv4
|| subtest
->needDNSv6
)
13554 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &missingResults
,
13556 "%s" // No Such Record (A)
13557 "%s" // No Such Record (AAAA)
13559 subtest
->needDNSv4
? kNoSuchRecordAStr
: NULL
,
13560 subtest
->needDNSv6
? kNoSuchRecordAAAAStr
: NULL
);
13561 require_noerr( err
, exit
);
13570 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_CorrectResults
, subtest
->correctResults
);
13572 if( missingResults
)
13574 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_MissingResults
, missingResults
);
13575 ForgetCF( &missingResults
);
13576 if( !subtest
->error
) subtest
->error
= kNotFoundErr
;
13579 if( CFArrayGetCount( subtest
->unexpectedResults
) > 0 )
13581 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_UnexpectedResults
, subtest
->unexpectedResults
);
13582 if( !subtest
->error
) subtest
->error
= kUnexpectedErr
;
13585 if( CFArrayGetCount( subtest
->duplicateResults
) > 0 )
13587 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_DuplicateResults
, subtest
->duplicateResults
);
13588 if( !subtest
->error
) subtest
->error
= kDuplicateErr
;
13591 if( subtest
->error
) inContext
->testFailed
= true;
13592 err
= CFDictionarySetInt64( reportDict
, kDotLocalTestReportKey_Error
, subtest
->error
);
13593 require_noerr( err
, exit
);
13595 reportArray
= ( inContext
->state
== kDotLocalTestState_QuerySRV
) ? inContext
->reportsQuerySRV
: inContext
->reportsGAI
;
13596 CFArrayAppendValue( reportArray
, reportDict
);
13599 _DotLocalSubtestFree( subtest
);
13600 CFReleaseNullSafe( reportDict
);
13604 //===========================================================================================================================
13605 // _DotLocalTestFinalizeAndExit
13606 //===========================================================================================================================
13608 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext
*inContext
)
13611 CFPropertyListRef plist
;
13612 char timestampStart
[ 32 ];
13613 char timestampEnd
[ 32 ];
13615 check( !inContext
->subtest
);
13616 inContext
->endTime
= NanoTimeGetCurrent();
13618 if( inContext
->replierPID
!= -1 )
13620 kill( inContext
->replierPID
, SIGTERM
);
13621 inContext
->replierPID
= -1;
13623 if( inContext
->serverPID
!= -1 )
13625 kill( inContext
->serverPID
, SIGTERM
);
13626 inContext
->serverPID
= -1;
13628 err
= DNSServiceRemoveRecord( inContext
->connection
, inContext
->localSOARef
, 0 );
13629 require_noerr( err
, exit
);
13631 _NanoTime64ToTimestamp( inContext
->startTime
, timestampStart
, sizeof( timestampStart
) );
13632 _NanoTime64ToTimestamp( inContext
->endTime
, timestampEnd
, sizeof( timestampEnd
) );
13634 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
13636 "%kO=%s" // startTime
13637 "%kO=%s" // endTime
13638 "%kO=%O" // testsGAI
13639 "%kO=%O" // testsQuerySRV
13640 "%kO=%b" // success
13641 "%kO=%s" // replierCmd
13642 "%kO=%s" // serverCmd
13644 kDotLocalTestReportKey_StartTime
, timestampStart
,
13645 kDotLocalTestReportKey_EndTime
, timestampEnd
,
13646 kDotLocalTestReportKey_GetAddrInfoTests
, inContext
->reportsGAI
,
13647 kDotLocalTestReportKey_QuerySRVTests
, inContext
->reportsQuerySRV
,
13648 kDotLocalTestReportKey_Success
, inContext
->testFailed
? false : true,
13649 kDotLocalTestReportKey_MDNSReplierCmd
, inContext
->replierCmd
,
13650 kDotLocalTestReportKey_DNSServerCmd
, inContext
->serverCmd
);
13651 require_noerr( err
, exit
);
13653 ForgetCF( &inContext
->reportsGAI
);
13654 ForgetCF( &inContext
->reportsQuerySRV
);
13656 err
= OutputPropertyList( plist
, inContext
->outputFormat
, inContext
->outputFilePath
);
13657 CFRelease( plist
);
13658 require_noerr( err
, exit
);
13660 exit( inContext
->testFailed
? 2 : 0 );
13663 ErrQuit( 1, "error: %#m\n", err
);
13666 //===========================================================================================================================
13667 // _DotLocalTestProbeQueryRecordCallback
13668 //===========================================================================================================================
13670 static void DNSSD_API
13671 _DotLocalTestProbeQueryRecordCallback(
13672 DNSServiceRef inSDRef
,
13673 DNSServiceFlags inFlags
,
13674 uint32_t inInterfaceIndex
,
13675 DNSServiceErrorType inError
,
13676 const char * inFullName
,
13679 uint16_t inRDataLen
,
13680 const void * inRDataPtr
,
13684 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13686 Unused( inInterfaceIndex
);
13687 Unused( inFullName
);
13690 Unused( inRDataLen
);
13691 Unused( inRDataPtr
);
13694 check( context
->state
== kDotLocalTestState_Preparing
);
13696 require_quiet( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
, exit
);
13698 if( inSDRef
== context
->op
)
13700 DNSServiceForget( &context
->op
);
13701 context
->serverIsReady
= true;
13703 else if( inSDRef
== context
->op2
)
13705 DNSServiceForget( &context
->op2
);
13706 context
->replierIsReady
= true;
13709 if( context
->registeredSOA
&& context
->serverIsReady
&& context
->replierIsReady
)
13711 _DotLocalTestStateMachine( context
);
13718 //===========================================================================================================================
13719 // _DotLocalTestRegisterRecordCallback
13720 //===========================================================================================================================
13722 static void DNSSD_API
13723 _DotLocalTestRegisterRecordCallback(
13724 DNSServiceRef inSDRef
,
13725 DNSRecordRef inRecordRef
,
13726 DNSServiceFlags inFlags
,
13727 DNSServiceErrorType inError
,
13730 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13733 Unused( inRecordRef
);
13736 if( inError
) ErrQuit( 1, "error: local. SOA record registration failed: %#m\n", inError
);
13738 if( !context
->registeredSOA
)
13740 context
->registeredSOA
= true;
13741 if( context
->serverIsReady
&& context
->replierIsReady
) _DotLocalTestStateMachine( context
);
13745 //===========================================================================================================================
13746 // _DotLocalTestTimerHandler
13747 //===========================================================================================================================
13749 static void _DotLocalTestTimerHandler( void *inContext
)
13751 _DotLocalTestStateMachine( (DotLocalTestContext
*) inContext
);
13754 //===========================================================================================================================
13755 // _DotLocalTestGAICallback
13756 //===========================================================================================================================
13758 static void DNSSD_API
13759 _DotLocalTestGAICallback(
13760 DNSServiceRef inSDRef
,
13761 DNSServiceFlags inFlags
,
13762 uint32_t inInterfaceIndex
,
13763 DNSServiceErrorType inError
,
13764 const char * inHostname
,
13765 const struct sockaddr
* inSockAddr
,
13770 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13771 DotLocalSubtest
* const subtest
= context
->subtest
;
13772 const sockaddr_ip
* const sip
= (const sockaddr_ip
*) inSockAddr
;
13775 Unused( inInterfaceIndex
);
13776 Unused( inHostname
);
13779 require_action_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
, err
= kFlagErr
);
13780 require_action_quiet( ( sip
->sa
.sa_family
== AF_INET
) || ( sip
->sa
.sa_family
== AF_INET6
), exit
, err
= kTypeErr
);
13782 if( context
->state
== kDotLocalTestState_GAINoSuchRecord
)
13784 if( inError
== kDNSServiceErr_NoSuchRecord
)
13786 CFMutableArrayRef array
= NULL
;
13787 const char * noSuchRecordStr
;
13789 if( sip
->sa
.sa_family
== AF_INET
)
13791 array
= subtest
->needDNSv4
? subtest
->correctResults
: subtest
->duplicateResults
;
13792 subtest
->needDNSv4
= false;
13794 noSuchRecordStr
= kNoSuchRecordAStr
;
13798 array
= subtest
->needDNSv6
? subtest
->correctResults
: subtest
->duplicateResults
;
13799 subtest
->needDNSv6
= false;
13801 noSuchRecordStr
= kNoSuchRecordAAAAStr
;
13803 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, array
, "%s", noSuchRecordStr
);
13804 require_noerr( err
, fatal
);
13806 else if( !inError
)
13808 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, subtest
->unexpectedResults
, "%##a", sip
);
13809 require_noerr( err
, fatal
);
13821 CFMutableArrayRef array
= NULL
;
13823 if( sip
->sa
.sa_family
== AF_INET
)
13825 const uint32_t addrV4
= sip
->v4
.sin_addr
.s_addr
;
13827 if( subtest
->hasDNSv4
&& ( addrV4
== subtest
->addrDNSv4
) )
13829 array
= subtest
->needDNSv4
? subtest
->correctResults
: subtest
->duplicateResults
;
13830 subtest
->needDNSv4
= false;
13832 else if( subtest
->hasMDNSv4
&& ( addrV4
== subtest
->addrMDNSv4
) )
13834 array
= subtest
->needMDNSv4
? subtest
->correctResults
: subtest
->duplicateResults
;
13835 subtest
->needMDNSv4
= false;
13840 const uint8_t * const addrV6
= sip
->v6
.sin6_addr
.s6_addr
;
13842 if( subtest
->hasDNSv6
&& ( memcmp( addrV6
, subtest
->addrDNSv6
, 16 ) == 0 ) )
13844 array
= subtest
->needDNSv6
? subtest
->correctResults
: subtest
->duplicateResults
;
13845 subtest
->needDNSv6
= false;
13847 else if( subtest
->hasMDNSv6
&& ( memcmp( addrV6
, subtest
->addrMDNSv6
, 16 ) == 0 ) )
13849 array
= subtest
->needMDNSv6
? subtest
->correctResults
: subtest
->duplicateResults
;
13850 subtest
->needMDNSv6
= false;
13853 if( !array
) array
= subtest
->unexpectedResults
;
13854 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, array
, "%##a", sip
);
13855 require_noerr( err
, fatal
);
13857 else if( inError
== kDNSServiceErr_NoSuchRecord
)
13859 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, subtest
->unexpectedResults
, "%s",
13860 ( sip
->sa
.sa_family
== AF_INET
) ? kNoSuchRecordAStr
: kNoSuchRecordAAAAStr
);
13861 require_noerr( err
, fatal
);
13873 subtest
->error
= err
;
13874 _DotLocalTestStateMachine( context
);
13879 ErrQuit( 1, "error: %#m\n", err
);
13882 //===========================================================================================================================
13883 // _DotLocalTestQueryRecordCallback
13884 //===========================================================================================================================
13886 static void DNSSD_API
13887 _DotLocalTestQueryRecordCallback(
13888 DNSServiceRef inSDRef
,
13889 DNSServiceFlags inFlags
,
13890 uint32_t inInterfaceIndex
,
13891 DNSServiceErrorType inError
,
13892 const char * inFullName
,
13895 uint16_t inRDataLen
,
13896 const void * inRDataPtr
,
13901 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13902 DotLocalSubtest
* const subtest
= context
->subtest
;
13903 const dns_fixed_fields_srv
* fields
;
13904 const uint8_t * target
;
13905 const uint8_t * ptr
;
13906 const uint8_t * end
;
13908 unsigned int priority
, weight
, port
;
13909 CFMutableArrayRef array
;
13912 Unused( inInterfaceIndex
);
13913 Unused( inFullName
);
13916 check( context
->state
== kDotLocalTestState_QuerySRV
);
13919 require_noerr_quiet( err
, exit
);
13920 require_action_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
, err
= kFlagErr
);
13921 require_action_quiet( ( inType
== kDNSServiceType_SRV
) && ( inClass
== kDNSServiceClass_IN
), exit
, err
= kTypeErr
);
13922 require_action_quiet( inRDataLen
> sizeof( dns_fixed_fields_srv
), exit
, err
= kSizeErr
);
13924 fields
= (const dns_fixed_fields_srv
*) inRDataPtr
;
13925 priority
= dns_fixed_fields_srv_get_priority( fields
);
13926 weight
= dns_fixed_fields_srv_get_weight( fields
);
13927 port
= dns_fixed_fields_srv_get_port( fields
);
13928 target
= (const uint8_t *) &fields
[ 1 ];
13929 end
= ( (const uint8_t *) inRDataPtr
) + inRDataLen
;
13930 for( ptr
= target
; ( ptr
< end
) && ( *ptr
!= 0 ); ptr
+= ( 1 + *ptr
) ) {}
13932 if( ( priority
== kDotLocalTestSRV_Priority
) &&
13933 ( weight
== kDotLocalTestSRV_Weight
) &&
13934 ( port
== kDotLocalTestSRV_Port
) &&
13935 ( ptr
< end
) && DomainNameEqual( target
, kDotLocalTestSRV_TargetName
) )
13937 array
= subtest
->needSRV
? subtest
->correctResults
: subtest
->duplicateResults
;
13938 subtest
->needSRV
= false;
13942 array
= subtest
->unexpectedResults
;
13946 DNSRecordDataToString( inRDataPtr
, inRDataLen
, kDNSServiceType_SRV
, NULL
, 0, &rdataStr
);
13949 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, inRDataLen
);
13950 require_action( rdataStr
, fatal
, err
= kNoMemoryErr
);
13953 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, array
, "%s", rdataStr
);
13955 require_noerr( err
, fatal
);
13960 subtest
->error
= err
;
13961 _DotLocalTestStateMachine( context
);
13966 ErrQuit( 1, "error: %#m\n", err
);
13969 //===========================================================================================================================
13970 // ProbeConflictTestCmd
13971 //===========================================================================================================================
13973 #define kProbeConflictTestService_DefaultName "pctest-name"
13974 #define kProbeConflictTestService_Port 60000
13976 #define kProbeConflictTestTXTPtr "\x13" "PROBE-CONFLICT-TEST"
13977 #define kProbeConflictTestTXTLen sizeof_string( kProbeConflictTestTXTPtr )
13981 const char * description
;
13982 const char * program
;
13983 Boolean expectsRename
;
13985 } ProbeConflictTestCase
;
13987 #define kPCTProgPreWait "wait 1000;" // Wait 1 second before sending gratuitous response.
13988 #define kPCTProgPostWait "wait 8000;" // Wait 8 seconds after sending gratuitous response.
13989 // This allows ~2.75 seconds for probing and ~5 seconds for a rename.
13991 static const ProbeConflictTestCase kProbeConflictTestCases
[] =
13995 { "No probe conflicts.", kPCTProgPreWait
"probes n-n-n;" "send;" kPCTProgPostWait
, false },
13997 // One multicast probe conflict
13999 { "One multicast probe conflict (1).", kPCTProgPreWait
"probes m;" "send;" kPCTProgPostWait
, false },
14000 { "One multicast probe conflict (2).", kPCTProgPreWait
"probes n-m;" "send;" kPCTProgPostWait
, false },
14001 { "One multicast probe conflict (3).", kPCTProgPreWait
"probes n-n-m;" "send;" kPCTProgPostWait
, false },
14003 // One unicast probe conflict
14005 { "One unicast probe conflict (1).", kPCTProgPreWait
"probes u;" "send;" kPCTProgPostWait
, true },
14006 { "One unicast probe conflict (2).", kPCTProgPreWait
"probes n-u;" "send;" kPCTProgPostWait
, true },
14007 { "One unicast probe conflict (3).", kPCTProgPreWait
"probes n-n-u;" "send;" kPCTProgPostWait
, true },
14009 // One multicast and one unicast probe conflict
14011 { "Multicast and unicast probe conflict (1).", kPCTProgPreWait
"probes m-u;" "send;" kPCTProgPostWait
, true },
14012 { "Multicast and unicast probe conflict (2).", kPCTProgPreWait
"probes m-n-u;" "send;" kPCTProgPostWait
, true },
14013 { "Multicast and unicast probe conflict (3).", kPCTProgPreWait
"probes m-n-n-u;" "send;" kPCTProgPostWait
, true },
14014 { "Multicast and unicast probe conflict (4).", kPCTProgPreWait
"probes n-m-u;" "send;" kPCTProgPostWait
, true },
14015 { "Multicast and unicast probe conflict (5).", kPCTProgPreWait
"probes n-m-n-u;" "send;" kPCTProgPostWait
, true },
14016 { "Multicast and unicast probe conflict (6).", kPCTProgPreWait
"probes n-m-n-n-u;" "send;" kPCTProgPostWait
, true },
14017 { "Multicast and unicast probe conflict (7).", kPCTProgPreWait
"probes n-n-m-u;" "send;" kPCTProgPostWait
, true },
14018 { "Multicast and unicast probe conflict (8).", kPCTProgPreWait
"probes n-n-m-n-u;" "send;" kPCTProgPostWait
, true },
14019 { "Multicast and unicast probe conflict (9).", kPCTProgPreWait
"probes n-n-m-n-n-u;" "send;" kPCTProgPostWait
, true },
14021 // Two multicast probe conflicts
14023 { "Two multicast probe conflicts (1).", kPCTProgPreWait
"probes m-m;" "send;" kPCTProgPostWait
, true },
14024 { "Two multicast probe conflicts (2).", kPCTProgPreWait
"probes m-n-m;" "send;" kPCTProgPostWait
, true },
14025 { "Two multicast probe conflicts (3).", kPCTProgPreWait
"probes m-n-n-m;" "send;" kPCTProgPostWait
, true },
14026 { "Two multicast probe conflicts (4).", kPCTProgPreWait
"probes n-m-m;" "send;" kPCTProgPostWait
, true },
14027 { "Two multicast probe conflicts (5).", kPCTProgPreWait
"probes n-m-n-m-n;" "send;" kPCTProgPostWait
, true },
14028 { "Two multicast probe conflicts (6).", kPCTProgPreWait
"probes n-m-n-n-m;" "send;" kPCTProgPostWait
, true },
14029 { "Two multicast probe conflicts (7).", kPCTProgPreWait
"probes n-n-m-m;" "send;" kPCTProgPostWait
, true },
14030 { "Two multicast probe conflicts (8).", kPCTProgPreWait
"probes n-n-m-n-m;" "send;" kPCTProgPostWait
, true },
14031 { "Two multicast probe conflicts (9).", kPCTProgPreWait
"probes n-n-m-n-n-m;" "send;" kPCTProgPostWait
, true },
14034 #define kProbeConflictTestCaseCount countof( kProbeConflictTestCases )
14038 DNSServiceRef registration
; // Test service registration.
14039 NanoTime64 testStartTime
; // Test's start time.
14040 NanoTime64 startTime
; // Current test case's start time.
14041 MDNSColliderRef collider
; // mDNS collider object.
14042 CFMutableArrayRef results
; // Array of test case results.
14043 char * serviceName
; // Test service's instance name as a string. (malloced)
14044 char * serviceType
; // Test service's service type as a string. (malloced)
14045 uint8_t * recordName
; // FQDN of collider's record (same as test service's SRV+TXT records). (malloced)
14046 unsigned int testCaseIndex
; // Index of the current test case.
14047 uint32_t ifIndex
; // Index of the interface that the collider is to operate on.
14048 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout. (malloced)
14049 OutputFormatType outputFormat
; // Format of test report output.
14050 Boolean registered
; // True if the test service instance is currently registered.
14051 Boolean testFailed
; // True if at least one test case failed.
14053 } ProbeConflictTestContext
;
14055 static void DNSSD_API
14056 _ProbeConflictTestRegisterCallback(
14057 DNSServiceRef inSDRef
,
14058 DNSServiceFlags inFlags
,
14059 DNSServiceErrorType inError
,
14060 const char * inName
,
14061 const char * inType
,
14062 const char * inDomain
,
14063 void * inContext
);
14064 static void _ProbeConflictTestColliderStopHandler( void *inContext
, OSStatus inError
);
14065 static OSStatus
_ProbeConflictTestStartNextTest( ProbeConflictTestContext
*inContext
);
14066 static OSStatus
_ProbeConflictTestStopCurrentTest( ProbeConflictTestContext
*inContext
, Boolean inRenamed
);
14067 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext
*inContext
) ATTRIBUTE_NORETURN
;
14069 static void ProbeConflictTestCmd( void )
14072 ProbeConflictTestContext
* context
;
14073 const char * serviceName
;
14076 context
= (ProbeConflictTestContext
*) calloc( 1, sizeof( *context
) );
14077 require_action( context
, exit
, err
= kNoMemoryErr
);
14079 if( gProbeConflictTest_Interface
)
14081 err
= InterfaceIndexFromArgString( gProbeConflictTest_Interface
, &context
->ifIndex
);
14082 require_noerr_quiet( err
, exit
);
14086 err
= _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All
, NULL
, &context
->ifIndex
);
14087 require_noerr_quiet( err
, exit
);
14090 if( gProbeConflictTest_OutputFilePath
)
14092 context
->outputFilePath
= strdup( gProbeConflictTest_OutputFilePath
);
14093 require_action( context
->outputFilePath
, exit
, err
= kNoMemoryErr
);
14096 err
= OutputFormatFromArgString( gProbeConflictTest_OutputFormat
, &context
->outputFormat
);
14097 require_noerr_quiet( err
, exit
);
14099 context
->results
= CFArrayCreateMutable( NULL
, kProbeConflictTestCaseCount
, &kCFTypeArrayCallBacks
);
14100 require_action( context
->results
, exit
, err
= kNoMemoryErr
);
14102 serviceName
= gProbeConflictTest_UseComputerName
? NULL
: kProbeConflictTestService_DefaultName
;
14104 ASPrintF( &context
->serviceType
, "_pctest-%s._udp",
14105 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
14106 require_action( context
->serviceType
, exit
, err
= kNoMemoryErr
);
14108 context
->testStartTime
= NanoTimeGetCurrent();
14109 err
= DNSServiceRegister( &context
->registration
, 0, context
->ifIndex
, serviceName
, context
->serviceType
, "local.",
14110 NULL
, htons( kProbeConflictTestService_Port
), 0, NULL
, _ProbeConflictTestRegisterCallback
, context
);
14111 require_noerr( err
, exit
);
14113 err
= DNSServiceSetDispatchQueue( context
->registration
, dispatch_get_main_queue() );
14114 require_noerr( err
, exit
);
14122 //===========================================================================================================================
14123 // _ProbeConflictTestRegisterCallback
14124 //===========================================================================================================================
14126 static void DNSSD_API
14127 _ProbeConflictTestRegisterCallback(
14128 DNSServiceRef inSDRef
,
14129 DNSServiceFlags inFlags
,
14130 DNSServiceErrorType inError
,
14131 const char * inName
,
14132 const char * inType
,
14133 const char * inDomain
,
14137 ProbeConflictTestContext
* const context
= (ProbeConflictTestContext
*) inContext
;
14141 Unused( inDomain
);
14144 require_noerr( err
, exit
);
14146 if( !context
->registered
)
14148 if( inFlags
& kDNSServiceFlagsAdd
)
14151 size_t recordNameLen
;
14153 uint8_t name
[ kDomainNameLengthMax
];
14155 context
->registered
= true;
14157 FreeNullSafe( context
->serviceName
);
14158 context
->serviceName
= strdup( inName
);
14159 require_action( context
->serviceName
, exit
, err
= kNoMemoryErr
);
14161 err
= DomainNameFromString( name
, context
->serviceName
, NULL
);
14162 require_noerr( err
, exit
);
14164 err
= DomainNameAppendString( name
, context
->serviceType
, NULL
);
14165 require_noerr( err
, exit
);
14167 err
= DomainNameAppendString( name
, "local", NULL
);
14168 require_noerr( err
, exit
);
14170 ForgetMem( &context
->recordName
);
14171 err
= DomainNameDup( name
, &context
->recordName
, &recordNameLen
);
14172 require_noerr( err
, exit
);
14173 require_fatal( recordNameLen
> 0, "Record name length is zero." ); // Prevents dubious static analyzer warning.
14175 // Make the first label all caps so that it's easier to spot in system logs.
14177 ptr
= context
->recordName
;
14178 for( len
= *ptr
++; len
> 0; --len
, ++ptr
) *ptr
= (uint8_t) toupper_safe( *ptr
);
14180 err
= _ProbeConflictTestStartNextTest( context
);
14181 require_noerr( err
, exit
);
14186 if( !( inFlags
& kDNSServiceFlagsAdd
) )
14188 context
->registered
= false;
14189 err
= _ProbeConflictTestStopCurrentTest( context
, true );
14190 require_noerr( err
, exit
);
14196 if( err
) exit( 1 );
14199 //===========================================================================================================================
14200 // _ProbeConflictTestColliderStopHandler
14201 //===========================================================================================================================
14203 static void _ProbeConflictTestColliderStopHandler( void *inContext
, OSStatus inError
)
14206 ProbeConflictTestContext
* const context
= (ProbeConflictTestContext
*) inContext
;
14209 require_noerr_quiet( err
, exit
);
14211 ForgetCF( &context
->collider
);
14213 err
= _ProbeConflictTestStopCurrentTest( context
, false );
14214 require_noerr( err
, exit
);
14216 err
= _ProbeConflictTestStartNextTest( context
);
14217 require_noerr( err
, exit
);
14220 if( err
) exit( 1 );
14223 //===========================================================================================================================
14224 // _ProbeConflictTestStartNextTest
14225 //===========================================================================================================================
14227 static OSStatus
_ProbeConflictTestStartNextTest( ProbeConflictTestContext
*inContext
)
14230 const ProbeConflictTestCase
* testCase
;
14232 check( !inContext
->collider
);
14234 if( inContext
->testCaseIndex
< kProbeConflictTestCaseCount
)
14236 testCase
= &kProbeConflictTestCases
[ inContext
->testCaseIndex
];
14240 _ProbeConflictTestFinalizeAndExit( inContext
);
14243 err
= MDNSColliderCreate( dispatch_get_main_queue(), &inContext
->collider
);
14244 require_noerr( err
, exit
);
14246 err
= MDNSColliderSetProgram( inContext
->collider
, testCase
->program
);
14247 require_noerr( err
, exit
);
14249 err
= MDNSColliderSetRecord( inContext
->collider
, inContext
->recordName
, kDNSServiceType_TXT
,
14250 kProbeConflictTestTXTPtr
, kProbeConflictTestTXTLen
);
14251 require_noerr( err
, exit
);
14253 MDNSColliderSetProtocols( inContext
->collider
, kMDNSColliderProtocol_IPv4
);
14254 MDNSColliderSetInterfaceIndex( inContext
->collider
, inContext
->ifIndex
);
14255 MDNSColliderSetStopHandler( inContext
->collider
, _ProbeConflictTestColliderStopHandler
, inContext
);
14257 inContext
->startTime
= NanoTimeGetCurrent();
14258 err
= MDNSColliderStart( inContext
->collider
);
14259 require_noerr( err
, exit
);
14265 //===========================================================================================================================
14266 // _ProbeConflictTestStopCurrentTest
14267 //===========================================================================================================================
14269 #define kProbeConflictTestCaseResultKey_Description CFSTR( "description" )
14270 #define kProbeConflictTestCaseResultKey_StartTime CFSTR( "startTime" )
14271 #define kProbeConflictTestCaseResultKey_EndTime CFSTR( "endTime" )
14272 #define kProbeConflictTestCaseResultKey_ExpectedRename CFSTR( "expectedRename" )
14273 #define kProbeConflictTestCaseResultKey_ServiceName CFSTR( "serviceName" )
14274 #define kProbeConflictTestCaseResultKey_Passed CFSTR( "passed" )
14276 static OSStatus
_ProbeConflictTestStopCurrentTest( ProbeConflictTestContext
*inContext
, Boolean inRenamed
)
14279 const ProbeConflictTestCase
* testCase
;
14282 char startTime
[ 32 ];
14283 char endTime
[ 32 ];
14285 now
= NanoTimeGetCurrent();
14287 if( inContext
->collider
)
14289 MDNSColliderSetStopHandler( inContext
->collider
, NULL
, NULL
);
14290 MDNSColliderStop( inContext
->collider
);
14291 CFRelease( inContext
->collider
);
14292 inContext
->collider
= NULL
;
14295 testCase
= &kProbeConflictTestCases
[ inContext
->testCaseIndex
];
14296 passed
= ( ( testCase
->expectsRename
&& inRenamed
) || ( !testCase
->expectsRename
&& !inRenamed
) ) ? true : false;
14297 if( !passed
) inContext
->testFailed
= true;
14299 _NanoTime64ToTimestamp( inContext
->startTime
, startTime
, sizeof( startTime
) );
14300 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
14302 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, inContext
->results
,
14304 "%kO=%s" // description
14305 "%kO=%b" // expectedRename
14306 "%kO=%s" // startTime
14307 "%kO=%s" // endTime
14308 "%kO=%s" // serviceName
14311 kProbeConflictTestCaseResultKey_Description
, testCase
->description
,
14312 kProbeConflictTestCaseResultKey_ExpectedRename
, testCase
->expectsRename
,
14313 kProbeConflictTestCaseResultKey_StartTime
, startTime
,
14314 kProbeConflictTestCaseResultKey_EndTime
, endTime
,
14315 kProbeConflictTestCaseResultKey_ServiceName
, inContext
->serviceName
,
14316 kProbeConflictTestCaseResultKey_Passed
, passed
);
14317 require_noerr( err
, exit
);
14319 ++inContext
->testCaseIndex
;
14325 //===========================================================================================================================
14326 // _ProbeConflictTestFinalizeAndExit
14327 //===========================================================================================================================
14329 #define kProbeConflictTestReportKey_StartTime CFSTR( "startTime" )
14330 #define kProbeConflictTestReportKey_EndTime CFSTR( "endTime" )
14331 #define kProbeConflictTestReportKey_ServiceType CFSTR( "serviceType" )
14332 #define kProbeConflictTestReportKey_Results CFSTR( "results" )
14333 #define kProbeConflictTestReportKey_Passed CFSTR( "passed" )
14335 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext
*inContext
)
14338 CFPropertyListRef plist
;
14340 char startTime
[ 32 ];
14341 char endTime
[ 32 ];
14343 now
= NanoTimeGetCurrent();
14345 check( !inContext
->collider
);
14347 _NanoTime64ToTimestamp( inContext
->testStartTime
, startTime
, sizeof( startTime
) );
14348 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
14350 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
14352 "%kO=%s" // startTime
14353 "%kO=%s" // endTime
14354 "%kO=%s" // serviceType
14355 "%kO=%O" // results
14358 kProbeConflictTestReportKey_StartTime
, startTime
,
14359 kProbeConflictTestReportKey_EndTime
, endTime
,
14360 kProbeConflictTestReportKey_ServiceType
, inContext
->serviceType
,
14361 kProbeConflictTestReportKey_Results
, inContext
->results
,
14362 kProbeConflictTestReportKey_Passed
, inContext
->testFailed
? false : true );
14363 require_noerr( err
, exit
);
14364 ForgetCF( &inContext
->results
);
14366 err
= OutputPropertyList( plist
, inContext
->outputFormat
, inContext
->outputFilePath
);
14367 CFRelease( plist
);
14368 require_noerr( err
, exit
);
14370 exit( inContext
->testFailed
? 2 : 0 );
14373 ErrQuit( 1, "error: %#m\n", err
);
14376 //===========================================================================================================================
14377 // ExpensiveConstrainedsTestCmd
14378 //===========================================================================================================================
14380 #define NOTIFICATION_TIME_THRESHOLD 1500 // The maximum wating time allowed before notification happens
14381 #define TEST_REPETITION 2 // the number of repetition that one test has to passed
14382 #define LOOPBACK_INTERFACE_NAME "lo0"
14383 #define WIFI_TEST_QUESTION_NAME "www.example.com"
14384 #define EXPENSIVE_CONSTRAINED_MAX_RETRIES 1
14385 #define EXPENSIVE_CONSTRAINED_TEST_INTERVAL 5
14386 // Use "-n tag-expensive-test.ttl-86400.d.test." to run the test locally
14387 // #define LOOPBACK_TEST_QUESTION_NAME "tag-expensive-test.ttl-86400.d.test."
14389 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
14390 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
14391 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED CFSTR( "All Tests Passed" )
14392 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT CFSTR( "Subtest Results" )
14394 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME CFSTR( "Start Time" )
14395 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME CFSTR( "End Time" )
14396 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME CFSTR( "Question Name" )
14397 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS CFSTR( "DNS Service Flags" )
14398 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS CFSTR( "Protocols" )
14399 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX CFSTR( "Interface Index" )
14400 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME CFSTR( "Interface Name" )
14401 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT CFSTR( "Result" )
14402 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR CFSTR( "Error Description" )
14403 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS CFSTR( "Test Progress" )
14405 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME CFSTR( "Start Time" )
14406 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME CFSTR( "End Time" )
14407 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE CFSTR( "State" )
14408 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT CFSTR( "Expected Result" )
14409 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT CFSTR( "Actual Result" )
14410 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW CFSTR( "Expensive Prev->Now" )
14411 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW CFSTR( "Constrained Prev->Now" )
14412 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK CFSTR( "Call Back" )
14414 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP CFSTR( "Timestamp" )
14415 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME CFSTR( "Answer Name" )
14416 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS CFSTR( "Add or Remove" )
14417 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE CFSTR( "Interface Index" )
14418 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS CFSTR( "Address" )
14420 // All the states that ends with _PREPARE represents the state where the test state is reset and initialized.
14421 enum ExpensiveConstrainedTestState
14424 TEST_EXPENSIVE_PREPARE
,
14425 TEST_EXPENSIVE
, // Test if mDNSResponder can handle "expensive" status change of the corresponding interface
14426 TEST_CONSTRAINED_PREPARE
,
14427 TEST_CONSTRAINED
, // Test if mDNSResponder can handle "constrained" status change of the corresponding interface
14428 TEST_EXPENSIVE_CONSTRAINED_PREPARE
,
14429 TEST_EXPENSIVE_CONSTRAINED
, // Test if mDNSResponder can handle "expensive" and "constrained" status change of the corresponding interface at the same time
14433 enum ExpensiveConstrainedTestOperation
14435 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.
14436 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.
14437 NO_UPDATE
// no status update notification
14442 uint32_t subtestIndex
; // The index of parameter for the subtest
14443 DNSServiceRef opRef
; // sdRef for the DNSServiceGetAddrInfo operation.
14444 const char * name
; // Hostname to resolve.
14445 DNSServiceFlags flags
; // Flags argument for DNSServiceGetAddrInfo().
14446 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceGetAddrInfo().
14447 uint32_t ifIndex
; // Interface index argument for DNSServiceGetAddrInfo().
14448 char ifName
[IFNAMSIZ
]; // Interface name for the given interface index.
14449 dispatch_source_t timer
; // The test will check if the current behavior is valid, which is called by
14450 // the timer per 2s.
14452 Boolean isExpensivePrev
; // If the interface is expensive in the previous test step.
14453 Boolean isExpensiveNow
; // If the interface is expensive now.
14454 Boolean isConstrainedPrev
; // If the interface is constrained in the previous test step.
14455 Boolean isConstrainedNow
; // If the interface is constrained now.
14456 Boolean startFromExpensive
; // All the test will start from expensive/constrained interface, so there won's be an answer until the interface is changed.
14457 uint8_t numOfRetries
; // the number of retries we can have if the test fail
14458 struct timeval updateTime
; // The time when interface status(expensive or constrained) is changed.
14459 struct timeval notificationTime
; // The time when callback function, which is passed to DNSServiceGetAddrInfo, gets called.
14460 uint32_t counter
; // To record how many times the test has repeated.
14461 enum ExpensiveConstrainedTestState state
; // The current test state.
14462 enum ExpensiveConstrainedTestOperation expectedOperation
; // the test expects this kind of notification
14463 enum ExpensiveConstrainedTestOperation operation
; // represents what notification the callback function gets.
14465 NanoTime64 testReport_startTime
; // when the entire test starts
14466 CFMutableArrayRef subtestReport
; // stores the log message for every subtest
14467 NanoTime64 subtestReport_startTime
; // when the subtest starts
14468 CFMutableArrayRef subtestProgress
; // one test iteration
14469 NanoTime64 subtestProgress_startTime
; // when the test iteration starts
14470 CFMutableArrayRef subtestProgress_callBack
; // array of ADD/REMOVE events
14471 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout. (malloced)
14472 OutputFormatType outputFormat
; // Format of test report output.
14473 } ExpensiveConstrainedContext
;
14475 // structure that controls how the subtest is run
14478 const char *qname
; // the name of the query, when the ends with ".d.test.", test will send query to local DNS server
14479 Boolean deny_expensive
; // if the query should avoid using expensive interface
14480 Boolean deny_constrained
; // if the query should avoid using constrained interface
14481 Boolean start_from_expensive
; // if the query should starts from using an expensive interface
14482 Boolean ipv4_query
; // only allow IPv4 query
14483 Boolean ipv6_query
; // only allow IPv6 query
14484 int8_t test_passed
; // if the subtest passes
14485 } ExpensiveConstrainedTestParams
;
14487 static ExpensiveConstrainedTestParams ExpensiveConstrainedSubtestParams
[] =
14489 // qname deny_expensive deny_constrained start_from_expensive ipv4_query ipv6_query
14490 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, true, -1},
14491 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, true, -1},
14492 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, true, -1},
14493 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, true, -1},
14494 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, true, -1},
14495 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, true, -1},
14497 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, true, false, -1},
14498 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, true, false, -1},
14499 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, true, false, -1},
14500 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, true, false, -1},
14501 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, true, false, -1},
14502 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, true, false, -1},
14504 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, false, false, true, -1},
14505 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, false, true, false, true, -1},
14506 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, false, false, true, -1},
14507 {"tag-expensive_constrained-test.ttl-86400.d.test.", false, true, true, false, true, -1},
14508 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, false, false, true, -1},
14509 {"tag-expensive_constrained-test.ttl-86400.d.test.", true, true, true, false, true, -1}
14512 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext
*context
);
14513 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext
*context
);
14514 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext
*context
);
14515 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext
*context
, uint32_t second
);
14516 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext
*context
);
14517 static void DNSSD_API
14518 ExpensiveConstrainedCallback(
14519 DNSServiceRef inSDRef
,
14520 DNSServiceFlags inFlags
,
14521 uint32_t inInterfaceIndex
,
14522 DNSServiceErrorType inError
,
14523 const char * inHostname
,
14524 const struct sockaddr
* inSockAddr
,
14526 void * inContext
);
14527 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext
*context
);
14528 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext
*context
);
14529 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext
*context
);
14530 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext
*context
, const char *error_description
);
14531 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext
*context
, Boolean allPassed
);
14532 static const char *ExpensiveConstrainedProtocolString(DNSServiceProtocol protocol
);
14533 static const char *ExpensiveConstrainedStateString(enum ExpensiveConstrainedTestState state
);
14534 static const char *ExpensiveConstrainedOperationString(enum ExpensiveConstrainedTestOperation operation
);
14535 static Boolean
expensiveConstrainedEndsWith( const char *str
, const char *suffix
);
14537 //===========================================================================================================================
14538 // ExpensiveConstrainedTestCmd
14539 //===========================================================================================================================
14541 static void ExpensiveConstrainedTestCmd( void )
14544 dispatch_source_t signalSource
= NULL
;
14545 ExpensiveConstrainedContext
* context
= NULL
;
14547 // Set up SIGINT handler.
14548 signal( SIGINT
, SIG_IGN
);
14549 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
14550 require_noerr( err
, exit
);
14551 dispatch_resume( signalSource
);
14553 // create the test context
14554 context
= (ExpensiveConstrainedContext
*) calloc( 1, sizeof(*context
) );
14555 require_action( context
, exit
, err
= kNoMemoryErr
);
14557 // get the command line option
14558 err
= OutputFormatFromArgString( gExpensiveConstrainedTest_OutputFormat
, &context
->outputFormat
);
14559 require_noerr_quiet( err
, exit
);
14560 if ( gExpensiveConstrainedTest_OutputFilePath
)
14562 context
->outputFilePath
= strdup( gExpensiveConstrainedTest_OutputFilePath
);
14563 require_noerr_quiet( context
->outputFilePath
, exit
);
14566 // initialize context
14567 context
->subtestIndex
= 0;
14568 context
->numOfRetries
= EXPENSIVE_CONSTRAINED_MAX_RETRIES
;
14570 // initialize the CFArray used to store the log
14571 context
->subtestReport
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14572 context
->testReport_startTime
= NanoTimeGetCurrent();
14574 // setup local DNS server
14575 ExpensiveConstrainedSetupLocalDNSServer( context
);
14577 ExpensiveConstrainedStartTestHandler( context
);
14585 //===========================================================================================================================
14586 // ExpensiveConstrainedSetupLocalDNSServer
14587 //===========================================================================================================================
14589 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext
*context
)
14591 pid_t current_pid
= getpid();
14592 OSStatus err
= SpawnCommand( &context
->serverPID
, "dnssdutil server -l --follow %d", current_pid
);
14595 FPrintF( stdout
, "dnssdutil server -l --follow <PID> failed, error: %d\n", err
);
14601 //===========================================================================================================================
14602 // ExpensiveConstrainedStartTestHandler
14603 //===========================================================================================================================
14605 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext
*context
)
14608 ExpensiveConstrainedSetupTimer( context
, EXPENSIVE_CONSTRAINED_TEST_INTERVAL
);
14610 // set the event handler for the 3s timer
14611 dispatch_source_set_event_handler( context
->timer
, ^{
14612 ExpensiveConstrainedTestTimerEventHandler( context
);
14615 dispatch_resume( context
->timer
);
14618 //===========================================================================================================================
14619 // ExpensiveConstrainedStartTestHandler
14620 //===========================================================================================================================
14622 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext
*context
)
14624 dispatch_cancel( context
->timer
);
14625 dispatch_release( context
->timer
);
14626 context
->timer
= NULL
;
14629 //===========================================================================================================================
14630 // ExpensiveConstrainedSetupTimer
14631 //===========================================================================================================================
14633 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext
*context
, uint32_t second
)
14635 // set the timer source, the event handler will be called for every "second" seconds
14636 context
->timer
= dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, dispatch_get_main_queue() );
14637 if ( context
->timer
== NULL
)
14639 FPrintF( stdout
, "dispatch_source_create:DISPATCH_SOURCE_TYPE_TIMER failed\n" );
14642 // the first block will be put into the queue "second"s after calling dispatch_resume
14643 dispatch_source_set_timer( context
->timer
, dispatch_time( DISPATCH_TIME_NOW
, second
* NSEC_PER_SEC
),
14644 (unsigned long long)(second
) * NSEC_PER_SEC
, 100ull * NSEC_PER_MSEC
);
14647 //===========================================================================================================================
14648 // ExpensiveConstrainedTestTimerEventHandler
14649 //===========================================================================================================================
14651 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext
*context
)
14654 char buffer
[ 1024 ];
14655 const char *errorDescription
= NULL
;
14657 // do not log the state if we are in transition state
14658 if (context
->state
!= TEST_BEGIN
14659 && context
->state
!= TEST_SUCCEEDED
14660 && context
->state
!= TEST_CONSTRAINED_PREPARE
14661 && context
->state
!= TEST_EXPENSIVE_CONSTRAINED_PREPARE
)
14662 ExpensiveConstrainedSubtestProgressReport( context
);
14664 switch ( context
->state
) {
14667 ExpensiveConstrainedStopTestHandler( context
);
14669 // clear mDNSResponder cache
14670 err
= systemf( NULL
, "killall -HUP mDNSResponder" );
14671 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed");
14673 // initialize the global parameters
14674 ExpensiveConstrainedInitializeContext( context
);
14676 // The local DNS server is set up on the local only interface.
14677 gExpensiveConstrainedTest_Interface
= LOOPBACK_INTERFACE_NAME
;
14678 strncpy( context
->ifName
, gExpensiveConstrainedTest_Interface
, sizeof( context
->ifName
) );
14680 // The local DNS server is unscoped, so we must set our question to unscoped.
14681 context
->ifIndex
= kDNSServiceInterfaceIndexAny
;
14683 // The question name must end with "d.test.", "tag-expensive-test.ttl-86400.d.test." for example, then the test will
14684 // use the local dns server set up previously to run the test locally.
14685 require_action( gExpensiveConstrainedTest_Name
!= NULL
&& expensiveConstrainedEndsWith( gExpensiveConstrainedTest_Name
, "d.test." ), test_failed
,
14686 SNPrintF( buffer
, sizeof( buffer
), "The question name (%s) must end with \"d.test.\".\n", gExpensiveConstrainedTest_Name
);
14687 errorDescription
= buffer
);
14689 // get the quesion name
14690 context
->name
= gExpensiveConstrainedTest_Name
;
14692 // set the initial state for the interface
14693 context
->startFromExpensive
= gExpensiveConstrainedTest_StartFromExpensive
;
14694 err
= systemf( NULL
, "ifconfig %s %sexpensive && ifconfig %s -constrained", context
->ifName
, context
->startFromExpensive
? "" : "-", context
->ifName
);
14695 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed");
14696 sleep( 5 ); // wait for 5s to allow the interface change event de delivered to others
14698 // get question flag
14699 if ( gExpensiveConstrainedTest_DenyExpensive
) context
->flags
|= kDNSServiceFlagsDenyExpensive
;
14700 if ( gExpensiveConstrainedTest_DenyConstrained
) context
->flags
|= kDNSServiceFlagsDenyConstrained
;
14701 if ( gExpensiveConstrainedTest_ProtocolIPv4
) context
->protocols
|= kDNSServiceProtocol_IPv4
;
14702 if ( gExpensiveConstrainedTest_ProtocolIPv6
) context
->protocols
|= kDNSServiceProtocol_IPv6
;
14704 // prevent mDNSResponder from doing extra path evaluation and changing the interface to others(such as Bluetooth)
14705 #if( TARGET_OS_WATCH )
14706 context
->flags
|= kDNSServiceFlagsPathEvaluationDone
;
14710 DNSServiceGetAddrInfo( &context
->opRef
, context
->flags
, context
->ifIndex
, context
->protocols
, context
->name
, ExpensiveConstrainedCallback
, context
);
14712 // set the initial test status
14713 context
->subtestReport_startTime
= NanoTimeGetCurrent();
14714 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14715 context
->state
= TEST_EXPENSIVE_PREPARE
; // start from expensive test
14716 context
->isExpensiveNow
= context
->startFromExpensive
? true : false;
14717 context
->isConstrainedNow
= false;
14718 context
->expectedOperation
= context
->isExpensiveNow
&& ( context
->flags
& kDNSServiceFlagsDenyExpensive
) ? NO_UPDATE
: RESULT_ADD
;
14719 context
->operation
= NO_UPDATE
;
14720 context
->subtestProgress
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14721 require_action( context
->subtestProgress
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14722 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14723 require_action( context
->subtestProgress
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14725 // set the queue where the callback will be called when there is an answer for the query
14726 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
14727 require_noerr( err
, test_failed
);
14729 ExpensiveConstrainedStartTestHandler( context
);
14732 case TEST_EXPENSIVE_PREPARE
:
14733 require_action( context
->isConstrainedNow
== false, test_failed
,
14734 SNPrintF( buffer
, sizeof( buffer
), "Interface %s should be unconstrained.\n", context
->ifName
);
14735 errorDescription
= buffer
);
14736 require_action( context
->expectedOperation
== context
->operation
, test_failed
,
14737 errorDescription
= "Operation is not expected" );
14739 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14740 context
->state
= TEST_EXPENSIVE
; // begin to test expensive flag
14741 context
->counter
= 0; // the number of test repetition that has passed
14742 context
->isExpensivePrev
= context
->isExpensiveNow
;
14743 context
->isExpensiveNow
= !context
->isExpensiveNow
; // flip the expensive status
14744 context
->isConstrainedPrev
= false; // the interface is currently unconstrained
14745 context
->isConstrainedNow
= false; // the interface will be unconstrained in the current test
14746 if ( gExpensiveConstrainedTest_DenyExpensive
)
14747 context
->expectedOperation
= context
->isExpensiveNow
? RESULT_RMV
: RESULT_ADD
;
14749 context
->expectedOperation
= NO_UPDATE
;
14750 context
->operation
= NO_UPDATE
; // NO_UPDATE means the call back function has not been called
14751 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14752 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14754 err
= systemf( NULL
, "ifconfig %s %sexpensive", context
->ifName
, context
->isExpensiveNow
? "" : "-" );
14755 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14757 // record the starting timestamp
14758 gettimeofday( &context
->updateTime
, NULL
);
14761 case TEST_EXPENSIVE
:
14762 // Since we are testing expensive flag, we should always turn the expensive flag on and off.
14763 require_action( context
->isExpensivePrev
^ context
->isExpensiveNow
, test_failed
,
14764 SNPrintF( buffer
, sizeof( buffer
), "The current expensive status should be different with the previous one: %d -> %d\n", context
->isExpensivePrev
, context
->isExpensiveNow
);
14765 errorDescription
= buffer
);
14766 // constrained flag is always turned off when testing expensive
14767 require_action( context
->isConstrainedNow
== false, test_failed
,
14768 SNPrintF( buffer
, sizeof( buffer
), "The interface %s should be unconstrained when testing \"expensive\"\n", context
->ifName
);
14769 errorDescription
= buffer
);
14770 require_action( context
->expectedOperation
== context
->operation
, test_failed
, errorDescription
= "Operation is not expected" );
14772 context
->counter
++; // one test repetition has passed
14773 if ( context
->counter
== TEST_REPETITION
) // expensive test finished
14775 // prepare to test constrained flag
14776 context
->state
= TEST_CONSTRAINED_PREPARE
;
14778 // reset the interface
14779 err
= systemf( NULL
, "ifconfig %s -expensive && ifconfig %s -constrained", context
->ifName
, context
->ifName
);
14780 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14782 context
->isExpensiveNow
= false;
14783 context
->isConstrainedNow
= false;
14784 gettimeofday( &context
->updateTime
, NULL
);
14788 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14789 context
->isExpensivePrev
= context
->isExpensiveNow
;
14790 context
->isExpensiveNow
= !context
->isExpensiveNow
; // flip the expensive status
14791 if ( gExpensiveConstrainedTest_DenyExpensive
)
14792 context
->expectedOperation
= context
->isExpensiveNow
? RESULT_RMV
: RESULT_ADD
;
14794 context
->expectedOperation
= NO_UPDATE
;
14795 context
->operation
= NO_UPDATE
;
14796 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14797 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14799 err
= systemf( NULL
, "ifconfig %s %sexpensive", context
->ifName
, context
->isExpensiveNow
? "" : "-" );
14800 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14802 gettimeofday( &context
->updateTime
, NULL
);
14805 case TEST_CONSTRAINED_PREPARE
:
14806 // The interface should be inexpensive and unconstrained when the constrained test starts
14807 require_action( context
->isExpensiveNow
== false, test_failed
, SNPrintF( buffer
, sizeof( buffer
), "Interface %s should be inexpensive.", context
->ifName
);
14808 errorDescription
= buffer
);
14809 require_action( context
->isConstrainedNow
== false, test_failed
, SNPrintF( buffer
, sizeof( buffer
), "Interface %s should be unconstrained.\n", context
->ifName
);
14810 errorDescription
= buffer
);
14812 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14813 context
->state
= TEST_CONSTRAINED
; // constrained interface is now under testing
14814 context
->counter
= 0;
14815 context
->isExpensivePrev
= false;
14816 context
->isExpensiveNow
= false;
14817 context
->isConstrainedPrev
= false;
14818 context
->isConstrainedNow
= true; // will set constrained flag on the interface
14819 if ( gExpensiveConstrainedTest_DenyConstrained
)
14820 context
->expectedOperation
= RESULT_RMV
;
14822 context
->expectedOperation
= NO_UPDATE
;
14823 context
->operation
= NO_UPDATE
;
14824 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14825 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14827 // change interface to the constrained one
14828 err
= systemf( NULL
, "ifconfig %s -expensive && ifconfig %s constrained", context
->ifName
, context
->ifName
);
14829 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14831 gettimeofday( &context
->updateTime
, NULL
);
14833 case TEST_CONSTRAINED
:
14834 // Since we are testing constrained flag, we should always turn the constrained flag on and off.
14835 require_action( context
->isConstrainedPrev
^ context
->isConstrainedNow
, test_failed
,
14836 SNPrintF( buffer
, sizeof( buffer
), "The current constrained status should be different with the previous one: %d -> %d\n", context
->isConstrainedPrev
, context
->isConstrainedNow
);
14837 errorDescription
= buffer
);
14838 require_action( context
->isExpensiveNow
== false, test_failed
,
14839 SNPrintF( buffer
, sizeof( buffer
), "The interface %s should be inexpensive when testing \"constrained\"\n", context
->ifName
);
14840 errorDescription
= buffer
);
14841 require_action( context
->expectedOperation
== context
->operation
, test_failed
, errorDescription
= "Operation is not expected");
14843 context
->counter
++;
14844 if (context
->counter
== TEST_REPETITION
)
14846 // test changing expensive and constrained flags at the same time
14847 context
->state
= TEST_EXPENSIVE_CONSTRAINED_PREPARE
;
14850 err
= systemf( NULL
, "ifconfig %s -expensive && ifconfig %s -constrained", context
->ifName
, context
->ifName
);
14851 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14853 context
->isExpensiveNow
= false;
14854 context
->isConstrainedNow
= false;
14855 gettimeofday( &context
->updateTime
, NULL
);
14859 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14860 context
->isConstrainedPrev
= context
->isConstrainedNow
;
14861 context
->isConstrainedNow
= !context
->isConstrainedNow
; // flip constrained flag
14862 if ( gExpensiveConstrainedTest_DenyConstrained
)
14863 context
->expectedOperation
= context
->isConstrainedNow
? RESULT_RMV
: RESULT_ADD
;
14865 context
->expectedOperation
= NO_UPDATE
;
14866 context
->operation
= NO_UPDATE
;
14867 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14868 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14870 err
= systemf( NULL
, "ifconfig %s %sconstrained", context
->ifName
, context
->isConstrainedNow
? "" : "-" );
14871 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14873 gettimeofday(&context
->updateTime
, NULL
);
14876 case TEST_EXPENSIVE_CONSTRAINED_PREPARE
:
14877 // The interface should be inexpensive and unconstrained when the constrained test starts
14878 require_action( context
->isExpensiveNow
== false, test_failed
,
14879 SNPrintF( buffer
, sizeof( buffer
), "Interface %s should be inexpensive.\n", context
->ifName
);
14880 errorDescription
= buffer
);
14881 require_action( context
->isConstrainedNow
== false, test_failed
,
14882 SNPrintF(buffer
, sizeof( buffer
), "Interface %s should be unconstrained.\n", context
->ifName
);
14883 errorDescription
= buffer
);
14885 // now flip expensive and constrained at the same time
14886 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14887 context
->state
= TEST_EXPENSIVE_CONSTRAINED
;
14888 context
->counter
= 0;
14889 context
->isExpensivePrev
= false;
14890 context
->isExpensiveNow
= true;
14891 context
->isConstrainedPrev
= false;
14892 context
->isConstrainedNow
= true;
14893 if (gExpensiveConstrainedTest_DenyConstrained
|| gExpensiveConstrainedTest_DenyExpensive
)
14894 context
->expectedOperation
= RESULT_RMV
;
14896 context
->expectedOperation
= NO_UPDATE
;
14897 context
->operation
= NO_UPDATE
;
14898 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14899 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14901 err
= systemf(NULL
, "ifconfig %s expensive && ifconfig %s constrained", context
->ifName
, context
->ifName
);
14902 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14904 gettimeofday( &context
->updateTime
, NULL
);
14906 case TEST_EXPENSIVE_CONSTRAINED
:
14907 // expensive and constrained flag should always be changed
14908 require_action( ( context
->isExpensivePrev
^ context
->isExpensiveNow
) && ( context
->isConstrainedPrev
^ context
->isConstrainedNow
), test_failed
,
14909 SNPrintF( buffer
, sizeof( buffer
), "Both expensive and constrained status need to be changed" );
14910 errorDescription
= buffer
);
14911 require_action( context
->isExpensiveNow
== context
->isConstrainedNow
, test_failed
, errorDescription
= "context->isExpensiveNow != context->isConstrainedNow" );
14912 require_action( context
->expectedOperation
== context
->operation
, test_failed
, errorDescription
= "Operation is not expected" );
14914 context
->counter
++;
14915 if ( context
->counter
== TEST_REPETITION
)
14917 context
->state
= TEST_SUCCEEDED
;
14921 context
->subtestProgress_startTime
= NanoTimeGetCurrent();
14922 context
->isExpensivePrev
= context
->isExpensiveNow
;
14923 context
->isExpensiveNow
= !context
->isExpensiveNow
;
14924 context
->isConstrainedPrev
= context
->isConstrainedNow
;
14925 context
->isConstrainedNow
= !context
->isConstrainedNow
;
14926 if (gExpensiveConstrainedTest_DenyConstrained
|| gExpensiveConstrainedTest_DenyExpensive
)
14927 context
->expectedOperation
= context
->isExpensiveNow
? RESULT_RMV
: RESULT_ADD
;
14929 context
->expectedOperation
= NO_UPDATE
;
14930 context
->operation
= NO_UPDATE
;
14931 context
->subtestProgress_callBack
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
14932 require_action( context
->subtestProgress_callBack
!= NULL
, test_failed
, errorDescription
= "CFArrayCreateMutable failed" );
14934 err
= systemf( NULL
, "ifconfig %s %sexpensive && ifconfig %s %sconstrained", context
->ifName
, context
->isExpensiveNow
? "" : "-", context
->ifName
, context
->isConstrainedNow
? "" : "-" );
14935 require_noerr_action( err
, test_failed
, errorDescription
= "systemf failed" );
14937 gettimeofday( &context
->updateTime
, NULL
);
14942 ExpensiveConstrainedSubtestReport( context
, errorDescription
);
14943 ExpensiveConstrainedStopAndCleanTheTest( context
);
14944 if ( context
->numOfRetries
> 0 )
14946 context
->state
= TEST_BEGIN
;
14947 context
->numOfRetries
--;
14950 ExpensiveConstrainedSubtestParams
[context
->subtestIndex
++].test_passed
= 0;
14951 if (context
->subtestIndex
== (int) countof( ExpensiveConstrainedSubtestParams
))
14953 ExpensiveConstrainedFinalResultReport( context
, false );
14956 if (context
->timer
== NULL
)
14958 // If timer is NULL, it means that we encounter error before we set up the test handler, which is unrecoverable.
14959 ExpensiveConstrainedFinalResultReport( context
, false );
14962 context
->state
= TEST_BEGIN
;
14964 case TEST_SUCCEEDED
:
14965 ExpensiveConstrainedSubtestReport( context
, NULL
);
14966 ExpensiveConstrainedStopAndCleanTheTest( context
);
14967 ExpensiveConstrainedSubtestParams
[context
->subtestIndex
++].test_passed
= 1;
14968 if (context
->subtestIndex
== (int) countof( ExpensiveConstrainedSubtestParams
))
14970 // all the subtests have been run
14971 Boolean hasFailed
= false;
14972 for ( int i
= 0; i
< (int) countof( ExpensiveConstrainedSubtestParams
) && !hasFailed
; i
++ )
14973 hasFailed
= ( ExpensiveConstrainedSubtestParams
[i
].test_passed
!= 1 );
14975 ExpensiveConstrainedFinalResultReport( context
, !hasFailed
);
14976 exit( hasFailed
? 2 : 0 );
14978 context
->state
= TEST_BEGIN
;
14981 FPrintF( stdout
, "unknown error\n" );
14986 //===========================================================================================================================
14987 // ExpensiveConstrainedCallback
14988 //===========================================================================================================================
14990 static void DNSSD_API
14991 ExpensiveConstrainedCallback(
14992 __unused DNSServiceRef inSDRef
,
14993 DNSServiceFlags inFlags
,
14994 uint32_t inInterfaceIndex
,
14995 DNSServiceErrorType inError
,
14996 const char * inHostname
,
14997 const struct sockaddr
* inSockAddr
,
14998 __unused
uint32_t inTTL
,
15001 ExpensiveConstrainedContext
* const context
= (ExpensiveConstrainedContext
*)inContext
;
15003 const char * addrStr
;
15004 char addrStrBuf
[ kSockAddrStringMaxSize
];
15005 char inFlagsDescription
[ 128 ];
15007 char nowTimestamp
[ 32 ];
15009 switch ( inError
) {
15010 case kDNSServiceErr_NoError
:
15011 case kDNSServiceErr_NoSuchRecord
:
15014 case kDNSServiceErr_Timeout
:
15015 Exit( kExitReason_Timeout
);
15022 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
15024 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
15031 err
= SockAddrToString( inSockAddr
, kSockAddrStringFlagsNone
, addrStrBuf
);
15032 require_noerr( err
, exit
);
15033 addrStr
= addrStrBuf
;
15037 addrStr
= ( inSockAddr
->sa_family
== AF_INET
) ? kNoSuchRecordAStr
: kNoSuchRecordAAAAStr
;
15040 now
= NanoTimeGetCurrent();
15041 _NanoTime64ToTimestamp( now
, nowTimestamp
, sizeof( nowTimestamp
) );
15042 SNPrintF( inFlagsDescription
, sizeof( inFlagsDescription
), "%{du:cbflags}", inFlags
);
15043 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->subtestProgress_callBack
,
15051 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP
, nowTimestamp
,
15052 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME
, inHostname
,
15053 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS
, inFlagsDescription
,
15054 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE
, (int64_t) inInterfaceIndex
,
15055 EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS
, addrStr
15057 require_noerr_quiet( err
, exit
);
15059 if ( inFlags
& kDNSServiceFlagsMoreComing
)
15062 if ( inFlags
& kDNSServiceFlagsAdd
)
15063 context
->operation
= RESULT_ADD
;
15065 context
->operation
= RESULT_RMV
;
15067 gettimeofday(&context
->notificationTime
, NULL
);
15069 if( err
) exit( 1 );
15072 //===========================================================================================================================
15073 // ExpensiveConstrainedInitializeContext
15074 //===========================================================================================================================
15076 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext
*context
)
15078 // clear the flags of the previous subtest
15079 context
->flags
= 0;
15080 context
->protocols
= 0;
15082 // get the parameter for the current subtest
15083 const ExpensiveConstrainedTestParams
*param
= &ExpensiveConstrainedSubtestParams
[context
->subtestIndex
];
15084 gExpensiveConstrainedTest_Name
= param
->qname
;
15085 gExpensiveConstrainedTest_DenyExpensive
= param
->deny_expensive
;
15086 gExpensiveConstrainedTest_DenyConstrained
= param
->deny_constrained
;
15087 gExpensiveConstrainedTest_StartFromExpensive
= param
->start_from_expensive
;
15088 gExpensiveConstrainedTest_ProtocolIPv4
= param
->ipv4_query
;
15089 gExpensiveConstrainedTest_ProtocolIPv6
= param
->ipv6_query
;
15092 //===========================================================================================================================
15093 // ExpensiveConstrainedStopAndCleanTheTest
15094 //===========================================================================================================================
15096 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext
*context
)
15098 // Stop the ongoing query
15099 if ( context
->opRef
!= NULL
)
15100 DNSServiceRefDeallocate( context
->opRef
);
15102 context
->opRef
= NULL
;
15103 context
->flags
= 0;
15104 context
->protocols
= 0;
15107 //===========================================================================================================================
15108 // ExpensiveConstrainedSubtestProgressReport
15109 //===========================================================================================================================
15111 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext
*context
)
15115 char startTime
[ 32 ];
15116 char endTime
[ 32 ];
15117 char expensive
[ 32 ];
15118 char constrained
[ 32 ];
15120 now
= NanoTimeGetCurrent();
15121 _NanoTime64ToTimestamp( context
->subtestProgress_startTime
, startTime
, sizeof( startTime
) );
15122 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
15124 snprintf( expensive
, sizeof( expensive
), "%s -> %s", context
->isExpensivePrev
? "True" : "False", context
->isExpensiveNow
? "True" : "False" );
15125 snprintf( constrained
, sizeof( constrained
), "%s -> %s", context
->isConstrainedPrev
? "True" : "False", context
->isConstrainedNow
? "True" : "False" );
15127 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->subtestProgress
,
15138 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME
, startTime
,
15139 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME
, endTime
,
15140 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE
, ExpensiveConstrainedStateString(context
->state
),
15141 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT
, ExpensiveConstrainedOperationString(context
->expectedOperation
),
15142 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT
, ExpensiveConstrainedOperationString(context
->operation
),
15143 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW
, expensive
,
15144 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW
, constrained
,
15145 EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK
, context
->subtestProgress_callBack
15147 require_noerr( err
, exit
);
15148 ForgetCF( &context
->subtestProgress_callBack
);
15152 ErrQuit( 1, "error: %#m\n", err
);
15155 //===========================================================================================================================
15156 // ExpensiveConstrainedFinalSubtestReport
15157 //===========================================================================================================================
15159 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext
*context
, const char *error_description
)
15163 char startTime
[ 32 ];
15164 char endTime
[ 32 ];
15165 char flagDescription
[ 1024 ];
15167 now
= NanoTimeGetCurrent();
15168 _NanoTime64ToTimestamp( context
->subtestReport_startTime
, startTime
, sizeof( startTime
) );
15169 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
15170 SNPrintF( flagDescription
, sizeof( flagDescription
), "%#{flags}", context
->flags
, kDNSServiceFlagsDescriptors
);
15172 if (error_description
!= NULL
)
15174 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->subtestReport
,
15187 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME
, startTime
,
15188 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME
, endTime
,
15189 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME
, context
->name
,
15190 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS
, flagDescription
,
15191 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS
, ExpensiveConstrainedProtocolString( context
->protocols
),
15192 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX
, (int64_t) context
->ifIndex
,
15193 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME
, context
->ifName
,
15194 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT
, CFSTR( "Fail" ),
15195 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR
, error_description
,
15196 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS
, context
->subtestProgress
15201 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->subtestReport
,
15213 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME
, startTime
,
15214 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME
, endTime
,
15215 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME
, context
->name
,
15216 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS
, flagDescription
,
15217 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS
, ExpensiveConstrainedProtocolString( context
->protocols
),
15218 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX
, (int64_t) context
->ifIndex
,
15219 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME
, context
->ifName
,
15220 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT
, CFSTR( "Pass" ),
15221 EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS
, context
->subtestProgress
15225 require_noerr( err
, exit
);
15226 ForgetCF( &context
->subtestProgress
);
15229 ErrQuit( 1, "error: %#m\n", err
);
15232 //===========================================================================================================================
15233 // ExpensiveConstrainedFinalResultReport
15234 //===========================================================================================================================
15236 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext
*context
, Boolean allPassed
)
15239 CFPropertyListRef plist
;
15241 char startTime
[ 32 ];
15242 char endTime
[ 32 ];
15244 now
= NanoTimeGetCurrent();
15245 _NanoTime64ToTimestamp( context
->testReport_startTime
, startTime
, sizeof( startTime
) );
15246 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
15248 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
15255 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME
, startTime
,
15256 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME
, endTime
,
15257 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED
, allPassed
,
15258 EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT
, context
->subtestReport
15260 require_noerr( err
, exit
);
15261 ForgetCF( &context
->subtestReport
);
15263 err
= OutputPropertyList( plist
, context
->outputFormat
, context
->outputFilePath
);
15264 CFRelease( plist
);
15265 require_noerr( err
, exit
);
15269 ErrQuit( 1, "error: %#m\n", err
);
15272 //===========================================================================================================================
15273 // ExpensiveConstrainedProtocolString
15274 //===========================================================================================================================
15276 static const char *ExpensiveConstrainedProtocolString( DNSServiceProtocol protocol
)
15278 const char *str
= NULL
;
15279 switch ( protocol
) {
15280 case kDNSServiceProtocol_IPv4
:
15283 case kDNSServiceProtocol_IPv6
:
15286 case kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
:
15287 str
= "IPv4 & IPv6";
15295 //===========================================================================================================================
15296 // ExpensiveConstrainedStateString
15297 //===========================================================================================================================
15299 static const char *ExpensiveConstrainedStateString( enum ExpensiveConstrainedTestState state
)
15301 const char *str
= NULL
;
15304 str
= "TEST_BEGIN";
15306 case TEST_EXPENSIVE_PREPARE
:
15307 str
= "TEST_EXPENSIVE_PREPARE";
15309 case TEST_EXPENSIVE
:
15310 str
= "TEST_EXPENSIVE";
15312 case TEST_CONSTRAINED_PREPARE
:
15313 str
= "TEST_CONSTRAINED_PREPARE";
15315 case TEST_CONSTRAINED
:
15316 str
= "TEST_CONSTRAINED";
15318 case TEST_EXPENSIVE_CONSTRAINED_PREPARE
:
15319 str
= "TEST_EXPENSIVE_CONSTRAINED_PREPARE";
15321 case TEST_EXPENSIVE_CONSTRAINED
:
15322 str
= "TEST_EXPENSIVE_CONSTRAINED";
15325 str
= "TEST_FAILED";
15327 case TEST_SUCCEEDED
:
15328 str
= "TEST_SUCCEEDED";
15338 //===========================================================================================================================
15339 // ExpensiveConstrainedOperationString
15340 //===========================================================================================================================
15342 static const char *ExpensiveConstrainedOperationString( enum ExpensiveConstrainedTestOperation operation
)
15344 const char *str
= NULL
;
15345 switch ( operation
) {
15347 str
= "RESULT_ADD";
15350 str
= "RESULT_RMV";
15362 //===========================================================================================================================
15363 // expensiveConstrainedEndsWith
15364 //===========================================================================================================================
15365 static Boolean
expensiveConstrainedEndsWith( const char *str
, const char *suffix
)
15367 if ( !str
|| !suffix
)
15369 size_t lenstr
= strlen( str
);
15370 size_t lensuffix
= strlen( suffix
);
15371 if ( lensuffix
> lenstr
)
15373 return strncmp( str
+ lenstr
- lensuffix
, suffix
, lensuffix
) == 0;
15376 //===========================================================================================================================
15377 // RegistrationTestCmd
15378 //===========================================================================================================================
15380 typedef struct RegistrationSubtest RegistrationSubtest
;
15384 CFMutableArrayRef subtestReports
; // Array of subtest reports.
15385 dispatch_source_t timer
; // Timer to enforce subtest durations.
15386 dispatch_source_t sigSourceINT
; // SIGINT signal handler for a clean test exit.
15387 dispatch_source_t sigSourceTERM
; // SIGTERM signal handler for a clean test exit.
15388 RegistrationSubtest
* subtest
; // Current subtest.
15389 char * outputFilePath
; // Path of test result output file. If NULL, stdout will be used.
15390 OutputFormatType outputFormat
; // Format of test results output.
15391 CFStringRef computerNamePrev
; // Previous ComputerName.
15392 CFStringRef localHostNamePrev
; // Previous LocalHostName.
15393 NanoTime64 startTime
; // Test's start time.
15394 char * computerName
; // Temporary ComputerName to set during testing. (malloc'd)
15395 char * localHostName
; // Temporary LocalHostName to set during testing. (malloc'd)
15396 CFStringEncoding computerNamePrevEncoding
; // Previous ComputerName's encoding.
15397 int subtestIndex
; // Index of current subtest.
15398 Boolean computerNameSet
; // True if a temporary ComputerName was set.
15399 Boolean localHostNameSet
; // True if a temporary LocalHostName was set.
15400 Boolean failed
; // True if at least one non-skipped subtest failed.
15401 Boolean forBATS
; // True if the test is running in a BATS environment.
15403 } RegistrationTest
;
15407 kRegistrationInterfaceSet_Null
= 0,
15408 kRegistrationInterfaceSet_All
= 1,
15409 kRegistrationInterfaceSet_AllPlusAWDL
= 2,
15410 kRegistrationInterfaceSet_LoopbackOnly
= 3,
15411 kRegistrationInterfaceSet_AWDLOnly
= 4
15413 } RegistrationInterfaceSet
;
15417 RegistrationInterfaceSet interfaceSet
; // Interfaces to register the service over.
15418 Boolean useDefaultName
; // True if registration is to use the default service name.
15419 Boolean useLODiscovery
; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
15421 } RegistrationSubtestParams
;
15423 static const RegistrationSubtestParams kRegistrationSubtestParams
[] =
15425 { kRegistrationInterfaceSet_All
, true, false },
15426 { kRegistrationInterfaceSet_All
, false, false },
15427 { kRegistrationInterfaceSet_AllPlusAWDL
, true, false },
15428 { kRegistrationInterfaceSet_AllPlusAWDL
, false, false },
15429 { kRegistrationInterfaceSet_LoopbackOnly
, true, false },
15430 { kRegistrationInterfaceSet_LoopbackOnly
, false, false },
15431 { kRegistrationInterfaceSet_AWDLOnly
, true, false },
15432 { kRegistrationInterfaceSet_AWDLOnly
, false, false },
15433 { kRegistrationInterfaceSet_All
, true, true },
15434 { kRegistrationInterfaceSet_All
, false, true },
15435 { kRegistrationInterfaceSet_AllPlusAWDL
, true, true },
15436 { kRegistrationInterfaceSet_AllPlusAWDL
, false, true },
15437 { kRegistrationInterfaceSet_LoopbackOnly
, true, true },
15438 { kRegistrationInterfaceSet_LoopbackOnly
, false, true },
15439 { kRegistrationInterfaceSet_AWDLOnly
, true, true },
15440 { kRegistrationInterfaceSet_AWDLOnly
, false, true }
15445 NanoTime64 browseResultTime
; // Per-interface browse result time.
15446 NanoTime64 querySRVResultTime
; // Per-interface SRV record query result time.
15447 NanoTime64 queryTXTResultTime
; // Per-interface TXT record query result time.
15449 } RegistrationResultTimes
;
15453 MDNSInterfaceItem base
; // Underlying MDNSInterface linked-list item.
15454 RegistrationResultTimes times
; // Per-interface result times.
15456 } RegistrationInterfaceItem
;
15458 struct RegistrationSubtest
15460 DNSServiceRef registration
; // DNS-SD service registration.
15461 DNSServiceRef connection
; // Shared DNS-SD connection.
15462 DNSServiceRef browse
; // DNS-SD browse for service's type.
15463 DNSServiceRef querySRV
; // DNS-SD query request for service's SRV record.
15464 DNSServiceRef queryTXT
; // DNS-SD query request for service's TXT record.
15465 CFMutableArrayRef unexpected
; // Array of unexpected registration, browse, and query results.
15466 #if( TARGET_OS_WATCH )
15467 CFMutableArrayRef ignored
; // Array of unexpected, but ignored, browse and query results.
15469 const char * serviceName
; // Service's name.
15470 char * serviceNameCustom
; // Service's name if using a custom name. (malloc'd)
15471 char * serviceType
; // Service's service type. (malloc'd)
15472 size_t serviceTypeLen
; // C string length of service's service type.
15473 char * serviceFQDN
; // Service's FQDN, i.e., name of its SRV and TXT records.
15474 uint8_t * txtPtr
; // Pointer to service's TXT record data. (malloc'd)
15475 size_t txtLen
; // Length of service's TXT record data.
15476 RegistrationInterfaceItem
* ifList
; // If ifIndex == 0, interfaces that service should register over.
15477 RegistrationResultTimes ifTimes
; // If ifIndex != 0, result times for interface with that index.
15478 RegistrationTest
* test
; // Pointer to parent test.
15479 NanoTime64 startTime
; // Subtest's start time.
15480 char * description
; // Subtest's description. (malloc'd)
15481 uint32_t ifIndex
; // Interface index used for service registration.
15482 uint16_t port
; // Service's port number.
15483 Boolean useLODiscovery
; // True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
15484 Boolean includeAWDL
; // True if the IncludeAWDL flag was used during registration.
15485 Boolean ifIsAWDL
; // True if ifIndex is the index of an AWDL interface.
15486 Boolean skipped
; // True if this subtest is to be skipped.
15487 Boolean registered
; // True if the test service was successfully registered.
15488 Boolean useDefaultName
; // True if the service is to use the default service name.
15491 static OSStatus
_RegistrationTestCreate( RegistrationTest
**outTest
);
15492 static void _RegistrationTestFree( RegistrationTest
*inTest
);
15493 static void _RegistrationTestBegin( void *inContext
);
15494 static void _RegistrationTestProceed( RegistrationTest
*inTest
);
15495 static OSStatus
_RegistrationTestStart( RegistrationTest
*inTest
);
15496 static void _RegistrationTestStop( RegistrationTest
*inTest
);
15497 #define _RegistrationTestForget( X ) ForgetCustomEx( X, _RegistrationTestStop, _RegistrationTestFree )
15499 _RegistrationTestStartSubtest(
15500 RegistrationTest
* inTest
,
15501 const RegistrationSubtestParams
* inParams
,
15502 Boolean
* outSkipped
);
15503 static OSStatus
_RegistrationTestEndSubtest( RegistrationTest
*inTest
);
15504 static void _RegistrationTestEnd( RegistrationTest
*inTest
) ATTRIBUTE_NORETURN
;
15505 static void _RegistrationTestExit( RegistrationTest
*inTest
, OSStatus inError
) ATTRIBUTE_NORETURN
;
15506 static OSStatus
_RegistrationSubtestCreate( RegistrationSubtest
**outSubtest
);
15507 static void _RegistrationSubtestStop( RegistrationSubtest
*inSubtest
);
15508 static void _RegistrationSubtestFree( RegistrationSubtest
*inSubtest
);
15509 #define _RegistrationSubtestForget( X ) ForgetCustomEx( X, _RegistrationSubtestStop, _RegistrationSubtestFree )
15510 static OSStatus
_RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL
, RegistrationInterfaceItem
**outList
);
15512 _RegistrationTestCreateRandomTXTRecord(
15515 uint8_t ** outTXTPtr
,
15516 size_t * outTXTLen
);
15517 static void DNSSD_API
15518 _RegistrationSubtestRegisterCallback(
15519 DNSServiceRef inSDRef
,
15520 DNSServiceFlags inFlags
,
15521 DNSServiceErrorType inError
,
15522 const char * inName
,
15523 const char * inType
,
15524 const char * inDomain
,
15525 void * inContext
);
15526 static void DNSSD_API
15527 _RegistrationSubtestBrowseCallback(
15528 DNSServiceRef inSDRef
,
15529 DNSServiceFlags inFlags
,
15530 uint32_t inIfIndex
,
15531 DNSServiceErrorType inError
,
15532 const char * inServiceName
,
15533 const char * inServiceType
,
15534 const char * inDomain
,
15535 void * inContext
);
15536 static void DNSSD_API
15537 _RegistrationSubtestQueryCallback(
15538 DNSServiceRef inSDRef
,
15539 DNSServiceFlags inFlags
,
15540 uint32_t inIfIndex
,
15541 DNSServiceErrorType inError
,
15542 const char * inName
,
15545 uint16_t inRDataLen
,
15546 const void * inRDataPtr
,
15548 void * inContext
);
15549 static Boolean
_RegistrationSubtestValidServiceType( const RegistrationSubtest
*inSubtest
, const char *inServiceType
);
15550 static RegistrationResultTimes
*
15551 _RegistrationSubtestGetInterfaceResultTimes(
15552 RegistrationSubtest
* inSubtest
,
15553 uint32_t inIfIndex
,
15554 Boolean
* outIsAWDL
);
15555 static void _RegistrationTestTimerHandler( void *inContext
);
15556 #if( TARGET_OS_WATCH )
15557 static Boolean
_RegistrationTestInterfaceIsWiFi( const char *inIfName
);
15560 static void RegistrationTestCmd( void )
15563 RegistrationTest
* test
;
15565 err
= _RegistrationTestCreate( &test
);
15566 require_noerr( err
, exit
);
15568 if( gRegistrationTest_BATSEnvironment
) test
->forBATS
= true;
15569 if( gRegistrationTest_OutputFilePath
)
15571 test
->outputFilePath
= strdup( gRegistrationTest_OutputFilePath
);
15572 require_action( test
->outputFilePath
, exit
, err
= kNoMemoryErr
);
15575 err
= OutputFormatFromArgString( gRegistrationTest_OutputFormat
, &test
->outputFormat
);
15576 require_noerr_quiet( err
, exit
);
15578 dispatch_async_f( dispatch_get_main_queue(), test
, _RegistrationTestBegin
);
15582 if( test
) _RegistrationTestFree( test
);
15583 ErrQuit( 1, "error: %#m\n", err
);
15586 //===========================================================================================================================
15588 static OSStatus
_RegistrationTestCreate( RegistrationTest
**outTest
)
15591 RegistrationTest
* obj
;
15593 obj
= (RegistrationTest
*) calloc( 1, sizeof( *obj
) );
15594 require_action( obj
, exit
, err
= kNoMemoryErr
);
15596 obj
->subtestReports
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
15597 require_action( obj
->subtestReports
, exit
, err
= kNoMemoryErr
);
15604 if( obj
) _RegistrationTestFree( obj
);
15608 //===========================================================================================================================
15610 static void _RegistrationTestFree( RegistrationTest
*inTest
)
15612 check( !inTest
->timer
);
15613 check( !inTest
->sigSourceINT
);
15614 check( !inTest
->sigSourceTERM
);
15615 check( !inTest
->computerNameSet
);
15616 check( !inTest
->localHostNameSet
);
15617 check( !inTest
->subtest
);
15618 ForgetCF( &inTest
->subtestReports
);
15619 ForgetMem( &inTest
->outputFilePath
);
15620 ForgetCF( &inTest
->computerNamePrev
);
15621 ForgetCF( &inTest
->localHostNamePrev
);
15622 ForgetMem( &inTest
->computerName
);
15623 ForgetMem( &inTest
->localHostName
);
15626 //===========================================================================================================================
15628 static void _RegistrationTestBegin( void *inContext
)
15630 _RegistrationTestProceed( (RegistrationTest
*) inContext
);
15633 //===========================================================================================================================
15635 static void _RegistrationTestProceed( RegistrationTest
*inTest
)
15638 Boolean skippedSubtest
;
15644 if( !inTest
->startTime
)
15646 err
= _RegistrationTestStart( inTest
);
15647 require_noerr_quiet( err
, exit
);
15649 inTest
->startTime
= NanoTimeGetCurrent();
15653 err
= _RegistrationTestEndSubtest( inTest
);
15654 require_noerr( err
, exit
);
15656 ++inTest
->subtestIndex
;
15659 subtestIndex
= inTest
->subtestIndex
;
15660 if( subtestIndex
< (int) countof( kRegistrationSubtestParams
) )
15662 err
= _RegistrationTestStartSubtest( inTest
, &kRegistrationSubtestParams
[ subtestIndex
], &skippedSubtest
);
15663 require_noerr_quiet( err
, exit
);
15667 _RegistrationTestEnd( inTest
);
15670 } while( skippedSubtest
);
15673 if( err
) _RegistrationTestExit( inTest
, err
);
15676 //===========================================================================================================================
15678 static void _RegistrationTestSignalHandler( void *inContext
);
15680 static OSStatus
_RegistrationTestStart( RegistrationTest
*inTest
)
15685 // Save original ComputerName and LocalHostName.
15687 check( !inTest
->computerNamePrev
);
15688 inTest
->computerNamePrev
= SCDynamicStoreCopyComputerName( NULL
, &inTest
->computerNamePrevEncoding
);
15689 err
= map_scerror( inTest
->computerNamePrev
);
15690 require_noerr( err
, exit
);
15692 check( !inTest
->localHostNamePrev
);
15693 inTest
->localHostNamePrev
= SCDynamicStoreCopyLocalHostName( NULL
);
15694 err
= map_scerror( inTest
->localHostNamePrev
);
15695 require_noerr( err
, exit
);
15697 // Generate a unique test ComputerName.
15699 check( !inTest
->computerName
);
15700 ASPrintF( &inTest
->computerName
, "dnssdutil-regtest-computer-name-%s",
15701 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
15702 require_action( inTest
->computerName
, exit
, err
= kNoMemoryErr
);
15704 // Generate a unique test LocalHostName.
15706 check( !inTest
->localHostName
);
15707 ASPrintF( &inTest
->localHostName
, "dnssdutil-regtest-local-hostname-%s",
15708 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
15709 require_action( inTest
->localHostName
, exit
, err
= kNoMemoryErr
);
15711 // Set up SIGINT signal handler.
15713 signal( SIGINT
, SIG_IGN
);
15714 check( !inTest
->sigSourceINT
);
15715 err
= DispatchSignalSourceCreate( SIGINT
, _RegistrationTestSignalHandler
, inTest
, &inTest
->sigSourceINT
);
15716 require_noerr( err
, exit
);
15717 dispatch_resume( inTest
->sigSourceINT
);
15719 // Set up SIGTERM signal handler.
15721 signal( SIGTERM
, SIG_IGN
);
15722 check( !inTest
->sigSourceTERM
);
15723 err
= DispatchSignalSourceCreate( SIGTERM
, _RegistrationTestSignalHandler
, inTest
, &inTest
->sigSourceTERM
);
15724 require_noerr( err
, exit
);
15725 dispatch_resume( inTest
->sigSourceTERM
);
15727 // Set test ComputerName.
15729 check( !inTest
->computerNameSet
);
15730 err
= _SetComputerNameWithUTF8CString( inTest
->computerName
);
15731 require_noerr( err
, exit
);
15732 inTest
->computerNameSet
= true;
15734 // Set test LocalHostName.
15736 check( !inTest
->localHostNameSet
);
15737 err
= _SetLocalHostNameWithUTF8CString( inTest
->localHostName
);
15738 require_noerr( err
, exit
);
15739 inTest
->localHostNameSet
= true;
15742 if( err
) _RegistrationTestStop( inTest
);
15746 static void _RegistrationTestSignalHandler( void *inContext
)
15748 RegistrationTest
* const test
= (RegistrationTest
*) inContext
;
15750 FPrintF( stderr
, "Registration test got a SIGINT or SIGTERM signal, exiting..." );
15752 _RegistrationTestExit( test
, kCanceledErr
);
15755 //===========================================================================================================================
15757 static void _RegistrationTestStop( RegistrationTest
*inTest
)
15761 dispatch_source_forget( &inTest
->timer
);
15762 dispatch_source_forget( &inTest
->sigSourceINT
);
15763 dispatch_source_forget( &inTest
->sigSourceTERM
);
15764 _RegistrationSubtestForget( &inTest
->subtest
);
15765 if( inTest
->computerNameSet
)
15767 err
= _SetComputerName( inTest
->computerNamePrev
, inTest
->computerNamePrevEncoding
);
15768 check_noerr( err
);
15769 if( !err
) inTest
->computerNameSet
= false;
15771 if( inTest
->localHostNameSet
)
15773 err
= _SetLocalHostName( inTest
->localHostNamePrev
);
15774 check_noerr( err
);
15775 if( !err
) inTest
->localHostNameSet
= false;
15779 //===========================================================================================================================
15781 #define kRegistrationTestSubtestDurationSecs 5
15784 _RegistrationTestStartSubtest(
15785 RegistrationTest
* inTest
,
15786 const RegistrationSubtestParams
* inParams
,
15787 Boolean
* outSkipped
)
15790 RegistrationSubtest
* subtest
;
15791 const char * interfaceDesc
;
15792 DNSServiceFlags flags
;
15796 err
= _RegistrationSubtestCreate( &subtest
);
15797 require_noerr( err
, exit
);
15799 subtest
->test
= inTest
;
15800 subtest
->useDefaultName
= inParams
->useDefaultName
;
15801 subtest
->useLODiscovery
= inParams
->useLODiscovery
;
15803 // Determine registration interfaces.
15805 switch( inParams
->interfaceSet
)
15807 case kRegistrationInterfaceSet_All
:
15808 subtest
->ifIndex
= kDNSServiceInterfaceIndexAny
;
15810 if( !subtest
->useLODiscovery
)
15812 err
= _RegistrationTestInterfaceListCreate( false, &subtest
->ifList
);
15813 require_noerr( err
, exit
);
15815 interfaceDesc
= "all interfaces (excluding AWDL)";
15818 case kRegistrationInterfaceSet_AllPlusAWDL
:
15819 subtest
->ifIndex
= kDNSServiceInterfaceIndexAny
;
15820 subtest
->includeAWDL
= true;
15822 if( !subtest
->useLODiscovery
)
15824 err
= _RegistrationTestInterfaceListCreate( true, &subtest
->ifList
);
15825 require_noerr( err
, exit
);
15827 interfaceDesc
= "all interfaces (including AWDL)";
15830 case kRegistrationInterfaceSet_LoopbackOnly
:
15831 subtest
->ifIndex
= if_nametoindex( "lo0" );
15832 if( subtest
->ifIndex
== 0 )
15834 FPrintF( stderr
, "Failed to get index for loopback interface lo0.\n" );
15835 err
= kNoResourcesErr
;
15838 interfaceDesc
= "loopback interface";
15841 case kRegistrationInterfaceSet_AWDLOnly
:
15842 err
= _MDNSInterfaceGetAny( kMDNSInterfaceSubset_AWDL
, NULL
, &subtest
->ifIndex
);
15843 if( err
== kNotFoundErr
)
15845 FPrintF( stderr
, "Warning: No mDNS-capable AWDL interface is available.\n" );
15846 subtest
->skipped
= true;
15849 require_noerr( err
, exit
);
15851 subtest
->ifIsAWDL
= true;
15852 interfaceDesc
= "AWDL interface";
15860 // Create description.
15862 ASPrintF( &subtest
->description
, "Service registration over %s using %s service name.%s",
15863 interfaceDesc
, subtest
->useDefaultName
? "default" : "custom",
15864 subtest
->useLODiscovery
? " (LocalOnly discovery)" : "" );
15865 require_action( subtest
->description
, exit
, err
= kNoMemoryErr
);
15867 if( subtest
->skipped
)
15869 subtest
->startTime
= NanoTimeGetCurrent();
15873 // Generate a service name.
15875 if( subtest
->useDefaultName
)
15877 subtest
->serviceName
= inTest
->computerName
;
15881 ASPrintF( &subtest
->serviceNameCustom
, "dnssdutil-regtest-service-name-%s",
15882 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
15883 require_action( subtest
->serviceNameCustom
, exit
, err
= kNoMemoryErr
);
15885 subtest
->serviceName
= subtest
->serviceNameCustom
;
15888 // Generate a service type.
15890 ASPrintF( &subtest
->serviceType
, "_regtest-%s._udp",
15891 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
15892 require_action( subtest
->serviceType
, exit
, err
= kNoMemoryErr
);
15894 subtest
->serviceTypeLen
= strlen( subtest
->serviceType
);
15896 // Create SRV and TXT record name FQDN.
15898 ASPrintF( &subtest
->serviceFQDN
, "%s.%s.local.", subtest
->serviceName
, subtest
->serviceType
);
15899 require_action( subtest
->serviceFQDN
, exit
, err
= kNoMemoryErr
);
15901 // Generate a port number.
15903 subtest
->port
= (uint16_t) RandomRange( 60000, 65535 );
15905 // Generate TXT record data.
15907 err
= _RegistrationTestCreateRandomTXTRecord( 100, 1000, &subtest
->txtPtr
, &subtest
->txtLen
);
15908 require_noerr( err
, exit
);
15910 // Register service.
15912 subtest
->startTime
= NanoTimeGetCurrent();
15914 flags
= kDNSServiceFlagsNoAutoRename
;
15915 if( subtest
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
15916 err
= DNSServiceRegister( &subtest
->registration
, flags
, subtest
->ifIndex
,
15917 subtest
->useDefaultName
? NULL
: subtest
->serviceNameCustom
, subtest
->serviceType
, "local.",
15918 NULL
, htons( subtest
->port
), (uint16_t) subtest
->txtLen
, subtest
->txtPtr
,
15919 _RegistrationSubtestRegisterCallback
, subtest
);
15920 require_noerr( err
, exit
);
15922 err
= DNSServiceSetDispatchQueue( subtest
->registration
, dispatch_get_main_queue() );
15923 require_noerr( err
, exit
);
15927 check( !inTest
->timer
);
15928 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kRegistrationTestSubtestDurationSecs
),
15929 INT64_C_safe( kRegistrationTestSubtestDurationSecs
) * kNanosecondsPerSecond
/ 10, dispatch_get_main_queue(),
15930 _RegistrationTestTimerHandler
, inTest
, &inTest
->timer
);
15931 require_noerr( err
, exit
);
15932 dispatch_resume( inTest
->timer
);
15935 *outSkipped
= subtest
->skipped
;
15937 check( !inTest
->subtest
);
15938 inTest
->subtest
= subtest
;
15942 _RegistrationSubtestForget( &subtest
);
15946 //===========================================================================================================================
15948 #define kRegistrationTestReportKey_ComputerName CFSTR( "computerName" ) // String
15949 #define kRegistrationTestReportKey_Description CFSTR( "description" ) // String
15950 #define kRegistrationTestReportKey_Domain CFSTR( "domain" ) // String
15951 #define kRegistrationTestReportKey_EndTime CFSTR( "endTime" ) // String
15952 #define kRegistrationTestReportKey_Error CFSTR( "error" ) // Integer
15953 #define kRegistrationTestReportKey_Flags CFSTR( "flags" ) // Integer
15954 #define kRegistrationTestReportKey_IgnoredResults CFSTR( "ignoredResults" ) // Array of dictionaries
15955 #define kRegistrationTestReportKey_InterfaceIndex CFSTR( "ifIndex" ) // Integer
15956 #define kRegistrationTestReportKey_InterfaceName CFSTR( "ifName" ) // String
15957 #define kRegistrationTestReportKey_LocalHostName CFSTR( "localHostName" ) // String
15958 #define kRegistrationTestReportKey_MissingResults CFSTR( "missingResults" ) // Array of dictionaries
15959 #define kRegistrationTestReportKey_Pass CFSTR( "pass" ) // Boolean
15960 #define kRegistrationTestReportKey_Port CFSTR( "port" ) // Integer
15961 #define kRegistrationTestReportKey_RDataFormatted CFSTR( "rdataFormatted" ) // String
15962 #define kRegistrationTestReportKey_RDataHexString CFSTR( "rdataHexString" ) // String
15963 #define kRegistrationTestReportKey_RecordClass CFSTR( "recordClass" ) // Integer
15964 #define kRegistrationTestReportKey_RecordType CFSTR( "recordType" ) // Integer
15965 #define kRegistrationTestReportKey_Registered CFSTR( "registered" ) // Boolean
15966 #define kRegistrationTestReportKey_ResultType CFSTR( "resultType" ) // String
15967 #define kRegistrationTestReportKey_ServiceFQDN CFSTR( "serviceFQDN" ) // String
15968 #define kRegistrationTestReportKey_ServiceName CFSTR( "serviceName" ) // String
15969 #define kRegistrationTestReportKey_ServiceType CFSTR( "serviceType" ) // String
15970 #define kRegistrationTestReportKey_Skipped CFSTR( "skipped" ) // Boolean
15971 #define kRegistrationTestReportKey_StartTime CFSTR( "startTime" ) // String
15972 #define kRegistrationTestReportKey_Subtests CFSTR( "subtests" ) // Array of dictionaries
15973 #define kRegistrationTestReportKey_Timestamp CFSTR( "timestamp" ) // String
15974 #define kRegistrationTestReportKey_TXT CFSTR( "txt" ) // String
15975 #define kRegistrationTestReportKey_UnexpectedResults CFSTR( "unexpectedResults" ) // Array of dictionaries
15976 #define kRegistrationTestReportKey_UsedDefaultName CFSTR( "usedDefaultName" ) // Boolean
15977 #define kRegistrationTestReportKey_UsedLODiscovery CFSTR( "usedLODiscovery" ) // Boolean
15979 #define kRegistrationTestResultType_Browse CFSTR( "browse" )
15980 #define kRegistrationTestResultType_Query CFSTR( "query" )
15981 #define kRegistrationTestResultType_QuerySRV CFSTR( "querySRV" )
15982 #define kRegistrationTestResultType_QueryTXT CFSTR( "queryTXT" )
15983 #define kRegistrationTestResultType_Registration CFSTR( "registration" )
15986 _RegistrationTestAppendMissingResults(
15987 CFMutableArrayRef inMissingResults
,
15988 const RegistrationResultTimes
* inTimes
,
15989 uint32_t inIfIndex
,
15990 const char * inIfName
);
15992 static OSStatus
_RegistrationTestEndSubtest( RegistrationTest
*inTest
)
15995 RegistrationSubtest
* subtest
;
15996 CFMutableDictionaryRef subtestReport
;
15997 CFMutableArrayRef missing
;
16000 Boolean subtestFailed
;
16001 char startTime
[ 32 ];
16002 char endTime
[ 32 ];
16003 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
16005 now
= NanoTimeGetCurrent();
16007 subtest
= inTest
->subtest
;
16008 inTest
->subtest
= NULL
;
16009 _RegistrationSubtestStop( subtest
);
16012 subtestReport
= NULL
;
16014 if( subtest
->txtPtr
)
16016 err
= DNSRecordDataToString( subtest
->txtPtr
, subtest
->txtLen
, kDNSServiceType_TXT
, NULL
, 0, &txtStr
);
16017 require_noerr( err
, exit
);
16019 _NanoTime64ToTimestamp( subtest
->startTime
, startTime
, sizeof( startTime
) );
16020 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
16021 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &subtestReport
,
16023 "%kO=%s" // description
16024 "%kO=%s" // startTime
16025 "%kO=%s" // endTime
16026 "%kO=%s" // serviceFQDN
16027 "%kO=%lli" // ifIndex
16031 "%kO=%b" // registered
16032 "%kO=%b" // usedDefaultName
16033 "%kO=%b" // usedLODiscovery
16035 kRegistrationTestReportKey_Description
, subtest
->description
,
16036 kRegistrationTestReportKey_StartTime
, startTime
,
16037 kRegistrationTestReportKey_EndTime
, endTime
,
16038 kRegistrationTestReportKey_ServiceFQDN
, subtest
->serviceFQDN
,
16039 kRegistrationTestReportKey_InterfaceIndex
, (int64_t) subtest
->ifIndex
,
16040 kRegistrationTestReportKey_InterfaceName
, if_indextoname( subtest
->ifIndex
, ifNameBuf
),
16041 kRegistrationTestReportKey_Port
, (int64_t) subtest
->port
,
16042 kRegistrationTestReportKey_TXT
, txtStr
,
16043 kRegistrationTestReportKey_Registered
, (int) subtest
->registered
,
16044 kRegistrationTestReportKey_UsedDefaultName
, (int) subtest
->useDefaultName
,
16045 kRegistrationTestReportKey_UsedLODiscovery
, (int) subtest
->useLODiscovery
);
16046 ForgetMem( &txtStr
);
16047 require_noerr( err
, exit
);
16049 if( !subtest
->skipped
&& subtest
->registered
)
16051 missing
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
16052 require_action( missing
, exit
, err
= kNoMemoryErr
);
16054 if( subtest
->ifList
)
16056 RegistrationInterfaceItem
* item
;
16058 for( item
= subtest
->ifList
; item
; item
= (RegistrationInterfaceItem
*) item
->base
.next
)
16060 #if( TARGET_OS_WATCH )
16061 if( inTest
->forBATS
&& item
->base
.isWiFi
) continue;
16063 err
= _RegistrationTestAppendMissingResults( missing
, &item
->times
, item
->base
.ifIndex
, item
->base
.ifName
);
16064 require_noerr( err
, exit
);
16069 err
= _RegistrationTestAppendMissingResults( missing
, &subtest
->ifTimes
, subtest
->ifIndex
, NULL
);
16070 require_noerr( err
, exit
);
16073 subtestFailed
= false;
16074 if( CFArrayGetCount( missing
) > 0 )
16076 subtestFailed
= true;
16077 CFDictionarySetValue( subtestReport
, kRegistrationTestReportKey_MissingResults
, missing
);
16079 if( CFArrayGetCount( subtest
->unexpected
) > 0 )
16081 subtestFailed
= true;
16082 CFDictionarySetValue( subtestReport
, kRegistrationTestReportKey_UnexpectedResults
, subtest
->unexpected
);
16084 #if( TARGET_OS_WATCH )
16085 if( CFArrayGetCount( subtest
->ignored
) > 0 )
16087 CFDictionarySetValue( subtestReport
, kRegistrationTestReportKey_IgnoredResults
, subtest
->ignored
);
16093 subtestFailed
= true;
16096 CFDictionarySetBoolean( subtestReport
, kRegistrationTestReportKey_Pass
, subtestFailed
? false : true );
16097 if( subtestFailed
)
16099 CFDictionarySetBoolean( subtestReport
, kRegistrationTestReportKey_Skipped
, subtest
->skipped
);
16100 if( !subtest
->skipped
) inTest
->failed
= true;
16102 CFArrayAppendValue( inTest
->subtestReports
, subtestReport
);
16105 CFReleaseNullSafe( missing
);
16106 CFReleaseNullSafe( subtestReport
);
16107 _RegistrationSubtestFree( subtest
);
16112 _RegistrationTestAppendMissingResult(
16113 CFMutableArrayRef inMissingResults
,
16114 CFStringRef inType
,
16115 uint32_t inIfIndex
,
16116 const char * inIfName
);
16119 _RegistrationTestAppendMissingResults(
16120 CFMutableArrayRef inMissingResults
,
16121 const RegistrationResultTimes
* inTimes
,
16122 uint32_t inIfIndex
,
16123 const char * inIfName
)
16127 if( !inTimes
->browseResultTime
)
16129 err
= _RegistrationTestAppendMissingResult( inMissingResults
, kRegistrationTestResultType_Browse
,
16130 inIfIndex
, inIfName
);
16131 require_noerr( err
, exit
);
16133 if( !inTimes
->querySRVResultTime
)
16135 err
= _RegistrationTestAppendMissingResult( inMissingResults
, kRegistrationTestResultType_QuerySRV
,
16136 inIfIndex
, inIfName
);
16137 require_noerr( err
, exit
);
16139 if( !inTimes
->queryTXTResultTime
)
16141 err
= _RegistrationTestAppendMissingResult( inMissingResults
, kRegistrationTestResultType_QueryTXT
,
16142 inIfIndex
, inIfName
);
16143 require_noerr( err
, exit
);
16152 _RegistrationTestAppendMissingResult(
16153 CFMutableArrayRef inMissingResults
,
16154 CFStringRef inType
,
16155 uint32_t inIfIndex
,
16156 const char * inIfName
)
16159 char ifName
[ IF_NAMESIZE
+ 1 ];
16161 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, inMissingResults
,
16163 "%kO=%O" // resultType
16164 "%kO=%lli" // ifIndex
16167 kRegistrationTestReportKey_ResultType
, inType
,
16168 kRegistrationTestReportKey_InterfaceIndex
, (int64_t) inIfIndex
,
16169 kRegistrationTestReportKey_InterfaceName
, inIfName
? inIfName
: if_indextoname( inIfIndex
, ifName
) );
16173 //===========================================================================================================================
16175 static void _RegistrationTestEnd( RegistrationTest
*inTest
)
16179 CFPropertyListRef plist
;
16180 char startTime
[ 32 ];
16181 char endTime
[ 32 ];
16183 now
= NanoTimeGetCurrent();
16184 _NanoTime64ToTimestamp( inTest
->startTime
, startTime
, sizeof( startTime
) );
16185 _NanoTime64ToTimestamp( now
, endTime
, sizeof( endTime
) );
16187 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
16189 "%kO=%s" // startTime
16190 "%kO=%s" // endTime
16191 "%kO=%s" // computerName
16192 "%kO=%s" // localHostName
16193 "%kO=%O" // subtests
16196 kRegistrationTestReportKey_StartTime
, startTime
,
16197 kRegistrationTestReportKey_EndTime
, endTime
,
16198 kRegistrationTestReportKey_ComputerName
, inTest
->computerName
,
16199 kRegistrationTestReportKey_LocalHostName
, inTest
->localHostName
,
16200 kRegistrationTestReportKey_Subtests
, inTest
->subtestReports
,
16201 kRegistrationTestReportKey_Pass
, inTest
->failed
? false : true );
16202 require_noerr( err
, exit
);
16204 err
= OutputPropertyList( plist
, inTest
->outputFormat
, inTest
->outputFilePath
);
16205 CFRelease( plist
);
16206 require_noerr( err
, exit
);
16209 _RegistrationTestExit( inTest
, err
);
16212 //===========================================================================================================================
16214 static void _RegistrationTestExit( RegistrationTest
*inTest
, OSStatus inError
)
16220 FPrintF( stderr
, "error: %#m\n", inError
);
16225 exitCode
= inTest
->failed
? 2 : 0;
16227 _RegistrationTestForget( &inTest
);
16231 //===========================================================================================================================
16233 static OSStatus
_RegistrationSubtestCreate( RegistrationSubtest
**outSubtest
)
16236 RegistrationSubtest
* obj
;
16238 obj
= (RegistrationSubtest
*) calloc( 1, sizeof( *obj
) );
16239 require_action( obj
, exit
, err
= kNoMemoryErr
);
16241 obj
->unexpected
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
16242 require_action( obj
->unexpected
, exit
, err
= kNoMemoryErr
);
16244 #if( TARGET_OS_WATCH )
16245 obj
->ignored
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
16246 require_action( obj
->ignored
, exit
, err
= kNoMemoryErr
);
16254 if( obj
) _RegistrationSubtestFree( obj
);
16258 //===========================================================================================================================
16260 static void _RegistrationSubtestFree( RegistrationSubtest
*inSubtest
)
16262 check( !inSubtest
->registration
);
16263 check( !inSubtest
->browse
);
16264 check( !inSubtest
->querySRV
);
16265 check( !inSubtest
->queryTXT
);
16266 check( !inSubtest
->connection
);
16267 ForgetMem( &inSubtest
->serviceNameCustom
);
16268 ForgetMem( &inSubtest
->serviceType
);
16269 ForgetMem( &inSubtest
->serviceFQDN
);
16270 ForgetMem( &inSubtest
->txtPtr
);
16271 ForgetCF( &inSubtest
->unexpected
);
16272 #if( TARGET_OS_WATCH )
16273 ForgetCF( &inSubtest
->ignored
);
16275 _MDNSInterfaceListForget( (MDNSInterfaceItem
**) &inSubtest
->ifList
);
16276 ForgetMem( &inSubtest
->description
);
16280 //===========================================================================================================================
16282 static void _RegistrationSubtestStop( RegistrationSubtest
*inSubtest
)
16284 DNSServiceForget( &inSubtest
->registration
);
16285 DNSServiceForget( &inSubtest
->browse
);
16286 DNSServiceForget( &inSubtest
->querySRV
);
16287 DNSServiceForget( &inSubtest
->queryTXT
);
16288 DNSServiceForget( &inSubtest
->connection
);
16291 //===========================================================================================================================
16293 static OSStatus
_RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL
, RegistrationInterfaceItem
**outList
)
16296 RegistrationInterfaceItem
* list
;
16297 const MDNSInterfaceSubset subset
= inIncludeAWDL
? kMDNSInterfaceSubset_All
: kMDNSInterfaceSubset_NonAWDL
;
16299 err
= _MDNSInterfaceListCreate( subset
, sizeof( *list
), (MDNSInterfaceItem
**) &list
);
16300 require_noerr( err
, exit
);
16308 //===========================================================================================================================
16311 _RegistrationTestCreateRandomTXTRecord(
16314 uint8_t ** outTXTPtr
,
16315 size_t * outTXTLen
)
16319 const uint8_t * txtEnd
;
16320 uint8_t * txtPtr
= NULL
;
16323 require_action_quiet( inMinLen
<= inMaxLen
, exit
, err
= kSizeErr
);
16325 txtLen
= RandomRange( inMinLen
, inMaxLen
);
16326 txtPtr
= (uint8_t *) malloc( txtLen
+ 1 );
16327 require_action( txtPtr
, exit
, err
= kNoMemoryErr
);
16329 _RandomStringExact( kAlphaNumericCharSet
, sizeof_string( kAlphaNumericCharSet
), txtLen
, (char *)txtPtr
);
16332 txtEnd
= &txtPtr
[ txtLen
];
16333 while( ptr
< txtEnd
)
16335 size_t maxLen
, len
;
16337 maxLen
= ( (size_t)( txtEnd
- ptr
) ) - 1;
16338 len
= RandomRange( 1, 255 );
16339 if( len
> maxLen
) len
= maxLen
;
16341 *ptr
= (uint8_t) len
;
16342 ptr
+= ( 1 + len
);
16344 check( ptr
== txtEnd
);
16348 *outTXTPtr
= txtPtr
;
16351 if( outTXTLen
) *outTXTLen
= txtLen
;
16355 FreeNullSafe( txtPtr
);
16359 //===========================================================================================================================
16361 static void DNSSD_API
16362 _RegistrationSubtestRegisterCallback(
16363 DNSServiceRef inSDRef
,
16364 DNSServiceFlags inFlags
,
16365 DNSServiceErrorType inError
,
16366 const char * inServiceName
,
16367 const char * inServiceType
,
16368 const char * inDomain
,
16372 const NanoTime64 now
= NanoTimeGetCurrent();
16373 RegistrationSubtest
* const subtest
= (RegistrationSubtest
*) inContext
;
16377 if( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
&&
16378 ( strcasecmp( inServiceName
, subtest
->serviceName
) == 0 ) &&
16379 _RegistrationSubtestValidServiceType( subtest
, inServiceType
) &&
16380 ( strcasecmp( inDomain
, "local." ) == 0 ) )
16382 if( !subtest
->registered
)
16384 DNSServiceRef sdRef
;
16385 const DNSServiceFlags flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsIncludeAWDL
;
16387 subtest
->registered
= true;
16389 // Create shared connection.
16391 check( !subtest
->connection
);
16392 err
= DNSServiceCreateConnection( &subtest
->connection
);
16393 require_noerr( err
, exit
);
16395 err
= DNSServiceSetDispatchQueue( subtest
->connection
, dispatch_get_main_queue() );
16396 require_noerr( err
, exit
);
16400 check( !subtest
->browse
);
16401 sdRef
= subtest
->connection
;
16402 err
= DNSServiceBrowse( &sdRef
, flags
,
16403 subtest
->useLODiscovery
? kDNSServiceInterfaceIndexLocalOnly
: kDNSServiceInterfaceIndexAny
,
16404 subtest
->serviceType
, "local.", _RegistrationSubtestBrowseCallback
, subtest
);
16405 require_noerr( err
, exit
);
16407 subtest
->browse
= sdRef
;
16412 char timestamp
[ 32 ];
16414 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, subtest
->unexpected
,
16416 "%kO=%O" // resultType
16417 "%kO=%s" // timestamp
16418 "%kO=%lli" // flags
16419 "%kO=%lli" // error
16420 "%kO=%s" // serviceName
16421 "%kO=%s" // serviceType
16424 kRegistrationTestReportKey_ResultType
, kRegistrationTestResultType_Registration
,
16425 kRegistrationTestReportKey_Timestamp
, _NanoTime64ToTimestamp( now
, timestamp
, sizeof( timestamp
) ),
16426 kRegistrationTestReportKey_Flags
, (int64_t) inFlags
,
16427 kRegistrationTestReportKey_Error
, (int64_t) inError
,
16428 kRegistrationTestReportKey_ServiceName
, inServiceName
,
16429 kRegistrationTestReportKey_ServiceType
, inServiceType
,
16430 kRegistrationTestReportKey_Domain
, inDomain
);
16431 require_noerr( err
, exit
);
16436 if( err
) _RegistrationTestExit( subtest
->test
, err
);
16439 //===========================================================================================================================
16441 static void DNSSD_API
16442 _RegistrationSubtestBrowseCallback(
16443 DNSServiceRef inSDRef
,
16444 DNSServiceFlags inFlags
,
16445 uint32_t inIfIndex
,
16446 DNSServiceErrorType inError
,
16447 const char * inServiceName
,
16448 const char * inServiceType
,
16449 const char * inDomain
,
16454 RegistrationSubtest
* const subtest
= (RegistrationSubtest
*) inContext
;
16455 Boolean serviceIsCorrect
, resultIsExpected
;
16459 now
= NanoTimeGetCurrent();
16460 if( !inError
&& ( strcasecmp( inServiceName
, subtest
->serviceName
) == 0 ) &&
16461 _RegistrationSubtestValidServiceType( subtest
, inServiceType
) && ( strcasecmp( inDomain
, "local." ) == 0 ) )
16463 serviceIsCorrect
= true;
16467 serviceIsCorrect
= false;
16470 resultIsExpected
= false;
16471 if( serviceIsCorrect
&& ( inFlags
& kDNSServiceFlagsAdd
) )
16473 RegistrationResultTimes
* times
;
16475 times
= _RegistrationSubtestGetInterfaceResultTimes( subtest
, inIfIndex
, NULL
);
16478 DNSServiceRef sdRef
;
16479 const DNSServiceFlags flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsIncludeAWDL
;
16482 resultIsExpected
= true;
16483 if( !times
->browseResultTime
) times
->browseResultTime
= now
;
16485 ifIndex
= subtest
->useLODiscovery
? kDNSServiceInterfaceIndexLocalOnly
: kDNSServiceInterfaceIndexAny
;
16486 if( !subtest
->querySRV
)
16488 // Start SRV record query.
16490 sdRef
= subtest
->connection
;
16491 err
= DNSServiceQueryRecord( &sdRef
, flags
, ifIndex
, subtest
->serviceFQDN
, kDNSServiceType_SRV
,
16492 kDNSServiceClass_IN
, _RegistrationSubtestQueryCallback
, subtest
);
16493 require_noerr( err
, exit
);
16495 subtest
->querySRV
= sdRef
;
16497 if( !subtest
->queryTXT
)
16499 // Start TXT record query.
16501 sdRef
= subtest
->connection
;
16502 err
= DNSServiceQueryRecord( &sdRef
, flags
, ifIndex
, subtest
->serviceFQDN
, kDNSServiceType_TXT
,
16503 kDNSServiceClass_IN
, _RegistrationSubtestQueryCallback
, subtest
);
16504 require_noerr( err
, exit
);
16506 subtest
->queryTXT
= sdRef
;
16511 if( !resultIsExpected
)
16513 CFMutableArrayRef resultArray
;
16514 char timestamp
[ 32 ];
16515 const char * ifNamePtr
;
16516 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
16518 ifNamePtr
= if_indextoname( inIfIndex
, ifNameBuf
);
16519 resultArray
= subtest
->unexpected
;
16520 #if( TARGET_OS_WATCH )
16521 if( subtest
->test
->forBATS
&& ( subtest
->ifIndex
== kDNSServiceInterfaceIndexAny
) &&
16522 ifNamePtr
&& _RegistrationTestInterfaceIsWiFi( ifNamePtr
) && serviceIsCorrect
)
16524 resultArray
= subtest
->ignored
;
16527 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, resultArray
,
16529 "%kO=%O" // resultType
16530 "%kO=%s" // timestamp
16531 "%kO=%lli" // flags
16532 "%kO=%lli" // ifIndex
16534 "%kO=%lli" // error
16535 "%kO=%s" // serviceName
16536 "%kO=%s" // serviceType
16539 kRegistrationTestReportKey_ResultType
, kRegistrationTestResultType_Browse
,
16540 kRegistrationTestReportKey_Timestamp
, _NanoTime64ToTimestamp( now
, timestamp
, sizeof( timestamp
) ),
16541 kRegistrationTestReportKey_Flags
, (int64_t) inFlags
,
16542 kRegistrationTestReportKey_InterfaceIndex
, (int64_t) inIfIndex
,
16543 kRegistrationTestReportKey_InterfaceName
, ifNamePtr
,
16544 kRegistrationTestReportKey_Error
, (int64_t) inError
,
16545 kRegistrationTestReportKey_ServiceName
, inServiceName
,
16546 kRegistrationTestReportKey_ServiceType
, inServiceType
,
16547 kRegistrationTestReportKey_Domain
, inDomain
);
16548 require_noerr( err
, exit
);
16553 if( err
) _RegistrationTestExit( subtest
->test
, err
);
16556 //===========================================================================================================================
16559 _RegistrationSubtestIsSRVRecordDataValid(
16560 RegistrationSubtest
* inSubtest
,
16561 const uint8_t * inRDataPtr
,
16563 Boolean inExpectRandHostname
);
16565 static void DNSSD_API
16566 _RegistrationSubtestQueryCallback(
16567 DNSServiceRef inSDRef
,
16568 DNSServiceFlags inFlags
,
16569 uint32_t inIfIndex
,
16570 DNSServiceErrorType inError
,
16571 const char * inName
,
16574 uint16_t inRDataLen
,
16575 const void * inRDataPtr
,
16581 RegistrationSubtest
* const subtest
= (RegistrationSubtest
*) inContext
;
16582 Boolean resultIsExpected
;
16587 now
= NanoTimeGetCurrent();
16588 resultIsExpected
= false;
16589 if( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
&& ( strcasecmp( inName
, subtest
->serviceFQDN
) == 0 ) &&
16590 ( inClass
== kDNSServiceClass_IN
) )
16592 RegistrationResultTimes
* times
;
16595 times
= _RegistrationSubtestGetInterfaceResultTimes( subtest
, inIfIndex
, &isAWDL
);
16598 if( inType
== kDNSServiceType_SRV
)
16600 Boolean expectRandHostname
;
16602 if( isAWDL
|| ( ( subtest
->ifIndex
== kDNSServiceInterfaceIndexAny
) && subtest
->includeAWDL
) )
16604 expectRandHostname
= true;
16608 expectRandHostname
= false;
16610 if( _RegistrationSubtestIsSRVRecordDataValid( subtest
, inRDataPtr
, inRDataLen
, expectRandHostname
) )
16612 resultIsExpected
= true;
16613 if( !times
->querySRVResultTime
) times
->querySRVResultTime
= now
;
16616 else if( inType
== kDNSServiceType_TXT
)
16618 if( MemEqual( inRDataPtr
, inRDataLen
, subtest
->txtPtr
, subtest
->txtLen
) )
16620 resultIsExpected
= true;
16621 if( !times
->queryTXTResultTime
) times
->queryTXTResultTime
= now
;
16627 if( !resultIsExpected
)
16629 CFMutableArrayRef resultArray
;
16630 CFMutableDictionaryRef resultDict
;
16631 CFStringRef rdataKey
;
16633 const char * ifNamePtr
;
16634 char timestamp
[ 32 ];
16635 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
16637 ifNamePtr
= if_indextoname( inIfIndex
, ifNameBuf
);
16638 resultArray
= subtest
->unexpected
;
16639 #if( TARGET_OS_WATCH )
16640 if( subtest
->test
->forBATS
&& ( subtest
->ifIndex
== kDNSServiceInterfaceIndexAny
) &&
16641 ifNamePtr
&& _RegistrationTestInterfaceIsWiFi( ifNamePtr
) && !inError
&&
16642 ( strcasecmp( inName
, subtest
->serviceFQDN
) == 0 ) )
16644 if( inType
== kDNSServiceType_SRV
)
16646 const Boolean expectRandHostname
= subtest
->includeAWDL
? true : false;
16648 if( _RegistrationSubtestIsSRVRecordDataValid( subtest
, inRDataPtr
, inRDataLen
, expectRandHostname
) )
16650 resultArray
= subtest
->ignored
;
16653 else if( inType
== kDNSServiceType_TXT
)
16655 if( MemEqual( inRDataPtr
, inRDataLen
, subtest
->txtPtr
, subtest
->txtLen
) )
16657 resultArray
= subtest
->ignored
;
16662 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &resultDict
,
16664 "%kO=%O" // resultType
16665 "%kO=%s" // timestamp
16666 "%kO=%lli" // flags
16667 "%kO=%lli" // ifIndex
16669 "%kO=%lli" // error
16670 "%kO=%s" // serviceFQDN
16671 "%kO=%lli" // recordType
16672 "%kO=%lli" // class
16674 kRegistrationTestReportKey_ResultType
, kRegistrationTestResultType_Query
,
16675 kRegistrationTestReportKey_Timestamp
, _NanoTime64ToTimestamp( now
, timestamp
, sizeof( timestamp
) ),
16676 kRegistrationTestReportKey_Flags
, (int64_t) inFlags
,
16677 kRegistrationTestReportKey_InterfaceIndex
, (int64_t) inIfIndex
,
16678 kRegistrationTestReportKey_InterfaceName
, ifNamePtr
,
16679 kRegistrationTestReportKey_Error
, (int64_t) inError
,
16680 kRegistrationTestReportKey_ServiceFQDN
, inName
,
16681 kRegistrationTestReportKey_RecordType
, (int64_t) inType
,
16682 kRegistrationTestReportKey_RecordClass
, (int64_t) inClass
);
16683 require_noerr( err
, exit
);
16686 DNSRecordDataToString( inRDataPtr
, inRDataLen
, inType
, NULL
, 0, &rdataStr
);
16689 rdataKey
= kRegistrationTestReportKey_RDataFormatted
;
16693 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, inRDataLen
);
16694 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
16696 rdataKey
= kRegistrationTestReportKey_RDataHexString
;
16698 err
= CFDictionarySetCString( resultDict
, rdataKey
, rdataStr
, kSizeCString
);
16699 ForgetMem( &rdataStr
);
16700 if( err
) CFRelease( resultDict
);
16701 require_noerr( err
, exit
);
16703 CFArrayAppendValue( resultArray
, resultDict
);
16704 CFRelease( resultDict
);
16709 if( err
) _RegistrationTestExit( subtest
->test
, err
);
16713 _RegistrationSubtestIsSRVRecordDataValid(
16714 RegistrationSubtest
* inSubtest
,
16715 const uint8_t * inRDataPtr
,
16717 Boolean inExpectRandHostname
)
16719 const dns_fixed_fields_srv
* fields
;
16720 const uint8_t * const end
= &inRDataPtr
[ inRDataLen
];
16721 const uint8_t * label
;
16727 require_quiet( inRDataLen
>= sizeof( dns_fixed_fields_srv
), exit
);
16729 fields
= (const dns_fixed_fields_srv
*) inRDataPtr
;
16730 port
= dns_fixed_fields_srv_get_port( fields
);
16731 require_quiet( port
== inSubtest
->port
, exit
);
16733 // First target label should be a UUID string for the AWDL interface.
16735 label
= (const uint8_t *) &fields
[ 1 ];
16736 require_quiet( ( end
- label
) >= 1, exit
);
16739 require_quiet( ( (size_t)( end
- label
) ) >= ( 1 + len
), exit
);
16741 if( inExpectRandHostname
)
16743 if( StringToUUID( (const char *) &label
[ 1 ], len
, false, NULL
) != kNoErr
) goto exit
;
16747 if( strnicmpx( &label
[ 1 ], len
, inSubtest
->test
->localHostName
) != 0 ) goto exit
;
16750 // Second target label should be "local".
16752 label
= &label
[ 1 + len
];
16753 require_quiet( ( end
- label
) >= 1, exit
);
16756 require_quiet( ( (size_t)( end
- label
) ) >= ( 1 + len
), exit
);
16758 if( ( len
!= kLocalLabel
[ 0 ] ) || ( _memicmp( &label
[ 1 ], &kLocalLabel
[ 1 ], kLocalLabel
[ 0 ] ) != 0 ) ) goto exit
;
16760 // Third target label should be the root label.
16762 label
= &label
[ 1 + len
];
16763 require_quiet( ( end
- label
) >= 1, exit
);
16766 if( len
!= 0 ) goto exit
;
16774 //===========================================================================================================================
16776 static Boolean
_RegistrationSubtestValidServiceType( const RegistrationSubtest
*inSubtest
, const char *inServiceType
)
16778 if( stricmp_prefix( inServiceType
, inSubtest
->serviceType
) == 0 )
16780 const char * const ptr
= &inServiceType
[ inSubtest
->serviceTypeLen
];
16782 if( ( ptr
[ 0 ] == '\0' ) || ( ( ptr
[ 0 ] == '.' ) && ( ptr
[ 1 ] == '\0' ) ) ) return( true );
16787 //===========================================================================================================================
16789 static RegistrationResultTimes
*
16790 _RegistrationSubtestGetInterfaceResultTimes(
16791 RegistrationSubtest
* inSubtest
,
16792 uint32_t inIfIndex
,
16793 Boolean
* outIsAWDL
)
16795 if( inSubtest
->ifList
)
16797 RegistrationInterfaceItem
* item
;
16799 for( item
= inSubtest
->ifList
; item
; item
= (RegistrationInterfaceItem
*) item
->base
.next
)
16801 if( inIfIndex
== item
->base
.ifIndex
)
16803 if( outIsAWDL
) *outIsAWDL
= item
->base
.isAWDL
? true : false;
16804 return( &item
->times
);
16810 if( inIfIndex
== inSubtest
->ifIndex
)
16812 if( outIsAWDL
) *outIsAWDL
= inSubtest
->ifIsAWDL
? true : false;
16813 return( &inSubtest
->ifTimes
);
16819 //===========================================================================================================================
16821 static void _RegistrationTestTimerHandler( void *inContext
)
16823 RegistrationTest
* const test
= (RegistrationTest
*) inContext
;
16825 dispatch_source_forget( &test
->timer
);
16826 _RegistrationTestProceed( test
);
16829 //===========================================================================================================================
16831 #if( TARGET_OS_WATCH )
16832 static Boolean
_RegistrationTestInterfaceIsWiFi( const char *inIfName
)
16834 NetTransportType type
= kNetTransportType_Undefined
;
16836 SocketGetInterfaceInfo( kInvalidSocketRef
, inIfName
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &type
);
16837 return( ( type
== kNetTransportType_WiFi
) ? true : false );
16841 //===========================================================================================================================
16843 //===========================================================================================================================
16845 #define kSSDPPort 1900
16849 HTTPHeader header
; // HTTP header object for sending and receiving.
16850 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
16851 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
16852 int receiveSecs
; // After send, the amount of time to spend receiving.
16853 uint32_t ifindex
; // Index of the interface over which to send the query.
16854 Boolean useIPv4
; // True if the query should be sent via IPv4 multicast.
16855 Boolean useIPv6
; // True if the query should be sent via IPv6 multicast.
16857 } SSDPDiscoverContext
;
16859 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext
*inContext
);
16860 static void SSDPDiscoverReadHandler( void *inContext
);
16861 static int SocketToPortNumber( SocketRef inSock
);
16862 static OSStatus
WriteSSDPSearchRequest( HTTPHeader
*inHeader
, const void *inHostSA
, int inMX
, const char *inST
);
16864 static void SSDPDiscoverCmd( void )
16867 struct timeval now
;
16868 SSDPDiscoverContext
* context
;
16869 dispatch_source_t signalSource
= NULL
;
16870 SocketRef sockV4
= kInvalidSocketRef
;
16871 SocketRef sockV6
= kInvalidSocketRef
;
16875 // Set up SIGINT handler.
16877 signal( SIGINT
, SIG_IGN
);
16878 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
16879 require_noerr( err
, exit
);
16880 dispatch_resume( signalSource
);
16882 // Check command parameters.
16884 if( gSSDPDiscover_ReceiveSecs
< -1 )
16886 FPrintF( stdout
, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs
);
16893 context
= (SSDPDiscoverContext
*) calloc( 1, sizeof( *context
) );
16894 require_action( context
, exit
, err
= kNoMemoryErr
);
16896 context
->receiveSecs
= gSSDPDiscover_ReceiveSecs
;
16897 context
->useIPv4
= ( gSSDPDiscover_UseIPv4
|| !gSSDPDiscover_UseIPv6
) ? true : false;
16898 context
->useIPv6
= ( gSSDPDiscover_UseIPv6
|| !gSSDPDiscover_UseIPv4
) ? true : false;
16900 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifindex
);
16901 require_noerr_quiet( err
, exit
);
16903 // Set up IPv4 socket.
16905 if( context
->useIPv4
)
16908 err
= UDPClientSocketOpen( AF_INET
, NULL
, 0, -1, &port
, &sockV4
);
16909 require_noerr( err
, exit
);
16911 err
= SocketSetMulticastInterface( sockV4
, NULL
, context
->ifindex
);
16912 require_noerr( err
, exit
);
16914 err
= setsockopt( sockV4
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *) &(uint8_t){ 1 }, (socklen_t
) sizeof( uint8_t ) );
16915 err
= map_socket_noerr_errno( sockV4
, err
);
16916 require_noerr( err
, exit
);
16919 // Set up IPv6 socket.
16921 if( context
->useIPv6
)
16923 err
= UDPClientSocketOpen( AF_INET6
, NULL
, 0, -1, NULL
, &sockV6
);
16924 require_noerr( err
, exit
);
16926 err
= SocketSetMulticastInterface( sockV6
, NULL
, context
->ifindex
);
16927 require_noerr( err
, exit
);
16929 err
= setsockopt( sockV6
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *) &(int){ 1 }, (socklen_t
) sizeof( int ) );
16930 err
= map_socket_noerr_errno( sockV6
, err
);
16931 require_noerr( err
, exit
);
16936 SSDPDiscoverPrintPrologue( context
);
16938 // Send mDNS query message.
16941 if( IsValidSocket( sockV4
) )
16943 struct sockaddr_in mcastAddr4
;
16945 memset( &mcastAddr4
, 0, sizeof( mcastAddr4
) );
16946 SIN_LEN_SET( &mcastAddr4
);
16947 mcastAddr4
.sin_family
= AF_INET
;
16948 mcastAddr4
.sin_port
= htons( kSSDPPort
);
16949 mcastAddr4
.sin_addr
.s_addr
= htonl( 0xEFFFFFFA ); // 239.255.255.250
16951 err
= WriteSSDPSearchRequest( &context
->header
, &mcastAddr4
, gSSDPDiscover_MX
, gSSDPDiscover_ST
);
16952 require_noerr( err
, exit
);
16954 n
= sendto( sockV4
, context
->header
.buf
, context
->header
.len
, 0, (const struct sockaddr
*) &mcastAddr4
,
16955 (socklen_t
) sizeof( mcastAddr4
) );
16956 err
= map_socket_value_errno( sockV4
, n
== (ssize_t
) context
->header
.len
, n
);
16959 FPrintF( stderr
, "*** Failed to send query on IPv4 socket with error %#m\n", err
);
16960 ForgetSocket( &sockV4
);
16964 if( gSSDPDiscover_Verbose
)
16966 gettimeofday( &now
, NULL
);
16967 FPrintF( stdout
, "---\n" );
16968 FPrintF( stdout
, "Send time: %{du:time}\n", &now
);
16969 FPrintF( stdout
, "Source Port: %d\n", SocketToPortNumber( sockV4
) );
16970 FPrintF( stdout
, "Destination: %##a\n", &mcastAddr4
);
16971 FPrintF( stdout
, "Message size: %zu\n", context
->header
.len
);
16972 FPrintF( stdout
, "HTTP header:\n%1{text}", context
->header
.buf
, context
->header
.len
);
16978 if( IsValidSocket( sockV6
) )
16980 struct sockaddr_in6 mcastAddr6
;
16982 memset( &mcastAddr6
, 0, sizeof( mcastAddr6
) );
16983 SIN6_LEN_SET( &mcastAddr6
);
16984 mcastAddr6
.sin6_family
= AF_INET6
;
16985 mcastAddr6
.sin6_port
= htons( kSSDPPort
);
16986 mcastAddr6
.sin6_addr
.s6_addr
[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C
16987 mcastAddr6
.sin6_addr
.s6_addr
[ 1 ] = 0x02;
16988 mcastAddr6
.sin6_addr
.s6_addr
[ 15 ] = 0x0C;
16990 err
= WriteSSDPSearchRequest( &context
->header
, &mcastAddr6
, gSSDPDiscover_MX
, gSSDPDiscover_ST
);
16991 require_noerr( err
, exit
);
16993 n
= sendto( sockV6
, context
->header
.buf
, context
->header
.len
, 0, (const struct sockaddr
*) &mcastAddr6
,
16994 (socklen_t
) sizeof( mcastAddr6
) );
16995 err
= map_socket_value_errno( sockV6
, n
== (ssize_t
) context
->header
.len
, n
);
16998 FPrintF( stderr
, "*** Failed to send query on IPv6 socket with error %#m\n", err
);
16999 ForgetSocket( &sockV6
);
17003 if( gSSDPDiscover_Verbose
)
17005 gettimeofday( &now
, NULL
);
17006 FPrintF( stdout
, "---\n" );
17007 FPrintF( stdout
, "Send time: %{du:time}\n", &now
);
17008 FPrintF( stdout
, "Source Port: %d\n", SocketToPortNumber( sockV6
) );
17009 FPrintF( stdout
, "Destination: %##a\n", &mcastAddr6
);
17010 FPrintF( stdout
, "Message size: %zu\n", context
->header
.len
);
17011 FPrintF( stdout
, "HTTP header:\n%1{text}", context
->header
.buf
, context
->header
.len
);
17016 require_action_quiet( sendCount
> 0, exit
, err
= kUnexpectedErr
);
17018 // If there's no wait period after the send, then exit.
17020 if( context
->receiveSecs
== 0 ) goto exit
;
17022 // Create dispatch read sources for socket(s).
17024 if( IsValidSocket( sockV4
) )
17026 SocketContext
* sockCtx
;
17028 err
= SocketContextCreate( sockV4
, context
, &sockCtx
);
17029 require_noerr( err
, exit
);
17030 sockV4
= kInvalidSocketRef
;
17032 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, SSDPDiscoverReadHandler
, SocketContextCancelHandler
, sockCtx
,
17033 &context
->readSourceV4
);
17034 if( err
) ForgetSocketContext( &sockCtx
);
17035 require_noerr( err
, exit
);
17037 dispatch_resume( context
->readSourceV4
);
17040 if( IsValidSocket( sockV6
) )
17042 SocketContext
* sockCtx
;
17044 err
= SocketContextCreate( sockV6
, context
, &sockCtx
);
17045 require_noerr( err
, exit
);
17046 sockV6
= kInvalidSocketRef
;
17048 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, SSDPDiscoverReadHandler
, SocketContextCancelHandler
, sockCtx
,
17049 &context
->readSourceV6
);
17050 if( err
) ForgetSocketContext( &sockCtx
);
17051 require_noerr( err
, exit
);
17053 dispatch_resume( context
->readSourceV6
);
17056 if( context
->receiveSecs
> 0 )
17058 dispatch_after_f( dispatch_time_seconds( context
->receiveSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
17064 ForgetSocket( &sockV4
);
17065 ForgetSocket( &sockV6
);
17066 dispatch_source_forget( &signalSource
);
17067 exit( err
? 1 : 0 );
17070 static int SocketToPortNumber( SocketRef inSock
)
17076 len
= (socklen_t
) sizeof( sip
);
17077 err
= getsockname( inSock
, &sip
.sa
, &len
);
17078 err
= map_socket_noerr_errno( inSock
, err
);
17079 check_noerr( err
);
17080 return( err
? -1 : SockAddrGetPort( &sip
) );
17083 static OSStatus
WriteSSDPSearchRequest( HTTPHeader
*inHeader
, const void *inHostSA
, int inMX
, const char *inST
)
17087 err
= HTTPHeader_InitRequest( inHeader
, "M-SEARCH", "*", "HTTP/1.1" );
17088 require_noerr( err
, exit
);
17090 err
= HTTPHeader_SetField( inHeader
, "Host", "%##a", inHostSA
);
17091 require_noerr( err
, exit
);
17093 err
= HTTPHeader_SetField( inHeader
, "ST", "%s", inST
? inST
: "ssdp:all" );
17094 require_noerr( err
, exit
);
17096 err
= HTTPHeader_SetField( inHeader
, "Man", "\"ssdp:discover\"" );
17097 require_noerr( err
, exit
);
17099 err
= HTTPHeader_SetField( inHeader
, "MX", "%d", inMX
);
17100 require_noerr( err
, exit
);
17102 err
= HTTPHeader_Commit( inHeader
);
17103 require_noerr( err
, exit
);
17109 //===========================================================================================================================
17110 // SSDPDiscoverPrintPrologue
17111 //===========================================================================================================================
17113 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext
*inContext
)
17115 const int receiveSecs
= inContext
->receiveSecs
;
17116 const char * ifName
;
17117 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
17118 NetTransportType ifType
;
17120 ifName
= if_indextoname( inContext
->ifindex
, ifNameBuf
);
17122 ifType
= kNetTransportType_Undefined
;
17123 if( ifName
) SocketGetInterfaceInfo( kInvalidSocketRef
, ifName
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &ifType
);
17125 FPrintF( stdout
, "Interface: %s/%d/%s\n",
17126 ifName
? ifName
: "?", inContext
->ifindex
, NetTransportTypeToString( ifType
) );
17127 FPrintF( stdout
, "IP protocols: %?s%?s%?s\n",
17128 inContext
->useIPv4
, "IPv4", ( inContext
->useIPv4
&& inContext
->useIPv6
), ", ", inContext
->useIPv6
, "IPv6" );
17129 FPrintF( stdout
, "Receive duration: " );
17130 if( receiveSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", receiveSecs
, receiveSecs
!= 1, 's' );
17131 else FPrintF( stdout
, "∞\n" );
17132 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
17135 //===========================================================================================================================
17136 // SSDPDiscoverReadHandler
17137 //===========================================================================================================================
17139 static Boolean
_HTTPHeader_Validate( HTTPHeader
*inHeader
);
17141 static void SSDPDiscoverReadHandler( void *inContext
)
17144 struct timeval now
;
17145 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
17146 SSDPDiscoverContext
* const context
= (SSDPDiscoverContext
*) sockCtx
->userContext
;
17147 HTTPHeader
* const header
= &context
->header
;
17148 sockaddr_ip fromAddr
;
17151 gettimeofday( &now
, NULL
);
17153 err
= SocketRecvFrom( sockCtx
->sock
, header
->buf
, sizeof( header
->buf
), &msgLen
, &fromAddr
, sizeof( fromAddr
),
17154 NULL
, NULL
, NULL
, NULL
);
17155 require_noerr( err
, exit
);
17157 FPrintF( stdout
, "---\n" );
17158 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
17159 FPrintF( stdout
, "Source: %##a\n", &fromAddr
);
17160 FPrintF( stdout
, "Message size: %zu\n", msgLen
);
17161 header
->len
= msgLen
;
17162 if( _HTTPHeader_Validate( header
) )
17164 FPrintF( stdout
, "HTTP header:\n%1{text}", header
->buf
, header
->len
);
17165 if( header
->extraDataLen
> 0 )
17167 FPrintF( stdout
, "HTTP body: %1.1H", header
->extraDataPtr
, (int) header
->extraDataLen
, INT_MAX
);
17172 FPrintF( stdout
, "Invalid HTTP message:\n%1.1H", header
->buf
, (int) msgLen
, INT_MAX
);
17177 if( err
) exit( 1 );
17180 //===========================================================================================================================
17181 // _HTTPHeader_Validate
17183 // Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid.
17184 // This assumes the "buf" and "len" fields are set. The other fields are set by this function.
17186 // Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework.
17187 //===========================================================================================================================
17189 static Boolean
_HTTPHeader_Validate( HTTPHeader
*inHeader
)
17194 // Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
17196 require( inHeader
->len
< sizeof( inHeader
->buf
), exit
);
17197 src
= inHeader
->buf
;
17198 end
= src
+ inHeader
->len
;
17199 if( ( ( end
- src
) >= 4 ) && ( src
[ 0 ] == '$' ) )
17205 // Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted.
17206 // $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over.
17210 while( ( src
< end
) && ( src
[ 0 ] != '\n' ) ) ++src
;
17211 if( src
>= end
) goto exit
;
17213 if( ( ( end
- src
) >= 2 ) && ( src
[ 0 ] == '\r' ) && ( src
[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF
17218 else if( ( ( end
- src
) >= 1 ) && ( src
[ 0 ] == '\n' ) ) // LFLF
17225 inHeader
->extraDataPtr
= src
;
17226 inHeader
->extraDataLen
= (size_t)( end
- src
);
17227 inHeader
->len
= (size_t)( src
- inHeader
->buf
);
17234 #if( TARGET_OS_DARWIN )
17235 //===========================================================================================================================
17237 //===========================================================================================================================
17239 // res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h).
17241 SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv
);
17242 SOFT_LINK_FUNCTION_EX( resolv
, res_9_query
,
17244 ( const char *dname
, int class, int type
, u_char
*answer
, int anslen
),
17245 ( dname
, class, type
, answer
, anslen
) );
17247 // res_query() from libinfo
17249 SOFT_LINK_LIBRARY_EX( "/usr/lib", info
);
17250 SOFT_LINK_FUNCTION_EX( info
, res_query
,
17252 ( const char *dname
, int class, int type
, u_char
*answer
, int anslen
),
17253 ( dname
, class, type
, answer
, anslen
) );
17255 typedef int ( *res_query_f
)( const char *dname
, int class, int type
, u_char
*answer
, int anslen
);
17257 static void ResQueryCmd( void )
17260 res_query_f res_query_ptr
;
17262 uint16_t type
, class;
17263 uint8_t answer
[ 1024 ];
17265 // Get pointer to one of the res_query() functions.
17267 if( gResQuery_UseLibInfo
)
17269 if( !SOFT_LINK_HAS_FUNCTION( info
, res_query
) )
17271 FPrintF( stderr
, "Failed to soft link res_query from libinfo.\n" );
17272 err
= kNotFoundErr
;
17275 res_query_ptr
= soft_res_query
;
17279 if( !SOFT_LINK_HAS_FUNCTION( resolv
, res_9_query
) )
17281 FPrintF( stderr
, "Failed to soft link res_query from libresolv.\n" );
17282 err
= kNotFoundErr
;
17285 res_query_ptr
= soft_res_9_query
;
17288 // Get record type.
17290 err
= RecordTypeFromArgString( gResQuery_Type
, &type
);
17291 require_noerr( err
, exit
);
17293 // Get record class.
17295 if( gResQuery_Class
)
17297 err
= RecordClassFromArgString( gResQuery_Class
, &class );
17298 require_noerr( err
, exit
);
17302 class = kDNSServiceClass_IN
;
17307 FPrintF( stdout
, "Name: %s\n", gResQuery_Name
);
17308 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
17309 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
17310 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
17311 FPrintF( stdout
, "---\n" );
17313 // Call res_query().
17315 n
= res_query_ptr( gResQuery_Name
, class, type
, (u_char
*) answer
, (int) sizeof( answer
) );
17318 FPrintF( stderr
, "res_query() failed with error: %d (%s).\n", h_errno
, hstrerror( h_errno
) );
17325 FPrintF( stdout
, "Message size: %d\n\n%{du:dnsmsg}", n
, answer
, (size_t) n
);
17328 if( err
) exit( 1 );
17331 //===========================================================================================================================
17332 // ResolvDNSQueryCmd
17333 //===========================================================================================================================
17335 // 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
17336 // avoid including the header file.
17338 typedef void * dns_handle_t
;
17340 SOFT_LINK_FUNCTION_EX( resolv
, dns_open
, dns_handle_t
, ( const char *path
), ( path
) );
17341 SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv
, dns_free
, ( dns_handle_t
*dns
), ( dns
) );
17342 SOFT_LINK_FUNCTION_EX( resolv
, dns_query
,
17350 struct sockaddr
* from
,
17351 uint32_t * fromlen
),
17352 ( dns
, name
, dnsclass
, dnstype
, buf
, len
, from
, fromlen
) );
17354 static void ResolvDNSQueryCmd( void )
17358 dns_handle_t dns
= NULL
;
17359 uint16_t type
, class;
17362 uint8_t answer
[ 1024 ];
17364 // Make sure that the required symbols are available.
17366 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_open
) )
17368 FPrintF( stderr
, "Failed to soft link dns_open from libresolv.\n" );
17369 err
= kNotFoundErr
;
17373 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_free
) )
17375 FPrintF( stderr
, "Failed to soft link dns_free from libresolv.\n" );
17376 err
= kNotFoundErr
;
17380 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_query
) )
17382 FPrintF( stderr
, "Failed to soft link dns_query from libresolv.\n" );
17383 err
= kNotFoundErr
;
17387 // Get record type.
17389 err
= RecordTypeFromArgString( gResolvDNSQuery_Type
, &type
);
17390 require_noerr( err
, exit
);
17392 // Get record class.
17394 if( gResolvDNSQuery_Class
)
17396 err
= RecordClassFromArgString( gResolvDNSQuery_Class
, &class );
17397 require_noerr( err
, exit
);
17401 class = kDNSServiceClass_IN
;
17406 dns
= soft_dns_open( gResolvDNSQuery_Path
);
17409 FPrintF( stderr
, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path
);
17416 FPrintF( stdout
, "Name: %s\n", gResolvDNSQuery_Name
);
17417 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
17418 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
17419 FPrintF( stdout
, "Path: %s\n", gResolvDNSQuery_Path
? gResolvDNSQuery_Name
: "<NULL>" );
17420 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
17421 FPrintF( stdout
, "---\n" );
17423 // Call dns_query().
17425 memset( &from
, 0, sizeof( from
) );
17426 fromLen
= (uint32_t) sizeof( from
);
17427 n
= soft_dns_query( dns
, gResolvDNSQuery_Name
, class, type
, (char *) answer
, (uint32_t) sizeof( answer
), &from
.sa
,
17431 FPrintF( stderr
, "dns_query() failed with error: %d (%s).\n", h_errno
, hstrerror( h_errno
) );
17438 FPrintF( stdout
, "From: %##a\n", &from
);
17439 FPrintF( stdout
, "Message size: %d\n\n%{du:dnsmsg}", n
, answer
, (size_t) n
);
17442 if( dns
) soft_dns_free( dns
);
17443 if( err
) exit( 1 );
17446 //===========================================================================================================================
17448 //===========================================================================================================================
17451 _CFHostResolveCallback(
17453 CFHostInfoType inInfoType
,
17454 const CFStreamError
* inError
,
17457 static void CFHostCmd( void )
17462 CFHostRef host
= NULL
;
17463 CFHostClientContext context
;
17464 CFStreamError streamErr
;
17466 name
= CFStringCreateWithCString( kCFAllocatorDefault
, gCFHost_Name
, kCFStringEncodingUTF8
);
17467 require_action( name
, exit
, err
= kUnknownErr
);
17469 host
= CFHostCreateWithName( kCFAllocatorDefault
, name
);
17471 require_action( host
, exit
, err
= kUnknownErr
);
17473 memset( &context
, 0, sizeof( context
) );
17474 success
= CFHostSetClient( host
, _CFHostResolveCallback
, &context
);
17475 require_action( success
, exit
, err
= kUnknownErr
);
17477 CFHostScheduleWithRunLoop( host
, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode
);
17481 FPrintF( stdout
, "Hostname: %s\n", gCFHost_Name
);
17482 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
17483 FPrintF( stdout
, "---\n" );
17485 success
= CFHostStartInfoResolution( host
, kCFHostAddresses
, &streamErr
);
17486 require_action( success
, exit
, err
= kUnknownErr
);
17492 CFReleaseNullSafe( host
);
17493 if( err
) exit( 1 );
17496 static void _CFHostResolveCallback( CFHostRef inHost
, CFHostInfoType inInfoType
, const CFStreamError
*inError
, void *inInfo
)
17499 struct timeval now
;
17501 gettimeofday( &now
, NULL
);
17503 Unused( inInfoType
);
17506 if( inError
&& ( inError
->domain
!= 0 ) && ( inError
->error
) )
17508 err
= inError
->error
;
17509 if( inError
->domain
== kCFStreamErrorDomainNetDB
)
17511 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
17515 FPrintF( stderr
, "Error %#m\n", err
);
17520 CFArrayRef addresses
;
17522 CFDataRef addrData
;
17523 const struct sockaddr
* sockAddr
;
17524 Boolean wasResolved
= false;
17526 addresses
= CFHostGetAddressing( inHost
, &wasResolved
);
17527 check( wasResolved
);
17531 count
= CFArrayGetCount( addresses
);
17532 for( i
= 0; i
< count
; ++i
)
17534 addrData
= CFArrayGetCFDataAtIndex( addresses
, i
, &err
);
17535 require_noerr( err
, exit
);
17537 sockAddr
= (const struct sockaddr
*) CFDataGetBytePtr( addrData
);
17538 FPrintF( stdout
, "%##a\n", sockAddr
);
17544 FPrintF( stdout
, "---\n" );
17545 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
17547 if( gCFHost_WaitSecs
> 0 ) sleep( (unsigned int) gCFHost_WaitSecs
);
17550 exit( err
? 1 : 0 );
17553 //===========================================================================================================================
17556 // Note: Based on ajn's supplemental test tool.
17557 //===========================================================================================================================
17559 static void DNSConfigAddCmd( void )
17562 CFMutableDictionaryRef dict
= NULL
;
17563 CFMutableArrayRef array
= NULL
;
17565 SCDynamicStoreRef store
= NULL
;
17566 CFStringRef key
= NULL
;
17569 // Create dictionary.
17571 dict
= CFDictionaryCreateMutable( NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
17572 require_action( dict
, exit
, err
= kNoMemoryErr
);
17574 // Add DNS server IP addresses.
17576 array
= CFArrayCreateMutable( NULL
, (CFIndex
) gDNSConfigAdd_IPAddrCount
, &kCFTypeArrayCallBacks
);
17577 require_action( array
, exit
, err
= kNoMemoryErr
);
17579 for( i
= 0; i
< gDNSConfigAdd_IPAddrCount
; ++i
)
17581 CFStringRef addrStr
;
17583 addrStr
= CFStringCreateWithCString( NULL
, gDNSConfigAdd_IPAddrArray
[ i
], kCFStringEncodingUTF8
);
17584 require_action( addrStr
, exit
, err
= kUnknownErr
);
17586 CFArrayAppendValue( array
, addrStr
);
17587 CFRelease( addrStr
);
17590 CFDictionarySetValue( dict
, kSCPropNetDNSServerAddresses
, array
);
17591 ForgetCF( &array
);
17593 // Add domains, if any.
17595 array
= CFArrayCreateMutable( NULL
, (CFIndex
) Min( gDNSConfigAdd_DomainCount
, 1 ), &kCFTypeArrayCallBacks
);
17596 require_action( array
, exit
, err
= kNoMemoryErr
);
17598 if( gDNSConfigAdd_DomainCount
> 0 )
17600 for( i
= 0; i
< gDNSConfigAdd_DomainCount
; ++i
)
17602 CFStringRef domainStr
;
17604 domainStr
= CFStringCreateWithCString( NULL
, gDNSConfigAdd_DomainArray
[ i
], kCFStringEncodingUTF8
);
17605 require_action( domainStr
, exit
, err
= kUnknownErr
);
17607 CFArrayAppendValue( array
, domainStr
);
17608 CFRelease( domainStr
);
17613 // There are no domains, but the domain array needs to be non-empty, so add a zero-length string to the array.
17615 CFArrayAppendValue( array
, CFSTR( "" ) );
17618 CFDictionarySetValue( dict
, kSCPropNetDNSSupplementalMatchDomains
, array
);
17619 ForgetCF( &array
);
17621 // Add interface, if any.
17623 if( gDNSConfigAdd_Interface
)
17625 err
= CFDictionarySetCString( dict
, kSCPropInterfaceName
, gDNSConfigAdd_Interface
, kSizeCString
);
17626 require_noerr( err
, exit
);
17628 CFDictionarySetValue( dict
, kSCPropNetDNSConfirmedServiceID
, gDNSConfigAdd_ID
);
17631 // Set dictionary in dynamic store.
17633 store
= SCDynamicStoreCreate( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
17634 err
= map_scerror( store
);
17635 require_noerr( err
, exit
);
17637 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
, gDNSConfigAdd_ID
, kSCEntNetDNS
);
17638 require_action( key
, exit
, err
= kUnknownErr
);
17640 success
= SCDynamicStoreSetValue( store
, key
, dict
);
17641 require_action( success
, exit
, err
= kUnknownErr
);
17644 CFReleaseNullSafe( dict
);
17645 CFReleaseNullSafe( array
);
17646 CFReleaseNullSafe( store
);
17647 CFReleaseNullSafe( key
);
17648 gExitCode
= err
? 1 : 0;
17651 //===========================================================================================================================
17652 // DNSConfigRemoveCmd
17653 //===========================================================================================================================
17655 static void DNSConfigRemoveCmd( void )
17658 SCDynamicStoreRef store
= NULL
;
17659 CFStringRef key
= NULL
;
17662 store
= SCDynamicStoreCreate( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
17663 err
= map_scerror( store
);
17664 require_noerr( err
, exit
);
17666 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
, gDNSConfigRemove_ID
, kSCEntNetDNS
);
17667 require_action( key
, exit
, err
= kUnknownErr
);
17669 success
= SCDynamicStoreRemoveValue( store
, key
);
17670 require_action( success
, exit
, err
= kUnknownErr
);
17673 CFReleaseNullSafe( store
);
17674 CFReleaseNullSafe( key
);
17675 gExitCode
= err
? 1 : 0;
17678 //===========================================================================================================================
17680 //===========================================================================================================================
17682 static OSStatus
_XPCDictionaryCreateFromString( const char *inString
, xpc_object_t
*outDict
);
17684 static void XPCSendCmd( void )
17687 xpc_object_t msg
, reply
;
17689 err
= _XPCDictionaryCreateFromString( gXPCSend_MessageStr
, &msg
);
17690 require_noerr_quiet( err
, exit
);
17692 FPrintF( stdout
, "Service: %s\n", gXPCSend_ServiceName
);
17693 FPrintF( stdout
, "Message: %s\n", gXPCSend_MessageStr
);
17694 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
17695 FPrintF( stdout
, "---\n" );
17696 FPrintF( stdout
, "XPC Message:\n%{xpc}\n", msg
);
17698 err
= xpc_send_message_sync( gXPCSend_ServiceName
, 0, 0, msg
, &reply
);
17699 xpc_forget( &msg
);
17700 require_noerr_quiet( err
, exit
);
17702 FPrintF( stdout
, "XPC Reply:\n%{xpc}\n", reply
);
17703 FPrintF( stdout
, "---\n" );
17704 FPrintF( stdout
, "End time: %{du:time}\n", NULL
);
17705 xpc_forget( &reply
);
17708 if( err
) ErrQuit( 1, "error: %#m\n", err
);
17711 //===========================================================================================================================
17712 // _XPCDictionaryCreateFromString
17713 //===========================================================================================================================
17715 #define kXPCObjectPrefix_Bool "bool:"
17716 #define kXPCObjectPrefix_Data "data:"
17717 #define kXPCObjectPrefix_Int64 "int:"
17718 #define kXPCObjectPrefix_String "string:"
17719 #define kXPCObjectPrefix_UInt64 "uint:"
17720 #define kXPCObjectPrefix_UUID "uuid:"
17722 typedef struct XPCListItem XPCListItem
;
17725 XPCListItem
* next
;
17730 static OSStatus
_XPCListItemCreate( xpc_object_t inObject
, const char *inKey
, XPCListItem
**outItem
);
17731 static void _XPCListItemFree( XPCListItem
*inItem
);
17732 static void _XPCListFree( XPCListItem
*inList
);
17734 static OSStatus
_XPCObjectFromString( const char *inString
, xpc_object_t
*outObject
);
17736 static OSStatus
_XPCDictionaryCreateFromString( const char *inString
, xpc_object_t
*outDict
)
17739 xpc_object_t container
;
17740 const char * ptr
= inString
;
17741 const char * const end
= inString
+ strlen( inString
);
17742 XPCListItem
* list
= NULL
;
17744 container
= xpc_dictionary_create( NULL
, NULL
, 0 );
17745 require_action( container
, exit
, err
= kNoMemoryErr
);
17749 xpc_type_t containerType
;
17750 xpc_object_t value
;
17752 char keyStr
[ 256 ];
17753 char valStr
[ 256 ];
17755 // At this point, zero or more of the current container's elements have been parsed.
17756 // Skip the white space leading up to the container's next element, if any, or the container's end.
17758 while( isspace_safe( *ptr
) ) ++ptr
;
17760 // Check if we're done with the current container.
17763 if( c
== '\0' ) break;
17765 containerType
= xpc_get_type( container
);
17766 if( ( ( containerType
== XPC_TYPE_DICTIONARY
) && ( c
== '}' ) ) ||
17767 ( ( containerType
== XPC_TYPE_ARRAY
) && ( c
== ']' ) ) )
17769 XPCListItem
* item
;
17772 require_action_quiet( item
, exit
, err
= kMalformedErr
);
17776 // Add the current container to its parent container.
17780 xpc_dictionary_set_value( item
->obj
, item
->key
, container
);
17784 xpc_array_append_value( item
->obj
, container
);
17787 // Continue with the parent container.
17789 xpc_release( container
);
17790 container
= xpc_retain( item
->obj
);
17792 _XPCListItemFree( item
);
17796 // If the current container is a dictionary, parse the key string.
17798 if( containerType
== XPC_TYPE_DICTIONARY
)
17800 err
= _ParseEscapedString( ptr
, end
, "={}[]" kWhiteSpaceCharSet
, keyStr
, sizeof( keyStr
), NULL
, NULL
, &ptr
);
17801 require_noerr_quiet( err
, exit
);
17804 require_action_quiet( c
== '=', exit
, err
= kMalformedErr
);
17808 // Check if the value is a dictionary ({...}) or an array ([...]).
17811 if( ( c
== '{' ) || ( c
== '[' ) )
17813 XPCListItem
* item
;
17817 // Save the current container.
17819 err
= _XPCListItemCreate( container
, ( containerType
== XPC_TYPE_DICTIONARY
) ? keyStr
: NULL
, &item
);
17820 require_noerr( err
, exit
);
17826 // Create and continue with the child container.
17828 xpc_release( container
);
17831 container
= xpc_dictionary_create( NULL
, NULL
, 0 );
17832 require_action( container
, exit
, err
= kNoMemoryErr
);
17836 container
= xpc_array_create( NULL
, 0 );
17837 require_action( container
, exit
, err
= kNoMemoryErr
);
17842 // Parse the value string.
17844 err
= _ParseEscapedString( ptr
, end
, "{}[]" kWhiteSpaceCharSet
, valStr
, sizeof( valStr
), NULL
, NULL
, &ptr
);
17845 require_noerr_quiet( err
, exit
);
17847 err
= _XPCObjectFromString( valStr
, &value
);
17848 require_noerr_quiet( err
, exit
);
17850 if( containerType
== XPC_TYPE_DICTIONARY
)
17852 xpc_dictionary_set_value( container
, keyStr
, value
);
17856 xpc_array_append_value( container
, value
);
17858 xpc_forget( &value
);
17860 require_action_quiet( !list
, exit
, err
= kMalformedErr
);
17862 check( container
);
17863 check( xpc_get_type( container
) == XPC_TYPE_DICTIONARY
);
17865 *outDict
= container
;
17870 xpc_release_null_safe( container
);
17871 if( list
) _XPCListFree( list
);
17875 static OSStatus
_XPCObjectFromString( const char *inString
, xpc_object_t
*outObject
)
17878 xpc_object_t object
;
17884 else if( stricmp_prefix( inString
, kXPCObjectPrefix_Bool
) == 0 )
17886 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_Bool
);
17889 if( IsTrueString( str
, kSizeCString
) )
17893 else if( IsFalseString( str
, kSizeCString
) )
17903 object
= xpc_bool_create( value
);
17904 require_action( object
, exit
, err
= kNoMemoryErr
);
17909 else if( stricmp_prefix( inString
, kXPCObjectPrefix_Data
) == 0 )
17911 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_Data
);
17915 err
= HexToDataCopy( str
, kSizeCString
, kHexToData_DefaultFlags
, &dataPtr
, &dataLen
, NULL
);
17916 require_noerr( err
, exit
);
17918 object
= xpc_data_create( dataPtr
, dataLen
);
17920 require_action( object
, exit
, err
= kNoMemoryErr
);
17925 else if( stricmp_prefix( inString
, kXPCObjectPrefix_Int64
) == 0 )
17927 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_Int64
);
17930 i64
= _StringToInt64( str
, &err
);
17931 require_noerr_quiet( err
, exit
);
17933 object
= xpc_int64_create( i64
);
17934 require_action( object
, exit
, err
= kNoMemoryErr
);
17939 else if( stricmp_prefix( inString
, kXPCObjectPrefix_String
) == 0 )
17941 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_String
);
17943 object
= xpc_string_create( str
);
17944 require_action( object
, exit
, err
= kNoMemoryErr
);
17949 else if( stricmp_prefix( inString
, kXPCObjectPrefix_UInt64
) == 0 )
17951 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_UInt64
);
17954 u64
= _StringToUInt64( str
, &err
);
17955 require_noerr_quiet( err
, exit
);
17957 object
= xpc_uint64_create( u64
);
17958 require_action( object
, exit
, err
= kNoMemoryErr
);
17963 else if( stricmp_prefix( inString
, kXPCObjectPrefix_UUID
) == 0 )
17965 const char * const str
= inString
+ sizeof_string( kXPCObjectPrefix_UUID
);
17968 err
= uuid_parse( str
, uuid
);
17969 require_noerr_action_quiet( err
, exit
, err
= kValueErr
);
17971 object
= xpc_uuid_create( uuid
);
17972 require_action( object
, exit
, err
= kNoMemoryErr
);
17975 // Unsupported prefix
17983 *outObject
= object
;
17990 static OSStatus
_XPCListItemCreate( xpc_object_t inObject
, const char *inKey
, XPCListItem
**outItem
)
17993 XPCListItem
* item
;
17995 item
= (XPCListItem
*) calloc( 1, sizeof( *item
) );
17996 require_action( item
, exit
, err
= kNoMemoryErr
);
17998 item
->obj
= xpc_retain( inObject
);
17999 if( ( xpc_get_type( item
->obj
) == XPC_TYPE_DICTIONARY
) && inKey
)
18001 item
->key
= strdup( inKey
);
18002 require_action( item
->key
, exit
, err
= kNoMemoryErr
);
18010 if( item
) _XPCListItemFree( item
);
18014 static void _XPCListItemFree( XPCListItem
*inItem
)
18016 xpc_forget( &inItem
->obj
);
18017 ForgetMem( &inItem
->key
);
18021 static void _XPCListFree( XPCListItem
*inList
)
18023 XPCListItem
* item
;
18025 while( ( item
= inList
) != NULL
)
18027 inList
= item
->next
;
18028 _XPCListItemFree( item
);
18031 #endif // TARGET_OS_DARWIN
18033 #if( MDNSRESPONDER_PROJECT )
18034 //===========================================================================================================================
18035 // InterfaceMonitorCmd
18036 //===========================================================================================================================
18038 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor
);
18039 static void _InterfaceMonitorSignalHandler( void *inContext
);
18041 static void InterfaceMonitorCmd( void )
18044 mdns_interface_monitor_t monitor
;
18045 dispatch_source_t signalSource
= NULL
;
18047 __block
int exitCode
;
18049 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
18050 require_noerr_quiet( err
, exit
);
18052 monitor
= mdns_interface_monitor_create( ifIndex
);
18053 require_action( monitor
, exit
, err
= kNoResourcesErr
);
18056 mdns_interface_monitor_set_queue( monitor
, dispatch_get_main_queue() );
18057 mdns_interface_monitor_set_event_handler( monitor
,
18058 ^( mdns_event_t inEvent
, OSStatus inError
)
18062 case mdns_event_error
:
18063 FPrintF( stderr
, "error: Interface monitor failed: %#m\n", inError
);
18064 mdns_interface_monitor_invalidate( monitor
);
18068 case mdns_event_invalidated
:
18069 FPrintF( stdout
, "Interface monitor invalidated.\n" );
18070 mdns_release( monitor
);
18074 FPrintF( stdout
, "Unhandled event '%s' (%ld)\n", mdns_event_to_string( inEvent
), (long) inEvent
);
18078 mdns_interface_monitor_set_update_handler( monitor
,
18079 ^( __unused mdns_interface_flags_t inChangeFlags
)
18081 _InterfaceMonitorPrint( monitor
);
18084 _InterfaceMonitorPrint( monitor
);
18085 mdns_interface_monitor_activate( monitor
);
18087 signal( SIGINT
, SIG_IGN
);
18088 err
= DispatchSignalSourceCreate( SIGINT
, _InterfaceMonitorSignalHandler
, monitor
, &signalSource
);
18089 require_noerr( err
, exit
);
18090 dispatch_resume( signalSource
);
18095 if( err
) ErrQuit( 1, "error: %#m\n", err
);
18098 static void _InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor
)
18100 FPrintF( stdout
, "%{du:time} %@\n", NULL
, inMonitor
);
18103 static void _InterfaceMonitorSignalHandler( void *inContext
)
18105 mdns_interface_monitor_invalidate( (mdns_interface_monitor_t
) inContext
);
18108 //===========================================================================================================================
18110 //===========================================================================================================================
18112 static void _DNSProxyCallback( DNSXConnRef inConnection
, DNSXErrorType inError
);
18113 static void _DNSProxyCmdSignalHandler( void *inContext
);
18115 static void DNSProxyCmd( void )
18119 DNSXConnRef connection
;
18120 IfIndex inputIfIndexes
[ MaxInputIf
];
18121 dispatch_source_t sigIntSource
= NULL
;
18122 dispatch_source_t sigTermSource
= NULL
;
18123 uint32_t outputIfIndex
;
18124 char ifName
[ kInterfaceNameBufLen
];
18126 if( gDNSProxy_InputInterfaceCount
> MaxInputIf
)
18128 FPrintF( stderr
, "error: Invalid input interface count: %zu > %d (max).\n",
18129 gDNSProxy_InputInterfaceCount
, MaxInputIf
);
18134 for( i
= 0; i
< gDNSProxy_InputInterfaceCount
; ++i
)
18138 err
= InterfaceIndexFromArgString( gDNSProxy_InputInterfaces
[ i
], &ifIndex
);
18139 require_noerr_quiet( err
, exit
);
18141 inputIfIndexes
[ i
] = ifIndex
;
18143 while( i
< MaxInputIf
) inputIfIndexes
[ i
++ ] = 0; // Remaining interface indexes are required to be 0.
18145 if( gDNSProxy_OutputInterface
)
18147 err
= InterfaceIndexFromArgString( gDNSProxy_OutputInterface
, &outputIfIndex
);
18148 require_noerr_quiet( err
, exit
);
18152 outputIfIndex
= kDNSIfindexAny
;
18155 FPrintF( stdout
, "Input Interfaces:" );
18156 for( i
= 0; i
< gDNSProxy_InputInterfaceCount
; ++i
)
18158 const uint32_t ifIndex
= (uint32_t) inputIfIndexes
[ i
];
18160 FPrintF( stdout
, "%s %u (%s)", ( i
== 0 ) ? "" : ",", ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
18162 FPrintF( stdout
, "\n" );
18163 FPrintF( stdout
, "Output Interface: %u (%s)\n", outputIfIndex
, InterfaceIndexToName( outputIfIndex
, ifName
) );
18164 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
18165 FPrintF( stdout
, "---\n" );
18168 err
= DNSXEnableProxy( &connection
, kDNSProxyEnable
, inputIfIndexes
, outputIfIndex
, dispatch_get_main_queue(),
18169 _DNSProxyCallback
);
18170 require_noerr_quiet( err
, exit
);
18172 signal( SIGINT
, SIG_IGN
);
18173 err
= DispatchSignalSourceCreate( SIGINT
, _DNSProxyCmdSignalHandler
, connection
, &sigIntSource
);
18174 require_noerr( err
, exit
);
18175 dispatch_activate( sigIntSource
);
18177 signal( SIGTERM
, SIG_IGN
);
18178 err
= DispatchSignalSourceCreate( SIGTERM
, _DNSProxyCmdSignalHandler
, connection
, &sigTermSource
);
18179 require_noerr( err
, exit
);
18180 dispatch_activate( sigTermSource
);
18185 if( err
) ErrQuit( 1, "error: %#m\n", err
);
18188 static void _DNSProxyCallback( DNSXConnRef inConnection
, DNSXErrorType inError
)
18190 Unused( inConnection
);
18192 if( inError
) ErrQuit( 1, "error: DNS proxy failed: %#m\n", inError
);
18195 static void _DNSProxyCmdSignalHandler( void *inContext
)
18197 DNSXConnRef
const connection
= (DNSXConnRef
) inContext
;
18198 struct timeval now
;
18200 gettimeofday( &now
, NULL
);
18202 DNSXRefDeAlloc( connection
);
18204 FPrintF( stdout
, "---\n" );
18205 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
18209 //===========================================================================================================================
18211 //===========================================================================================================================
18213 static void XCTestCmd( void )
18216 setenv(DNSSDUTIL_XCTEST
, DNSSDUTIL_XCTEST
, 0);
18217 if(!TestUtilsRunXCTestNamed(gXCTest_Classname
)) {
18220 unsetenv(DNSSDUTIL_XCTEST
);
18224 #endif // MDNSRESPONDER_PROJECT
18226 //===========================================================================================================================
18227 // DaemonVersionCmd
18228 //===========================================================================================================================
18230 static void DaemonVersionCmd( void )
18233 uint32_t size
, version
;
18236 size
= (uint32_t) sizeof( version
);
18237 err
= DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion
, &version
, &size
);
18238 require_noerr( err
, exit
);
18240 FPrintF( stdout
, "Daemon version: %s\n", SourceVersionToCString( version
, strBuf
) );
18243 if( err
) exit( 1 );
18246 //===========================================================================================================================
18248 //===========================================================================================================================
18250 static void Exit( void *inContext
)
18252 const char * const reason
= (const char *) inContext
;
18254 FPrintF( stdout
, "---\n" );
18255 FPrintF( stdout
, "End time: %{du:time}\n", NULL
);
18256 if( reason
) FPrintF( stdout
, "End reason: %s\n", reason
);
18260 //===========================================================================================================================
18261 // _PrintFExtensionTimestampHandler
18262 //===========================================================================================================================
18265 _PrintFExtensionTimestampHandler(
18266 PrintFContext
* inContext
,
18267 PrintFFormat
* inFormat
,
18268 PrintFVAList
* inArgs
,
18269 void * inUserContext
)
18271 struct timeval now
;
18272 const struct timeval
* tv
;
18273 struct tm
* localTime
;
18276 char dateTimeStr
[ 32 ];
18278 Unused( inUserContext
);
18280 tv
= va_arg( inArgs
->args
, const struct timeval
* );
18281 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
18285 gettimeofday( &now
, NULL
);
18288 localTime
= localtime( &tv
->tv_sec
);
18289 len
= strftime( dateTimeStr
, sizeof( dateTimeStr
), "%Y-%m-%d %H:%M:%S", localTime
);
18290 if( len
== 0 ) dateTimeStr
[ 0 ] = '\0';
18292 n
= PrintFCore( inContext
, "%s.%06u", dateTimeStr
, (unsigned int) tv
->tv_usec
);
18298 //===========================================================================================================================
18299 // _PrintFExtensionDNSMessageHandler
18300 //===========================================================================================================================
18303 _PrintFExtensionDNSMessageHandler(
18304 PrintFContext
* inContext
,
18305 PrintFFormat
* inFormat
,
18306 PrintFVAList
* inArgs
,
18307 void * inUserContext
)
18310 const void * msgPtr
;
18315 Boolean printRawRData
;
18317 Unused( inUserContext
);
18319 msgPtr
= va_arg( inArgs
->args
, const void * );
18320 msgLen
= va_arg( inArgs
->args
, size_t );
18321 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
18323 isMDNS
= ( inFormat
->altForm
> 0 ) ? true : false;
18324 printRawRData
= ( inFormat
->precision
> 0 ) ? true : false;
18325 err
= DNSMessageToText( msgPtr
, msgLen
, isMDNS
, printRawRData
, &text
);
18328 n
= PrintFCore( inContext
, "%*{text}", inFormat
->fieldWidth
, text
, kSizeCString
);
18333 n
= PrintFCore( inContext
, "%*.1H", inFormat
->fieldWidth
, msgPtr
, (int) msgLen
, (int) msgLen
);
18340 //===========================================================================================================================
18341 // _PrintFExtensionCallbackFlagsHandler
18342 //===========================================================================================================================
18345 _PrintFExtensionCallbackFlagsHandler(
18346 PrintFContext
* inContext
,
18347 PrintFFormat
* inFormat
,
18348 PrintFVAList
* inArgs
,
18349 void * inUserContext
)
18351 DNSServiceFlags flags
;
18354 Unused( inUserContext
);
18356 flags
= va_arg( inArgs
->args
, DNSServiceFlags
);
18357 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
18359 n
= PrintFCore( inContext
, "%08X %s%c %c%c",
18360 flags
, DNSServiceFlagsToAddRmvStr( flags
),
18361 ( flags
& kDNSServiceFlagsMoreComing
) ? '+' : ' ',
18362 ( flags
& kDNSServiceFlagAnsweredFromCache
) ? 'C' : ' ',
18363 ( flags
& kDNSServiceFlagsExpiredAnswer
) ? '*' : ' ' );
18369 //===========================================================================================================================
18370 // _PrintFExtensionDNSRecordDataHandler
18371 //===========================================================================================================================
18374 _PrintFExtensionDNSRecordDataHandler(
18375 PrintFContext
* inContext
,
18376 PrintFFormat
* inFormat
,
18377 PrintFVAList
* inArgs
,
18378 void * inUserContext
)
18380 const void * rdataPtr
;
18381 unsigned int rdataLen
, rdataType
;
18384 Unused( inUserContext
);
18386 rdataType
= va_arg( inArgs
->args
, unsigned int );
18387 rdataPtr
= va_arg( inArgs
->args
, const void * );
18388 rdataLen
= va_arg( inArgs
->args
, unsigned int );
18389 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
18391 check( inFormat
->fieldWidth
< INT_MAX
);
18392 fieldWidth
= inFormat
->leftJustify
? -( (int) inFormat
->fieldWidth
) : ( (int) inFormat
->fieldWidth
);
18396 char * rdataStr
= NULL
;
18398 DNSRecordDataToString( rdataPtr
, rdataLen
, rdataType
, NULL
, 0, &rdataStr
);
18401 n
= PrintFCore( inContext
, "%*s", fieldWidth
, rdataStr
);
18406 n
= PrintFCore( inContext
, "%*H", fieldWidth
, rdataPtr
, rdataLen
, rdataLen
);
18411 n
= PrintFCore( inContext
, "%*s", fieldWidth
, "<< ZERO-LENGTH RDATA >>" );
18418 //===========================================================================================================================
18419 // GetDNSSDFlagsFromOpts
18420 //===========================================================================================================================
18422 static DNSServiceFlags
GetDNSSDFlagsFromOpts( void )
18424 DNSServiceFlags flags
;
18426 flags
= (DNSServiceFlags
) gDNSSDFlags
;
18427 if( flags
& kDNSServiceFlagsShareConnection
)
18429 FPrintF( stderr
, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
18430 kDNSServiceFlagsShareConnection
);
18433 if( gDNSSDFlag_AllowExpiredAnswers
) flags
|= kDNSServiceFlagsAllowExpiredAnswers
;
18434 if( gDNSSDFlag_BrowseDomains
) flags
|= kDNSServiceFlagsBrowseDomains
;
18435 if( gDNSSDFlag_DenyCellular
) flags
|= kDNSServiceFlagsDenyCellular
;
18436 if( gDNSSDFlag_DenyConstrained
) flags
|= kDNSServiceFlagsDenyConstrained
;
18437 if( gDNSSDFlag_DenyExpensive
) flags
|= kDNSServiceFlagsDenyExpensive
;
18438 if( gDNSSDFlag_ForceMulticast
) flags
|= kDNSServiceFlagsForceMulticast
;
18439 if( gDNSSDFlag_IncludeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
18440 if( gDNSSDFlag_KnownUnique
) flags
|= kDNSServiceFlagsKnownUnique
;
18441 if( gDNSSDFlag_NoAutoRename
) flags
|= kDNSServiceFlagsNoAutoRename
;
18442 if( gDNSSDFlag_PathEvaluationDone
) flags
|= kDNSServiceFlagsPathEvaluationDone
;
18443 if( gDNSSDFlag_RegistrationDomains
) flags
|= kDNSServiceFlagsRegistrationDomains
;
18444 if( gDNSSDFlag_ReturnIntermediates
) flags
|= kDNSServiceFlagsReturnIntermediates
;
18445 if( gDNSSDFlag_Shared
) flags
|= kDNSServiceFlagsShared
;
18446 if( gDNSSDFlag_SuppressUnusable
) flags
|= kDNSServiceFlagsSuppressUnusable
;
18447 if( gDNSSDFlag_Timeout
) flags
|= kDNSServiceFlagsTimeout
;
18448 if( gDNSSDFlag_UnicastResponse
) flags
|= kDNSServiceFlagsUnicastResponse
;
18449 if( gDNSSDFlag_Unique
) flags
|= kDNSServiceFlagsUnique
;
18450 if( gDNSSDFlag_WakeOnResolve
) flags
|= kDNSServiceFlagsWakeOnResolve
;
18455 //===========================================================================================================================
18456 // CreateConnectionFromArgString
18457 //===========================================================================================================================
18460 CreateConnectionFromArgString(
18461 const char * inString
,
18462 dispatch_queue_t inQueue
,
18463 DNSServiceRef
* outSDRef
,
18464 ConnectionDesc
* outDesc
)
18467 DNSServiceRef sdRef
= NULL
;
18468 ConnectionType type
;
18469 int32_t pid
= -1; // Initializing because the analyzer claims pid may be used uninitialized.
18470 uint8_t uuid
[ 16 ];
18472 if( strcasecmp( inString
, kConnectionArg_Normal
) == 0 )
18474 err
= DNSServiceCreateConnection( &sdRef
);
18475 require_noerr( err
, exit
);
18476 type
= kConnectionType_Normal
;
18478 else if( stricmp_prefix( inString
, kConnectionArgPrefix_PID
) == 0 )
18480 const char * const pidStr
= inString
+ sizeof_string( kConnectionArgPrefix_PID
);
18482 err
= StringToInt32( pidStr
, &pid
);
18485 FPrintF( stderr
, "Invalid delegate connection PID value: %s\n", pidStr
);
18490 memset( uuid
, 0, sizeof( uuid
) );
18491 err
= DNSServiceCreateDelegateConnection( &sdRef
, pid
, uuid
);
18494 FPrintF( stderr
, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err
, pid
);
18497 type
= kConnectionType_DelegatePID
;
18499 else if( stricmp_prefix( inString
, kConnectionArgPrefix_UUID
) == 0 )
18501 const char * const uuidStr
= inString
+ sizeof_string( kConnectionArgPrefix_UUID
);
18503 check_compile_time_code( sizeof( uuid
) == sizeof( uuid_t
) );
18505 err
= StringToUUID( uuidStr
, kSizeCString
, false, uuid
);
18508 FPrintF( stderr
, "Invalid delegate connection UUID value: %s\n", uuidStr
);
18513 err
= DNSServiceCreateDelegateConnection( &sdRef
, 0, uuid
);
18516 FPrintF( stderr
, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err
, uuid
);
18519 type
= kConnectionType_DelegateUUID
;
18523 FPrintF( stderr
, "Unrecognized connection string \"%s\".\n", inString
);
18528 err
= DNSServiceSetDispatchQueue( sdRef
, inQueue
);
18529 require_noerr( err
, exit
);
18534 outDesc
->type
= type
;
18535 if( type
== kConnectionType_DelegatePID
) outDesc
->delegate
.pid
= pid
;
18536 else if( type
== kConnectionType_DelegateUUID
) memcpy( outDesc
->delegate
.uuid
, uuid
, 16 );
18541 if( sdRef
) DNSServiceRefDeallocate( sdRef
);
18545 //===========================================================================================================================
18546 // InterfaceIndexFromArgString
18547 //===========================================================================================================================
18549 static OSStatus
InterfaceIndexFromArgString( const char *inString
, uint32_t *outIndex
)
18556 ifIndex
= if_nametoindex( inString
);
18559 err
= StringToUInt32( inString
, &ifIndex
);
18562 FPrintF( stderr
, "error: Invalid interface value: %s\n", inString
);
18573 *outIndex
= ifIndex
;
18580 //===========================================================================================================================
18581 // RecordDataFromArgString
18582 //===========================================================================================================================
18584 static OSStatus
RecordDataFromArgString( const char *inString
, uint8_t **outDataPtr
, size_t *outDataLen
)
18587 uint8_t * dataPtr
= NULL
;
18594 else if( stricmp_prefix( inString
, kRDataArgPrefix_Domain
) == 0 )
18596 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_Domain
);
18598 err
= StringToDomainName( str
, &dataPtr
, &dataLen
);
18599 require_noerr_quiet( err
, exit
);
18604 else if( stricmp_prefix( inString
, kRDataArgPrefix_File
) == 0 )
18606 const char * const path
= inString
+ sizeof_string( kRDataArgPrefix_File
);
18608 err
= CopyFileDataByPath( path
, (char **) &dataPtr
, &dataLen
);
18609 require_noerr( err
, exit
);
18610 require_action( dataLen
<= kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
18613 // Hexadecimal string
18615 else if( stricmp_prefix( inString
, kRDataArgPrefix_HexString
) == 0 )
18617 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_HexString
);
18619 err
= HexToDataCopy( str
, kSizeCString
, kHexToData_DefaultFlags
, &dataPtr
, &dataLen
, NULL
);
18620 require_noerr( err
, exit
);
18621 require_action( dataLen
<= kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
18624 // IPv4 address string
18626 else if( stricmp_prefix( inString
, kRDataArgPrefix_IPv4
) == 0 )
18628 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_IPv4
);
18630 err
= StringToARecordData( str
, &dataPtr
, &dataLen
);
18631 require_noerr_quiet( err
, exit
);
18634 // IPv6 address string
18636 else if( stricmp_prefix( inString
, kRDataArgPrefix_IPv6
) == 0 )
18638 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_IPv6
);
18640 err
= StringToAAAARecordData( str
, &dataPtr
, &dataLen
);
18641 require_noerr_quiet( err
, exit
);
18646 else if( stricmp_prefix( inString
, kRDataArgPrefix_SRV
) == 0 )
18648 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_SRV
);
18650 err
= CreateSRVRecordDataFromString( str
, &dataPtr
, &dataLen
);
18651 require_noerr( err
, exit
);
18654 // String with escaped hex and octal bytes
18656 else if( stricmp_prefix( inString
, kRDataArgPrefix_String
) == 0 )
18658 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_String
);
18659 const char * const end
= str
+ strlen( str
);
18666 success
= _ParseQuotedEscapedString( str
, end
, "", NULL
, 0, NULL
, &totalLen
, NULL
);
18667 require_action( success
, exit
, err
= kParamErr
);
18668 require_action( totalLen
<= kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
18670 dataLen
= totalLen
;
18671 dataPtr
= (uint8_t *) malloc( dataLen
);
18672 require_action( dataPtr
, exit
, err
= kNoMemoryErr
);
18674 success
= _ParseQuotedEscapedString( str
, end
, "", (char *) dataPtr
, dataLen
, &copiedLen
, NULL
, NULL
);
18675 require_action( success
, exit
, err
= kParamErr
);
18676 check( copiedLen
== dataLen
);
18687 else if( stricmp_prefix( inString
, kRDataArgPrefix_TXT
) == 0 )
18689 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_TXT
);
18691 err
= CreateTXTRecordDataFromString( str
, ',', &dataPtr
, &dataLen
);
18692 require_noerr( err
, exit
);
18695 // Unrecognized format
18699 FPrintF( stderr
, "Unrecognized record data string \"%s\".\n", inString
);
18705 *outDataLen
= dataLen
;
18706 *outDataPtr
= dataPtr
;
18710 FreeNullSafe( dataPtr
);
18714 //===========================================================================================================================
18715 // RecordTypeFromArgString
18716 //===========================================================================================================================
18720 uint16_t value
; // Record type's numeric value.
18721 const char * name
; // Record type's name as a string (e.g., "A", "PTR", "SRV").
18725 static const RecordType kRecordTypes
[] =
18729 { kDNSServiceType_A
, "A" },
18730 { kDNSServiceType_AAAA
, "AAAA" },
18731 { kDNSServiceType_PTR
, "PTR" },
18732 { kDNSServiceType_SRV
, "SRV" },
18733 { kDNSServiceType_TXT
, "TXT" },
18734 { kDNSServiceType_CNAME
, "CNAME" },
18735 { kDNSServiceType_SOA
, "SOA" },
18736 { kDNSServiceType_NSEC
, "NSEC" },
18737 { kDNSServiceType_NS
, "NS" },
18738 { kDNSServiceType_MX
, "MX" },
18739 { kDNSServiceType_ANY
, "ANY" },
18740 { kDNSServiceType_OPT
, "OPT" },
18742 // Less common types.
18744 { kDNSServiceType_MD
, "MD" },
18745 { kDNSServiceType_NS
, "NS" },
18746 { kDNSServiceType_MD
, "MD" },
18747 { kDNSServiceType_MF
, "MF" },
18748 { kDNSServiceType_MB
, "MB" },
18749 { kDNSServiceType_MG
, "MG" },
18750 { kDNSServiceType_MR
, "MR" },
18751 { kDNSServiceType_NULL
, "NULL" },
18752 { kDNSServiceType_WKS
, "WKS" },
18753 { kDNSServiceType_HINFO
, "HINFO" },
18754 { kDNSServiceType_MINFO
, "MINFO" },
18755 { kDNSServiceType_RP
, "RP" },
18756 { kDNSServiceType_AFSDB
, "AFSDB" },
18757 { kDNSServiceType_X25
, "X25" },
18758 { kDNSServiceType_ISDN
, "ISDN" },
18759 { kDNSServiceType_RT
, "RT" },
18760 { kDNSServiceType_NSAP
, "NSAP" },
18761 { kDNSServiceType_NSAP_PTR
, "NSAP_PTR" },
18762 { kDNSServiceType_SIG
, "SIG" },
18763 { kDNSServiceType_KEY
, "KEY" },
18764 { kDNSServiceType_PX
, "PX" },
18765 { kDNSServiceType_GPOS
, "GPOS" },
18766 { kDNSServiceType_LOC
, "LOC" },
18767 { kDNSServiceType_NXT
, "NXT" },
18768 { kDNSServiceType_EID
, "EID" },
18769 { kDNSServiceType_NIMLOC
, "NIMLOC" },
18770 { kDNSServiceType_ATMA
, "ATMA" },
18771 { kDNSServiceType_NAPTR
, "NAPTR" },
18772 { kDNSServiceType_KX
, "KX" },
18773 { kDNSServiceType_CERT
, "CERT" },
18774 { kDNSServiceType_A6
, "A6" },
18775 { kDNSServiceType_DNAME
, "DNAME" },
18776 { kDNSServiceType_SINK
, "SINK" },
18777 { kDNSServiceType_APL
, "APL" },
18778 { kDNSServiceType_DS
, "DS" },
18779 { kDNSServiceType_SSHFP
, "SSHFP" },
18780 { kDNSServiceType_IPSECKEY
, "IPSECKEY" },
18781 { kDNSServiceType_RRSIG
, "RRSIG" },
18782 { kDNSServiceType_DNSKEY
, "DNSKEY" },
18783 { kDNSServiceType_DHCID
, "DHCID" },
18784 { kDNSServiceType_NSEC3
, "NSEC3" },
18785 { kDNSServiceType_NSEC3PARAM
, "NSEC3PARAM" },
18786 { kDNSServiceType_HIP
, "HIP" },
18787 { kDNSServiceType_SPF
, "SPF" },
18788 { kDNSServiceType_UINFO
, "UINFO" },
18789 { kDNSServiceType_UID
, "UID" },
18790 { kDNSServiceType_GID
, "GID" },
18791 { kDNSServiceType_UNSPEC
, "UNSPEC" },
18792 { kDNSServiceType_TKEY
, "TKEY" },
18793 { kDNSServiceType_TSIG
, "TSIG" },
18794 { kDNSServiceType_IXFR
, "IXFR" },
18795 { kDNSServiceType_AXFR
, "AXFR" },
18796 { kDNSServiceType_MAILB
, "MAILB" },
18797 { kDNSServiceType_MAILA
, "MAILA" }
18800 static OSStatus
RecordTypeFromArgString( const char *inString
, uint16_t *outValue
)
18804 const RecordType
* type
;
18805 const RecordType
* const end
= kRecordTypes
+ countof( kRecordTypes
);
18807 for( type
= kRecordTypes
; type
< end
; ++type
)
18809 if( strcasecmp( type
->name
, inString
) == 0 )
18811 *outValue
= type
->value
;
18816 err
= StringToInt32( inString
, &i32
);
18817 require_noerr_quiet( err
, exit
);
18818 require_action_quiet( ( i32
>= 0 ) && ( i32
<= UINT16_MAX
), exit
, err
= kParamErr
);
18820 *outValue
= (uint16_t) i32
;
18826 //===========================================================================================================================
18827 // RecordClassFromArgString
18828 //===========================================================================================================================
18830 static OSStatus
RecordClassFromArgString( const char *inString
, uint16_t *outValue
)
18835 if( strcasecmp( inString
, "IN" ) == 0 )
18837 *outValue
= kDNSServiceClass_IN
;
18842 err
= StringToInt32( inString
, &i32
);
18843 require_noerr_quiet( err
, exit
);
18844 require_action_quiet( ( i32
>= 0 ) && ( i32
<= UINT16_MAX
), exit
, err
= kParamErr
);
18846 *outValue
= (uint16_t) i32
;
18852 //===========================================================================================================================
18853 // InterfaceIndexToName
18854 //===========================================================================================================================
18856 static char * InterfaceIndexToName( uint32_t inIfIndex
, char inNameBuf
[ kInterfaceNameBufLen
] )
18858 switch( inIfIndex
)
18860 case kDNSServiceInterfaceIndexAny
:
18861 strlcpy( inNameBuf
, "Any", kInterfaceNameBufLen
);
18864 case kDNSServiceInterfaceIndexLocalOnly
:
18865 strlcpy( inNameBuf
, "LocalOnly", kInterfaceNameBufLen
);
18868 case kDNSServiceInterfaceIndexUnicast
:
18869 strlcpy( inNameBuf
, "Unicast", kInterfaceNameBufLen
);
18872 case kDNSServiceInterfaceIndexP2P
:
18873 strlcpy( inNameBuf
, "P2P", kInterfaceNameBufLen
);
18876 #if( defined( kDNSServiceInterfaceIndexBLE ) )
18877 case kDNSServiceInterfaceIndexBLE
:
18878 strlcpy( inNameBuf
, "BLE", kInterfaceNameBufLen
);
18886 name
= if_indextoname( inIfIndex
, inNameBuf
);
18887 if( !name
) strlcpy( inNameBuf
, "NO NAME", kInterfaceNameBufLen
);
18892 return( inNameBuf
);
18895 //===========================================================================================================================
18896 // RecordTypeToString
18897 //===========================================================================================================================
18899 static const char * RecordTypeToString( unsigned int inValue
)
18901 const RecordType
* type
;
18902 const RecordType
* const end
= kRecordTypes
+ countof( kRecordTypes
);
18904 for( type
= kRecordTypes
; type
< end
; ++type
)
18906 if( type
->value
== inValue
) return( type
->name
);
18911 //===========================================================================================================================
18912 // DNSRecordDataToString
18913 //===========================================================================================================================
18916 DNSRecordDataToString(
18917 const void * inRDataPtr
,
18919 unsigned int inRDataType
,
18920 const void * inMsgPtr
,
18922 char ** outString
)
18925 const uint8_t * const rdataPtr
= (uint8_t *) inRDataPtr
;
18926 const uint8_t * const rdataEnd
= rdataPtr
+ inRDataLen
;
18928 const uint8_t * ptr
;
18930 char domainNameStr
[ kDNSServiceMaxDomainName
];
18936 if( inRDataType
== kDNSServiceType_A
)
18938 require_action_quiet( inRDataLen
== 4, exit
, err
= kMalformedErr
);
18940 ASPrintF( &rdataStr
, "%.4a", rdataPtr
);
18941 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
18946 else if( inRDataType
== kDNSServiceType_AAAA
)
18948 require_action_quiet( inRDataLen
== 16, exit
, err
= kMalformedErr
);
18950 ASPrintF( &rdataStr
, "%.16a", rdataPtr
);
18951 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
18954 // PTR, CNAME, or NS Record
18956 else if( ( inRDataType
== kDNSServiceType_PTR
) ||
18957 ( inRDataType
== kDNSServiceType_CNAME
) ||
18958 ( inRDataType
== kDNSServiceType_NS
) )
18962 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, NULL
);
18963 require_noerr( err
, exit
);
18967 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, NULL
);
18968 require_noerr( err
, exit
);
18971 rdataStr
= strdup( domainNameStr
);
18972 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
18977 else if( inRDataType
== kDNSServiceType_SRV
)
18979 const dns_fixed_fields_srv
* fields
;
18980 const uint8_t * target
;
18981 unsigned int priority
, weight
, port
;
18983 require_action_quiet( inRDataLen
> sizeof( dns_fixed_fields_srv
), exit
, err
= kMalformedErr
);
18985 fields
= (const dns_fixed_fields_srv
*) rdataPtr
;
18986 priority
= dns_fixed_fields_srv_get_priority( fields
);
18987 weight
= dns_fixed_fields_srv_get_weight( fields
);
18988 port
= dns_fixed_fields_srv_get_port( fields
);
18989 target
= (const uint8_t *) &fields
[ 1 ];
18993 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, target
, domainNameStr
, NULL
);
18994 require_noerr( err
, exit
);
18998 err
= DomainNameToString( target
, rdataEnd
, domainNameStr
, NULL
);
18999 require_noerr( err
, exit
);
19002 ASPrintF( &rdataStr
, "%u %u %u %s", priority
, weight
, port
, domainNameStr
);
19003 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19008 else if( inRDataType
== kDNSServiceType_TXT
)
19010 require_action_quiet( inRDataLen
> 0, exit
, err
= kMalformedErr
);
19012 if( inRDataLen
== 1 )
19014 ASPrintF( &rdataStr
, "%#H", rdataPtr
, (int) inRDataLen
, INT_MAX
);
19015 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19019 ASPrintF( &rdataStr
, "%#{txt}", rdataPtr
, inRDataLen
);
19020 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19026 else if( inRDataType
== kDNSServiceType_SOA
)
19028 const dns_fixed_fields_soa
* fields
;
19029 uint32_t serial
, refresh
, retry
, expire
, minimum
;
19033 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
19034 require_noerr( err
, exit
);
19036 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
19038 rdataStr
= strdup( domainNameStr
);
19039 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19041 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, ptr
, domainNameStr
, &ptr
);
19042 require_noerr( err
, exit
);
19046 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
19047 require_noerr( err
, exit
);
19049 rdataStr
= strdup( domainNameStr
);
19050 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19052 err
= DomainNameToString( ptr
, rdataEnd
, domainNameStr
, &ptr
);
19053 require_noerr( err
, exit
);
19056 require_action_quiet( ( rdataEnd
- ptr
) == sizeof( dns_fixed_fields_soa
), exit
, err
= kMalformedErr
);
19058 fields
= (const dns_fixed_fields_soa
*) ptr
;
19059 serial
= dns_fixed_fields_soa_get_serial( fields
);
19060 refresh
= dns_fixed_fields_soa_get_refresh( fields
);
19061 retry
= dns_fixed_fields_soa_get_retry( fields
);
19062 expire
= dns_fixed_fields_soa_get_expire( fields
);
19063 minimum
= dns_fixed_fields_soa_get_minimum( fields
);
19065 n
= AppendPrintF( &rdataStr
, " %s %u %u %u %u %u\n", domainNameStr
, serial
, refresh
, retry
, expire
, minimum
);
19066 require_action( n
> 0, exit
, err
= kUnknownErr
);
19071 else if( inRDataType
== kDNSServiceType_NSEC
)
19073 unsigned int windowBlock
, bitmapLen
, i
, recordType
;
19074 const uint8_t * bitmapPtr
;
19078 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
19079 require_noerr( err
, exit
);
19083 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
19084 require_noerr( err
, exit
);
19087 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
19089 rdataStr
= strdup( domainNameStr
);
19090 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19092 for( ; ptr
< rdataEnd
; ptr
+= ( 2 + bitmapLen
) )
19094 require_action_quiet( ( ptr
+ 2 ) < rdataEnd
, exit
, err
= kMalformedErr
);
19096 windowBlock
= ptr
[ 0 ];
19097 bitmapLen
= ptr
[ 1 ];
19098 bitmapPtr
= &ptr
[ 2 ];
19100 require_action_quiet( ( bitmapLen
>= 1 ) && ( bitmapLen
<= 32 ) , exit
, err
= kMalformedErr
);
19101 require_action_quiet( ( bitmapPtr
+ bitmapLen
) <= rdataEnd
, exit
, err
= kMalformedErr
);
19103 for( i
= 0; i
< BitArray_MaxBits( bitmapLen
); ++i
)
19105 if( BitArray_GetBit( bitmapPtr
, bitmapLen
, i
) )
19107 recordType
= ( windowBlock
* 256 ) + i
;
19108 n
= AppendPrintF( &rdataStr
, " %s", RecordTypeToString( recordType
) );
19109 require_action( n
> 0, exit
, err
= kUnknownErr
);
19117 else if( inRDataType
== kDNSServiceType_MX
)
19119 uint16_t preference
;
19120 const uint8_t * exchange
;
19122 require_action_quiet( ( rdataPtr
+ 2 ) < rdataEnd
, exit
, err
= kMalformedErr
);
19124 preference
= ReadBig16( rdataPtr
);
19125 exchange
= &rdataPtr
[ 2 ];
19129 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, exchange
, domainNameStr
, NULL
);
19130 require_noerr( err
, exit
);
19134 err
= DomainNameToString( exchange
, rdataEnd
, domainNameStr
, NULL
);
19135 require_noerr( err
, exit
);
19138 n
= ASPrintF( &rdataStr
, "%u %s", preference
, domainNameStr
);
19139 require_action( n
> 0, exit
, err
= kUnknownErr
);
19142 // Unhandled record type
19146 err
= kNotHandledErr
;
19151 *outString
= rdataStr
;
19156 FreeNullSafe( rdataStr
);
19160 //===========================================================================================================================
19161 // DNSMessageToText
19162 //===========================================================================================================================
19164 #define DNSFlagsOpCodeToString( X ) ( \
19165 ( (X) == kDNSOpCode_Query ) ? "Query" : \
19166 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \
19167 ( (X) == kDNSOpCode_Status ) ? "Status" : \
19168 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \
19169 ( (X) == kDNSOpCode_Update ) ? "Update" : \
19172 #define DNSFlagsRCodeToString( X ) ( \
19173 ( (X) == kDNSRCode_NoError ) ? "NoError" : \
19174 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \
19175 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \
19176 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \
19177 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \
19178 ( (X) == kDNSRCode_Refused ) ? "Refused" : \
19183 const uint8_t * inMsgPtr
,
19185 const Boolean inMDNS
,
19186 const Boolean inPrintRaw
,
19190 DataBuffer dataBuf
;
19192 const DNSHeader
* hdr
;
19193 const uint8_t * ptr
;
19194 unsigned int id
, flags
, opcode
, rcode
;
19195 unsigned int questionCount
, answerCount
, authorityCount
, additionalCount
, i
, totalRRCount
;
19196 uint8_t name
[ kDomainNameLengthMax
];
19197 char nameStr
[ kDNSServiceMaxDomainName
];
19199 DataBuffer_Init( &dataBuf
, NULL
, 0, SIZE_MAX
);
19200 #define _Append( ... ) do { err = DataBuffer_AppendF( &dataBuf, __VA_ARGS__ ); require_noerr( err, exit ); } while( 0 )
19202 require_action_quiet( inMsgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
19204 hdr
= (DNSHeader
*) inMsgPtr
;
19205 id
= DNSHeaderGetID( hdr
);
19206 flags
= DNSHeaderGetFlags( hdr
);
19207 questionCount
= DNSHeaderGetQuestionCount( hdr
);
19208 answerCount
= DNSHeaderGetAnswerCount( hdr
);
19209 authorityCount
= DNSHeaderGetAuthorityCount( hdr
);
19210 additionalCount
= DNSHeaderGetAdditionalCount( hdr
);
19211 opcode
= DNSFlagsGetOpCode( flags
);
19212 rcode
= DNSFlagsGetRCode( flags
);
19214 _Append( "ID: 0x%04X (%u)\n", id
, id
);
19215 _Append( "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA%?s%?s %s\n",
19217 ( flags
& kDNSHeaderFlag_Response
) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode
),
19218 ( flags
& kDNSHeaderFlag_AuthAnswer
) ? ' ' : '!',
19219 ( flags
& kDNSHeaderFlag_Truncation
) ? ' ' : '!',
19220 ( flags
& kDNSHeaderFlag_RecursionDesired
) ? ' ' : '!',
19221 ( flags
& kDNSHeaderFlag_RecursionAvailable
) ? ' ' : '!',
19222 !inMDNS
, ( flags
& kDNSHeaderFlag_AuthenticData
) ? " AD" : "!AD",
19223 !inMDNS
, ( flags
& kDNSHeaderFlag_CheckingDisabled
) ? " CD" : "!CD",
19224 DNSFlagsRCodeToString( rcode
) );
19225 _Append( "Question count: %u\n", questionCount
);
19226 _Append( "Answer count: %u\n", answerCount
);
19227 _Append( "Authority count: %u\n", authorityCount
);
19228 _Append( "Additional count: %u\n", additionalCount
);
19230 ptr
= (const uint8_t *) &hdr
[ 1 ];
19231 for( i
= 0; i
< questionCount
; ++i
)
19233 uint16_t qtype
, qclass
;
19236 err
= DNSMessageExtractQuestion( inMsgPtr
, inMsgLen
, ptr
, name
, &qtype
, &qclass
, &ptr
);
19237 require_noerr( err
, exit
);
19239 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
19240 require_noerr( err
, exit
);
19242 isQU
= ( inMDNS
&& ( qclass
& kQClassUnicastResponseBit
) ) ? true : false;
19243 if( inMDNS
) qclass
&= ~kQClassUnicastResponseBit
;
19245 if( i
== 0 ) _Append( "\nQUESTION SECTION\n" );
19247 _Append( "%-30s %2s %?2s%?2u %-5s\n",
19248 nameStr
, inMDNS
? ( isQU
? "QU" : "QM" ) : "",
19249 ( qclass
== kDNSServiceClass_IN
), "IN", ( qclass
!= kDNSServiceClass_IN
), qclass
, RecordTypeToString( qtype
) );
19252 totalRRCount
= answerCount
+ authorityCount
+ additionalCount
;
19253 for( i
= 0; i
< totalRRCount
; ++i
)
19258 const uint8_t * rdataPtr
;
19261 Boolean cacheFlush
;
19263 err
= DNSMessageExtractRecord( inMsgPtr
, inMsgLen
, ptr
, name
, &type
, &class, &ttl
, &rdataPtr
, &rdataLen
, &ptr
);
19264 require_noerr( err
, exit
);
19266 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
19267 require_noerr( err
, exit
);
19269 cacheFlush
= ( inMDNS
&& ( class & kRRClassCacheFlushBit
) ) ? true : false;
19270 if( inMDNS
) class &= ~kRRClassCacheFlushBit
;
19273 if( !inPrintRaw
) DNSRecordDataToString( rdataPtr
, rdataLen
, type
, inMsgPtr
, inMsgLen
, &rdataStr
);
19276 ASPrintF( &rdataStr
, "%#H", rdataPtr
, (int) rdataLen
, (int) rdataLen
);
19277 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
19280 if( answerCount
&& ( i
== 0 ) ) _Append( "\nANSWER SECTION\n" );
19281 else if( authorityCount
&& ( i
== answerCount
) ) _Append( "\nAUTHORITY SECTION\n" );
19282 else if( additionalCount
&& ( i
== ( answerCount
+ authorityCount
) ) ) _Append( "\nADDITIONAL SECTION\n" );
19284 _Append( "%-42s %6u %2s %?2s%?2u %-5s %s\n",
19285 nameStr
, ttl
, cacheFlush
? "CF" : "",
19286 ( class == kDNSServiceClass_IN
), "IN", ( class != kDNSServiceClass_IN
), class,
19287 RecordTypeToString( type
), rdataStr
);
19292 err
= DataBuffer_Append( &dataBuf
, "", 1 );
19293 require_noerr( err
, exit
);
19295 err
= DataBuffer_Detach( &dataBuf
, (uint8_t **) outText
, &len
);
19296 require_noerr( err
, exit
);
19299 DataBuffer_Free( &dataBuf
);
19303 //===========================================================================================================================
19304 // WriteDNSQueryMessage
19305 //===========================================================================================================================
19308 WriteDNSQueryMessage(
19309 uint8_t inMsg
[ kDNSQueryMessageMaxLen
],
19312 const char * inQName
,
19315 size_t * outMsgLen
)
19318 uint8_t qname
[ kDomainNameLengthMax
];
19320 err
= DomainNameFromString( qname
, inQName
, NULL
);
19321 require_noerr_quiet( err
, exit
);
19323 err
= DNSMessageWriteQuery( inMsgID
, inFlags
, qname
, inQType
, inQClass
, inMsg
, outMsgLen
);
19324 require_noerr_quiet( err
, exit
);
19330 //===========================================================================================================================
19331 // DispatchSignalSourceCreate
19332 //===========================================================================================================================
19335 DispatchSignalSourceCreate(
19337 DispatchHandler inEventHandler
,
19339 dispatch_source_t
* outSource
)
19342 dispatch_source_t source
;
19344 source
= dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL
, (uintptr_t) inSignal
, 0, dispatch_get_main_queue() );
19345 require_action( source
, exit
, err
= kUnknownErr
);
19347 dispatch_set_context( source
, inContext
);
19348 dispatch_source_set_event_handler_f( source
, inEventHandler
);
19350 *outSource
= source
;
19357 //===========================================================================================================================
19358 // DispatchSocketSourceCreate
19359 //===========================================================================================================================
19362 DispatchSocketSourceCreate(
19364 dispatch_source_type_t inType
,
19365 dispatch_queue_t inQueue
,
19366 DispatchHandler inEventHandler
,
19367 DispatchHandler inCancelHandler
,
19369 dispatch_source_t
* outSource
)
19372 dispatch_source_t source
;
19374 source
= dispatch_source_create( inType
, (uintptr_t) inSock
, 0, inQueue
? inQueue
: dispatch_get_main_queue() );
19375 require_action( source
, exit
, err
= kUnknownErr
);
19377 dispatch_set_context( source
, inContext
);
19378 dispatch_source_set_event_handler_f( source
, inEventHandler
);
19379 dispatch_source_set_cancel_handler_f( source
, inCancelHandler
);
19381 *outSource
= source
;
19388 //===========================================================================================================================
19389 // DispatchTimerCreate
19390 //===========================================================================================================================
19393 DispatchTimerCreate(
19394 dispatch_time_t inStart
,
19395 uint64_t inIntervalNs
,
19396 uint64_t inLeewayNs
,
19397 dispatch_queue_t inQueue
,
19398 DispatchHandler inEventHandler
,
19399 DispatchHandler inCancelHandler
,
19401 dispatch_source_t
* outTimer
)
19404 dispatch_source_t timer
;
19406 timer
= dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, inQueue
? inQueue
: dispatch_get_main_queue() );
19407 require_action( timer
, exit
, err
= kUnknownErr
);
19409 dispatch_source_set_timer( timer
, inStart
, inIntervalNs
, inLeewayNs
);
19410 dispatch_set_context( timer
, inContext
);
19411 dispatch_source_set_event_handler_f( timer
, inEventHandler
);
19412 dispatch_source_set_cancel_handler_f( timer
, inCancelHandler
);
19421 //===========================================================================================================================
19422 // DispatchProcessMonitorCreate
19423 //===========================================================================================================================
19426 DispatchProcessMonitorCreate(
19428 unsigned long inFlags
,
19429 dispatch_queue_t inQueue
,
19430 DispatchHandler inEventHandler
,
19431 DispatchHandler inCancelHandler
,
19433 dispatch_source_t
* outMonitor
)
19436 dispatch_source_t monitor
;
19438 monitor
= dispatch_source_create( DISPATCH_SOURCE_TYPE_PROC
, (uintptr_t) inPID
, inFlags
,
19439 inQueue
? inQueue
: dispatch_get_main_queue() );
19440 require_action( monitor
, exit
, err
= kUnknownErr
);
19442 dispatch_set_context( monitor
, inContext
);
19443 dispatch_source_set_event_handler_f( monitor
, inEventHandler
);
19444 dispatch_source_set_cancel_handler_f( monitor
, inCancelHandler
);
19446 *outMonitor
= monitor
;
19453 //===========================================================================================================================
19454 // ServiceTypeDescription
19455 //===========================================================================================================================
19459 const char * name
; // Name of the service type in two-label "_service._proto" format.
19460 const char * description
; // Description of the service type.
19464 // A Non-comprehensive table of DNS-SD service types
19466 static const ServiceType kServiceTypes
[] =
19468 { "_acp-sync._tcp", "AirPort Base Station Sync" },
19469 { "_adisk._tcp", "Automatic Disk Discovery" },
19470 { "_afpovertcp._tcp", "Apple File Sharing" },
19471 { "_airdrop._tcp", "AirDrop" },
19472 { "_airplay._tcp", "AirPlay" },
19473 { "_airport._tcp", "AirPort Base Station" },
19474 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
19475 { "_eppc._tcp", "Remote AppleEvents" },
19476 { "_ftp._tcp", "File Transfer Protocol" },
19477 { "_home-sharing._tcp", "Home Sharing" },
19478 { "_homekit._tcp", "HomeKit" },
19479 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
19480 { "_https._tcp", "HTTP over SSL/TLS" },
19481 { "_ipp._tcp", "Internet Printing Protocol" },
19482 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
19483 { "_mediaremotetv._tcp", "Media Remote" },
19484 { "_net-assistant._tcp", "Apple Remote Desktop" },
19485 { "_od-master._tcp", "OpenDirectory Master" },
19486 { "_nfs._tcp", "Network File System" },
19487 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
19488 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
19489 { "_raop._tcp", "Remote Audio Output Protocol" },
19490 { "_rfb._tcp", "Remote Frame Buffer" },
19491 { "_scanner._tcp", "Bonjour Scanning" },
19492 { "_smb._tcp", "Server Message Block over TCP/IP" },
19493 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
19494 { "_sleep-proxy._udp", "Sleep Proxy Server" },
19495 { "_ssh._tcp", "SSH Remote Login Protocol" },
19496 { "_teleport._tcp", "teleport" },
19497 { "_tftp._tcp", "Trivial File Transfer Protocol" },
19498 { "_workstation._tcp", "Workgroup Manager" },
19499 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
19500 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
19503 static const char * ServiceTypeDescription( const char *inName
)
19505 const ServiceType
* serviceType
;
19506 const ServiceType
* const end
= kServiceTypes
+ countof( kServiceTypes
);
19508 for( serviceType
= kServiceTypes
; serviceType
< end
; ++serviceType
)
19510 if( ( stricmp_prefix( inName
, serviceType
->name
) == 0 ) )
19512 const char * const ptr
= &inName
[ strlen( serviceType
->name
) ];
19514 if( ( ptr
[ 0 ] == '\0' ) || ( ( ptr
[ 0 ] == '.' ) && ( ptr
[ 1 ] == '\0' ) ) )
19516 return( serviceType
->description
);
19523 //===========================================================================================================================
19524 // SocketContextCreate
19525 //===========================================================================================================================
19527 static OSStatus
SocketContextCreate( SocketRef inSock
, void * inUserContext
, SocketContext
**outContext
)
19530 SocketContext
* context
;
19532 context
= (SocketContext
*) calloc( 1, sizeof( *context
) );
19533 require_action( context
, exit
, err
= kNoMemoryErr
);
19535 context
->refCount
= 1;
19536 context
->sock
= inSock
;
19537 context
->userContext
= inUserContext
;
19539 *outContext
= context
;
19546 //===========================================================================================================================
19547 // SocketContextRetain
19548 //===========================================================================================================================
19550 static SocketContext
* SocketContextRetain( SocketContext
*inContext
)
19552 ++inContext
->refCount
;
19553 return( inContext
);
19556 //===========================================================================================================================
19557 // SocketContextRelease
19558 //===========================================================================================================================
19560 static void SocketContextRelease( SocketContext
*inContext
)
19562 if( --inContext
->refCount
== 0 )
19564 ForgetSocket( &inContext
->sock
);
19569 //===========================================================================================================================
19570 // SocketContextCancelHandler
19571 //===========================================================================================================================
19573 static void SocketContextCancelHandler( void *inContext
)
19575 SocketContextRelease( (SocketContext
*) inContext
);
19578 //===========================================================================================================================
19580 //===========================================================================================================================
19582 static OSStatus
StringToInt32( const char *inString
, int32_t *outValue
)
19588 value
= strtol( inString
, &endPtr
, 0 );
19589 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
19590 require_action_quiet( ( value
>= INT32_MIN
) && ( value
<= INT32_MAX
), exit
, err
= kRangeErr
);
19592 *outValue
= (int32_t) value
;
19599 //===========================================================================================================================
19601 //===========================================================================================================================
19603 static OSStatus
StringToUInt32( const char *inString
, uint32_t *outValue
)
19609 value
= (uint32_t) strtol( inString
, &endPtr
, 0 );
19610 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
19619 #if( TARGET_OS_DARWIN )
19620 //===========================================================================================================================
19622 //===========================================================================================================================
19624 static int64_t _StringToInt64( const char *inString
, OSStatus
*outError
)
19631 set_errno_compat( 0 );
19632 val
= strtoll( inString
, &end
, 0 );
19633 errnoVal
= errno_compat();
19635 require_action_quiet( ( *end
== '\0' ) && ( end
!= inString
), exit
, err
= kMalformedErr
);
19636 require_action_quiet( ( ( val
!= LLONG_MIN
) && ( val
!= LLONG_MAX
) ) || ( errnoVal
!= ERANGE
), exit
, err
= kRangeErr
);
19637 require_action_quiet( ( val
>= INT64_MIN
) && ( val
<= INT64_MAX
), exit
, err
= kRangeErr
);
19641 if( outError
) *outError
= err
;
19642 return( (int64_t)val
);
19645 //===========================================================================================================================
19647 //===========================================================================================================================
19649 static uint64_t _StringToUInt64( const char *inString
, OSStatus
*outError
)
19652 unsigned long long val
;
19656 set_errno_compat( 0 );
19657 val
= strtoull( inString
, &end
, 0 );
19658 errnoVal
= errno_compat();
19660 require_action_quiet( ( *end
== '\0' ) && ( end
!= inString
), exit
, err
= kMalformedErr
);
19661 require_action_quiet( ( val
!= ULLONG_MAX
) || ( errnoVal
!= ERANGE
), exit
, err
= kRangeErr
);
19662 require_action_quiet( val
<= UINT64_MAX
, exit
, err
= kRangeErr
);
19666 if( outError
) *outError
= err
;
19667 return( (uint64_t)val
);
19670 //===========================================================================================================================
19672 //===========================================================================================================================
19674 static pid_t
_StringToPID( const char *inString
, OSStatus
*outError
)
19679 val
= _StringToInt64( inString
, &err
);
19680 require_noerr_quiet( err
, exit
);
19681 require_action_quiet( val
== (pid_t
) val
, exit
, err
= kRangeErr
);
19685 if( outError
) *outError
= err
;
19686 return( (pid_t
) val
);
19689 //===========================================================================================================================
19690 // _ParseEscapedString
19692 // Note: Similar to ParseEscapedString() from CoreUtils except that _ParseEscapedString() takes an optional C string
19693 // containing delimiter characters instead of being limited to one delimiter character. Also, when the function returns
19694 // due to a delimiter, the output pointer is set to the delimiter character instead of the character after the delimiter.
19695 //===========================================================================================================================
19698 _ParseEscapedString(
19699 const char * inSrc
,
19700 const char * inEnd
,
19701 const char * inDelimiters
,
19704 size_t * outCopiedLen
,
19705 size_t * outActualLen
,
19706 const char ** outPtr
)
19710 char * dst
= inBufPtr
;
19711 const char * const lim
= ( inBufLen
> 0 ) ? &inBufPtr
[ inBufLen
- 1 ] : inBufPtr
;
19716 if( !inDelimiters
) inDelimiters
= "";
19717 while( ptr
< inEnd
)
19723 for( del
= inDelimiters
; ( *del
!= '\0' ) && ( c
!= *del
); ++del
) {}
19724 if( *del
!= '\0' ) break;
19728 require_action_quiet( ptr
< inEnd
, exit
, err
= kUnderrunErr
);
19732 if( dst
< lim
) *dst
++ = (char) c
;
19734 if( inBufLen
> 0 ) *dst
= '\0';
19736 if( outCopiedLen
) *outCopiedLen
= (size_t)( dst
- inBufPtr
);
19737 if( outActualLen
) *outActualLen
= len
;
19738 if( outPtr
) *outPtr
= ptr
;
19746 //===========================================================================================================================
19747 // StringToARecordData
19748 //===========================================================================================================================
19750 static OSStatus
StringToARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
19753 uint32_t * addrPtr
;
19754 const size_t addrLen
= sizeof( *addrPtr
);
19757 addrPtr
= (uint32_t *) malloc( addrLen
);
19758 require_action( addrPtr
, exit
, err
= kNoMemoryErr
);
19760 err
= _StringToIPv4Address( inString
, kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
, addrPtr
,
19761 NULL
, NULL
, NULL
, &end
);
19762 if( !err
&& ( *end
!= '\0' ) ) err
= kMalformedErr
;
19763 require_noerr_quiet( err
, exit
);
19765 *addrPtr
= HostToBig32( *addrPtr
);
19767 *outPtr
= (uint8_t *) addrPtr
;
19772 FreeNullSafe( addrPtr
);
19776 //===========================================================================================================================
19777 // StringToAAAARecordData
19778 //===========================================================================================================================
19780 static OSStatus
StringToAAAARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
19784 const size_t addrLen
= 16;
19787 addrPtr
= (uint8_t *) malloc( addrLen
);
19788 require_action( addrPtr
, exit
, err
= kNoMemoryErr
);
19790 err
= _StringToIPv6Address( inString
,
19791 kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
| kStringToIPAddressFlagsNoScope
,
19792 addrPtr
, NULL
, NULL
, NULL
, &end
);
19793 if( !err
&& ( *end
!= '\0' ) ) err
= kMalformedErr
;
19794 require_noerr_quiet( err
, exit
);
19801 FreeNullSafe( addrPtr
);
19805 //===========================================================================================================================
19806 // StringToDomainName
19807 //===========================================================================================================================
19809 static OSStatus
StringToDomainName( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
19815 uint8_t nameBuf
[ kDomainNameLengthMax
];
19817 err
= DomainNameFromString( nameBuf
, inString
, &end
);
19818 require_noerr_quiet( err
, exit
);
19820 nameLen
= (size_t)( end
- nameBuf
);
19821 namePtr
= _memdup( nameBuf
, nameLen
);
19822 require_action( namePtr
, exit
, err
= kNoMemoryErr
);
19826 if( outLen
) *outLen
= nameLen
;
19832 #if( TARGET_OS_DARWIN )
19833 //===========================================================================================================================
19834 // GetDefaultDNSServer
19835 //===========================================================================================================================
19837 static OSStatus
GetDefaultDNSServer( sockaddr_ip
*outAddr
)
19840 dns_config_t
* config
;
19841 struct sockaddr
* addr
;
19844 config
= dns_configuration_copy();
19845 require_action( config
, exit
, err
= kUnknownErr
);
19848 for( i
= 0; i
< config
->n_resolver
; ++i
)
19850 const dns_resolver_t
* const resolver
= config
->resolver
[ i
];
19852 if( !resolver
->domain
&& ( resolver
->n_nameserver
> 0 ) )
19854 addr
= resolver
->nameserver
[ 0 ];
19858 require_action_quiet( addr
, exit
, err
= kNotFoundErr
);
19860 SockAddrCopy( addr
, outAddr
);
19864 if( config
) dns_configuration_free( config
);
19869 //===========================================================================================================================
19870 // GetMDNSMulticastAddrV4
19871 //===========================================================================================================================
19873 static void _MDNSMulticastAddrV4Init( void *inContext
);
19875 static const struct sockaddr
* GetMDNSMulticastAddrV4( void )
19877 static struct sockaddr_in sMDNSMulticastAddrV4
;
19878 static dispatch_once_t sMDNSMulticastAddrV4InitOnce
= 0;
19880 dispatch_once_f( &sMDNSMulticastAddrV4InitOnce
, &sMDNSMulticastAddrV4
, _MDNSMulticastAddrV4Init
);
19881 return( (const struct sockaddr
*) &sMDNSMulticastAddrV4
);
19884 static void _MDNSMulticastAddrV4Init( void *inContext
)
19886 struct sockaddr_in
* const addr
= (struct sockaddr_in
*) inContext
;
19888 memset( addr
, 0, sizeof( *addr
) );
19889 SIN_LEN_SET( addr
);
19890 addr
->sin_family
= AF_INET
;
19891 addr
->sin_port
= htons( kMDNSPort
);
19892 addr
->sin_addr
.s_addr
= htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251
19895 //===========================================================================================================================
19896 // GetMDNSMulticastAddrV6
19897 //===========================================================================================================================
19899 static void _MDNSMulticastAddrV6Init( void *inContext
);
19901 static const struct sockaddr
* GetMDNSMulticastAddrV6( void )
19903 static struct sockaddr_in6 sMDNSMulticastAddrV6
;
19904 static dispatch_once_t sMDNSMulticastAddrV6InitOnce
= 0;
19906 dispatch_once_f( &sMDNSMulticastAddrV6InitOnce
, &sMDNSMulticastAddrV6
, _MDNSMulticastAddrV6Init
);
19907 return( (const struct sockaddr
*) &sMDNSMulticastAddrV6
);
19910 static void _MDNSMulticastAddrV6Init( void *inContext
)
19912 struct sockaddr_in6
* const addr
= (struct sockaddr_in6
*) inContext
;
19914 memset( addr
, 0, sizeof( *addr
) );
19915 SIN6_LEN_SET( addr
);
19916 addr
->sin6_family
= AF_INET6
;
19917 addr
->sin6_port
= htons( kMDNSPort
);
19918 addr
->sin6_addr
.s6_addr
[ 0 ] = 0xFF; // The mDNS IPv6 multicast address is FF02::FB.
19919 addr
->sin6_addr
.s6_addr
[ 1 ] = 0x02;
19920 addr
->sin6_addr
.s6_addr
[ 15 ] = 0xFB;
19923 //===========================================================================================================================
19924 // CreateMulticastSocket
19925 //===========================================================================================================================
19928 CreateMulticastSocket(
19929 const struct sockaddr
* inAddr
,
19931 const char * inIfName
,
19932 uint32_t inIfIndex
,
19935 SocketRef
* outSock
)
19938 SocketRef sock
= kInvalidSocketRef
;
19939 const int family
= inAddr
->sa_family
;
19942 require_action_quiet( ( family
== AF_INET
) ||( family
== AF_INET6
), exit
, err
= kUnsupportedErr
);
19944 err
= ServerSocketOpen( family
, SOCK_DGRAM
, IPPROTO_UDP
, inPort
, &port
, kSocketBufferSize_DontSet
, &sock
);
19945 require_noerr_quiet( err
, exit
);
19947 err
= SocketSetMulticastInterface( sock
, inIfName
, inIfIndex
);
19948 require_noerr_quiet( err
, exit
);
19950 if( family
== AF_INET
)
19952 err
= setsockopt( sock
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *) &(uint8_t){ 1 }, (socklen_t
) sizeof( uint8_t ) );
19953 err
= map_socket_noerr_errno( sock
, err
);
19954 require_noerr_quiet( err
, exit
);
19958 err
= setsockopt( sock
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *) &(int){ 1 }, (socklen_t
) sizeof( int ) );
19959 err
= map_socket_noerr_errno( sock
, err
);
19960 require_noerr_quiet( err
, exit
);
19965 err
= SocketJoinMulticast( sock
, inAddr
, inIfName
, inIfIndex
);
19966 require_noerr_quiet( err
, exit
);
19969 if( outPort
) *outPort
= port
;
19971 sock
= kInvalidSocketRef
;
19974 ForgetSocket( &sock
);
19978 //===========================================================================================================================
19979 // DecimalTextToUInt32
19980 //===========================================================================================================================
19982 static OSStatus
DecimalTextToUInt32( const char *inSrc
, const char *inEnd
, uint32_t *outValue
, const char **outPtr
)
19986 const char * ptr
= inSrc
;
19988 require_action_quiet( ( ptr
< inEnd
) && isdigit_safe( *ptr
), exit
, err
= kMalformedErr
);
19990 value
= (uint64_t)( *ptr
++ - '0' );
19993 if( ( ptr
< inEnd
) && isdigit_safe( *ptr
) )
19995 err
= kMalformedErr
;
20001 while( ( ptr
< inEnd
) && isdigit_safe( *ptr
) )
20003 value
= ( value
* 10 ) + (uint64_t)( *ptr
++ - '0' );
20004 require_action_quiet( value
<= UINT32_MAX
, exit
, err
= kRangeErr
);
20008 *outValue
= (uint32_t) value
;
20009 if( outPtr
) *outPtr
= ptr
;
20016 //===========================================================================================================================
20017 // CheckIntegerArgument
20018 //===========================================================================================================================
20020 static OSStatus
CheckIntegerArgument( int inArgValue
, const char *inArgName
, int inMin
, int inMax
)
20022 if( ( inArgValue
>= inMin
) && ( inArgValue
<= inMax
) ) return( kNoErr
);
20024 FPrintF( stderr
, "error: Invalid %s: %d. Valid range is [%d, %d].\n", inArgName
, inArgValue
, inMin
, inMax
);
20025 return( kRangeErr
);
20028 //===========================================================================================================================
20029 // CheckDoubleArgument
20030 //===========================================================================================================================
20032 static OSStatus
CheckDoubleArgument( double inArgValue
, const char *inArgName
, double inMin
, double inMax
)
20034 if( ( inArgValue
>= inMin
) && ( inArgValue
<= inMax
) ) return( kNoErr
);
20036 FPrintF( stderr
, "error: Invalid %s: %.1f. Valid range is [%.1f, %.1f].\n", inArgName
, inArgValue
, inMin
, inMax
);
20037 return( kRangeErr
);
20040 //===========================================================================================================================
20042 //===========================================================================================================================
20044 static OSStatus
CheckRootUser( void )
20046 if( geteuid() == 0 ) return( kNoErr
);
20048 FPrintF( stderr
, "error: This command must to be run as root.\n" );
20049 return( kPermissionErr
);
20052 //===========================================================================================================================
20055 // Note: Based on systemf() from CoreUtils framework.
20056 //===========================================================================================================================
20058 extern char ** environ
;
20060 static OSStatus
SpawnCommand( pid_t
*outPID
, const char *inFormat
, ... )
20069 va_start( args
, inFormat
);
20070 VASPrintF( &command
, inFormat
, args
);
20072 require_action( command
, exit
, err
= kUnknownErr
);
20074 argv
[ 0 ] = "/bin/sh";
20076 argv
[ 2 ] = command
;
20078 err
= posix_spawn( &pid
, argv
[ 0 ], NULL
, NULL
, argv
, environ
);
20080 require_noerr_quiet( err
, exit
);
20082 if( outPID
) *outPID
= pid
;
20088 //===========================================================================================================================
20089 // OutputFormatFromArgString
20090 //===========================================================================================================================
20092 static OSStatus
OutputFormatFromArgString( const char *inArgString
, OutputFormatType
*outFormat
)
20095 OutputFormatType format
;
20097 format
= (OutputFormatType
) CLIArgToValue( "format", inArgString
, &err
,
20098 kOutputFormatStr_JSON
, kOutputFormatType_JSON
,
20099 kOutputFormatStr_XML
, kOutputFormatType_XML
,
20100 kOutputFormatStr_Binary
, kOutputFormatType_Binary
,
20102 if( outFormat
) *outFormat
= format
;
20106 //===========================================================================================================================
20107 // OutputPropertyList
20108 //===========================================================================================================================
20110 static OSStatus
OutputPropertyList( CFPropertyListRef inPList
, OutputFormatType inType
, const char *inOutputFilePath
)
20113 CFDataRef results
= NULL
;
20114 FILE * file
= NULL
;
20116 // Convert plist to a specific format.
20120 case kOutputFormatType_JSON
:
20121 results
= CFCreateJSONData( inPList
, kJSONFlags_None
, NULL
);
20122 require_action( results
, exit
, err
= kUnknownErr
);
20125 case kOutputFormatType_XML
:
20126 results
= CFPropertyListCreateData( NULL
, inPList
, kCFPropertyListXMLFormat_v1_0
, 0, NULL
);
20127 require_action( results
, exit
, err
= kUnknownErr
);
20130 case kOutputFormatType_Binary
:
20131 results
= CFPropertyListCreateData( NULL
, inPList
, kCFPropertyListBinaryFormat_v1_0
, 0, NULL
);
20132 require_action( results
, exit
, err
= kUnknownErr
);
20140 // Write formatted results to file or stdout.
20142 if( inOutputFilePath
)
20144 file
= fopen( inOutputFilePath
, "wb" );
20145 err
= map_global_value_errno( file
, file
);
20146 require_noerr( err
, exit
);
20153 err
= WriteANSIFile( file
, CFDataGetBytePtr( results
), (size_t) CFDataGetLength( results
) );
20154 require_noerr_quiet( err
, exit
);
20156 // Write a trailing newline for JSON-formatted results.
20158 if( inType
== kOutputFormatType_JSON
)
20160 err
= WriteANSIFile( file
, "\n", 1 );
20161 require_noerr_quiet( err
, exit
);
20165 if( file
&& ( file
!= stdout
) ) fclose( file
);
20166 CFReleaseNullSafe( results
);
20170 //===========================================================================================================================
20171 // CreateSRVRecordDataFromString
20172 //===========================================================================================================================
20174 static OSStatus
CreateSRVRecordDataFromString( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
20177 DataBuffer dataBuf
;
20181 uint8_t target
[ kDomainNameLengthMax
];
20183 DataBuffer_Init( &dataBuf
, NULL
, 0, ( 3 * 2 ) + kDomainNameLengthMax
);
20185 // Parse and set the priority, weight, and port values (all three are unsigned 16-bit values).
20188 for( i
= 0; i
< 3; ++i
)
20194 value
= strtol( ptr
, &next
, 0 );
20195 require_action_quiet( ( next
!= ptr
) && ( *next
== ',' ), exit
, err
= kMalformedErr
);
20196 require_action_quiet( ( value
>= 0 ) && ( value
<= UINT16_MAX
), exit
, err
= kRangeErr
);
20199 WriteBig16( buf
, value
);
20201 err
= DataBuffer_Append( &dataBuf
, buf
, sizeof( buf
) );
20202 require_noerr( err
, exit
);
20205 // Set the target domain name.
20207 err
= DomainNameFromString( target
, ptr
, &end
);
20208 require_noerr_quiet( err
, exit
);
20210 err
= DataBuffer_Append( &dataBuf
, target
, (size_t)( end
- target
) );
20211 require_noerr( err
, exit
);
20213 err
= DataBuffer_Detach( &dataBuf
, outPtr
, outLen
);
20214 require_noerr( err
, exit
);
20217 DataBuffer_Free( &dataBuf
);
20221 //===========================================================================================================================
20222 // CreateTXTRecordDataFromString
20223 //===========================================================================================================================
20225 static OSStatus
CreateTXTRecordDataFromString(const char *inString
, int inDelimiter
, uint8_t **outPtr
, size_t *outLen
)
20228 DataBuffer dataBuf
;
20230 uint8_t txtStr
[ 256 ]; // Buffer for single TXT string: 1 length byte + up to 255 bytes of data.
20232 DataBuffer_Init( &dataBuf
, NULL
, 0, kDNSRecordDataLengthMax
);
20237 uint8_t * dst
= &txtStr
[ 1 ];
20238 const uint8_t * const lim
= &txtStr
[ 256 ];
20241 while( *src
&& ( *src
!= inDelimiter
) )
20243 if( ( c
= *src
++ ) == '\\' )
20245 require_action_quiet( *src
!= '\0', exit
, err
= kUnderrunErr
);
20248 require_action_quiet( dst
< lim
, exit
, err
= kOverrunErr
);
20249 *dst
++ = (uint8_t) c
;
20251 txtStr
[ 0 ] = (uint8_t)( dst
- &txtStr
[ 1 ] );
20252 err
= DataBuffer_Append( &dataBuf
, txtStr
, 1 + txtStr
[ 0 ] );
20253 require_noerr( err
, exit
);
20255 if( *src
== '\0' ) break;
20259 err
= DataBuffer_Detach( &dataBuf
, outPtr
, outLen
);
20260 require_noerr( err
, exit
);
20263 DataBuffer_Free( &dataBuf
);
20267 //===========================================================================================================================
20268 // CreateNSECRecordData
20269 //===========================================================================================================================
20271 DECLARE_QSORT_NUMERIC_COMPARATOR( _QSortCmpUnsigned
);
20272 DEFINE_QSORT_NUMERIC_COMPARATOR( unsigned int, _QSortCmpUnsigned
)
20274 #define kNSECBitmapMaxLength 32 // 32 bytes (256 bits). See <https://tools.ietf.org/html/rfc4034#section-4.1.2>.
20277 CreateNSECRecordData(
20278 const uint8_t * inNextDomainName
,
20281 unsigned int inTypeCount
,
20286 DataBuffer rdataDB
;
20287 unsigned int * array
= NULL
;
20288 unsigned int i
, type
, maxBit
, currBlock
, bitmapLen
;
20289 uint8_t fields
[ 2 + kNSECBitmapMaxLength
];
20290 uint8_t * const bitmap
= &fields
[ 2 ];
20292 va_start( args
, inTypeCount
);
20293 DataBuffer_Init( &rdataDB
, NULL
, 0, kDNSRecordDataLengthMax
);
20295 // Append Next Domain Name.
20297 err
= DataBuffer_Append( &rdataDB
, inNextDomainName
, DomainNameLength( inNextDomainName
) );
20298 require_noerr( err
, exit
);
20300 // Append Type Bit Maps.
20303 memset( bitmap
, 0, kNSECBitmapMaxLength
);
20304 if( inTypeCount
> 0 )
20306 array
= (unsigned int *) malloc( inTypeCount
* sizeof_element( array
) );
20307 require_action( array
, exit
, err
= kNoMemoryErr
);
20309 for( i
= 0; i
< inTypeCount
; ++i
)
20311 type
= va_arg( args
, unsigned int );
20312 require_action_quiet( type
<= UINT16_MAX
, exit
, err
= kRangeErr
);
20315 qsort( array
, inTypeCount
, sizeof_element( array
), _QSortCmpUnsigned
);
20317 currBlock
= array
[ 0 ] / 256;
20318 for( i
= 0; i
< inTypeCount
; ++i
)
20320 const unsigned int block
= array
[ i
] / 256;
20321 const unsigned int bit
= array
[ i
] % 256;
20323 if( block
!= currBlock
)
20325 bitmapLen
= BitArray_MaxBytes( maxBit
+ 1 );
20326 fields
[ 0 ] = (uint8_t) currBlock
;
20327 fields
[ 1 ] = (uint8_t) bitmapLen
;
20329 err
= DataBuffer_Append( &rdataDB
, fields
, 2 + bitmapLen
);
20330 require_noerr( err
, exit
);
20334 memset( bitmap
, 0, bitmapLen
);
20336 BitArray_SetBit( bitmap
, bit
);
20337 if( bit
> maxBit
) maxBit
= bit
;
20345 bitmapLen
= BitArray_MaxBytes( maxBit
+ 1 );
20346 fields
[ 0 ] = (uint8_t) currBlock
;
20347 fields
[ 1 ] = (uint8_t) bitmapLen
;
20349 err
= DataBuffer_Append( &rdataDB
, fields
, 2 + bitmapLen
);
20350 require_noerr( err
, exit
);
20352 err
= DataBuffer_Detach( &rdataDB
, outPtr
, outLen
);
20353 require_noerr( err
, exit
);
20357 DataBuffer_Free( &rdataDB
);
20358 FreeNullSafe( array
);
20362 //===========================================================================================================================
20364 //===========================================================================================================================
20367 _AppendSOARecordData(
20369 const uint8_t * inMName
,
20370 const uint8_t * inRName
,
20372 uint32_t inRefresh
,
20375 uint32_t inMinimumTTL
,
20381 const uint8_t * inNamePtr
,
20386 const uint8_t * inMName
,
20387 const uint8_t * inRName
,
20389 uint32_t inRefresh
,
20392 uint32_t inMinimumTTL
,
20397 size_t rdlengthOffset
= 0;
20398 uint8_t * rdlengthPtr
;
20402 err
= _DataBuffer_AppendDNSRecord( inDB
, inNamePtr
, inNameLen
, inType
, inClass
, inTTL
, NULL
, 0 );
20403 require_noerr( err
, exit
);
20405 rdlengthOffset
= DataBuffer_GetLen( inDB
) - 2;
20408 err
= _AppendSOARecordData( inDB
, inMName
, inRName
, inSerial
, inRefresh
, inRetry
, inExpire
, inMinimumTTL
, &rdataLen
);
20409 require_noerr( err
, exit
);
20413 rdlengthPtr
= DataBuffer_GetPtr( inDB
) + rdlengthOffset
;
20414 WriteBig16( rdlengthPtr
, rdataLen
);
20417 if( outLen
) *outLen
= inNameLen
+ sizeof( dns_fixed_fields_record
) + rdataLen
;
20425 _AppendSOARecordData(
20427 const uint8_t * inMName
,
20428 const uint8_t * inRName
,
20430 uint32_t inRefresh
,
20433 uint32_t inMinimumTTL
,
20437 dns_fixed_fields_soa fields
;
20438 const size_t mnameLen
= DomainNameLength( inMName
);
20439 const size_t rnameLen
= DomainNameLength( inRName
);
20443 err
= DataBuffer_Append( inDB
, inMName
, mnameLen
);
20444 require_noerr( err
, exit
);
20446 err
= DataBuffer_Append( inDB
, inRName
, rnameLen
);
20447 require_noerr( err
, exit
);
20449 dns_fixed_fields_soa_init( &fields
, inSerial
, inRefresh
, inRetry
, inExpire
, inMinimumTTL
);
20450 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
20451 require_noerr( err
, exit
);
20453 if( outLen
) *outLen
= mnameLen
+ rnameLen
+ sizeof( fields
);
20460 //===========================================================================================================================
20461 // CreateSOARecordData
20462 //===========================================================================================================================
20465 CreateSOARecordData(
20466 const uint8_t * inMName
,
20467 const uint8_t * inRName
,
20469 uint32_t inRefresh
,
20472 uint32_t inMinimumTTL
,
20477 DataBuffer rdataDB
;
20479 DataBuffer_Init( &rdataDB
, NULL
, 0, kDNSRecordDataLengthMax
);
20481 err
= _AppendSOARecordData( &rdataDB
, inMName
, inRName
, inSerial
, inRefresh
, inRetry
, inExpire
, inMinimumTTL
, NULL
);
20482 require_noerr( err
, exit
);
20484 err
= DataBuffer_Detach( &rdataDB
, outPtr
, outLen
);
20485 require_noerr( err
, exit
);
20488 DataBuffer_Free( &rdataDB
);
20492 //===========================================================================================================================
20493 // _DataBuffer_AppendDNSQuestion
20494 //===========================================================================================================================
20497 _DataBuffer_AppendDNSQuestion(
20499 const uint8_t * inNamePtr
,
20505 dns_fixed_fields_question fields
;
20507 err
= DataBuffer_Append( inDB
, inNamePtr
, inNameLen
);
20508 require_noerr( err
, exit
);
20510 dns_fixed_fields_question_init( &fields
, inType
, inClass
);
20511 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
20512 require_noerr( err
, exit
);
20518 //===========================================================================================================================
20519 // _DataBuffer_AppendDNSRecord
20520 //===========================================================================================================================
20523 _DataBuffer_AppendDNSRecord(
20525 const uint8_t * inNamePtr
,
20530 const uint8_t * inRDataPtr
,
20531 size_t inRDataLen
)
20534 dns_fixed_fields_record fields
;
20536 require_action_quiet( inRDataLen
< kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
20538 err
= DataBuffer_Append( inDB
, inNamePtr
, inNameLen
);
20539 require_noerr( err
, exit
);
20541 dns_fixed_fields_record_init( &fields
, inType
, inClass
, inTTL
, (uint16_t) inRDataLen
);
20542 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
20543 require_noerr( err
, exit
);
20547 err
= DataBuffer_Append( inDB
, inRDataPtr
, inRDataLen
);
20548 require_noerr( err
, exit
);
20555 //===========================================================================================================================
20556 // _NanoTime64ToTimestamp
20557 //===========================================================================================================================
20559 static char * _NanoTime64ToTimestamp( NanoTime64 inTime
, char *inBuf
, size_t inMaxLen
)
20563 NanoTimeToTimeVal( inTime
, &tv
);
20564 return( MakeFractionalDateString( &tv
, inBuf
, inMaxLen
) );
20567 //===========================================================================================================================
20568 // _MDNSInterfaceListCreate
20569 //===========================================================================================================================
20571 static Boolean
_MDNSInterfaceIsBlacklisted( SocketRef inInfoSock
, const char *inIfName
);
20573 static OSStatus
_MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset
, size_t inItemSize
, MDNSInterfaceItem
**outList
)
20576 struct ifaddrs
* ifaList
;
20577 const struct ifaddrs
* ifa
;
20578 MDNSInterfaceItem
* interfaceList
;
20579 MDNSInterfaceItem
** ptr
;
20580 SocketRef infoSock
;
20583 interfaceList
= NULL
;
20584 infoSock
= kInvalidSocketRef
;
20585 if( inItemSize
== 0 ) inItemSize
= sizeof( MDNSInterfaceItem
);
20586 require_action_quiet( inItemSize
>= sizeof( MDNSInterfaceItem
), exit
, err
= kSizeErr
);
20588 infoSock
= socket( AF_INET
, SOCK_DGRAM
, 0 );
20589 err
= map_socket_creation_errno( infoSock
);
20590 require_noerr( err
, exit
);
20592 err
= getifaddrs( &ifaList
);
20593 err
= map_global_noerr_errno( err
);
20594 require_noerr( err
, exit
);
20596 ptr
= &interfaceList
;
20597 for( ifa
= ifaList
; ifa
; ifa
= ifa
->ifa_next
)
20599 MDNSInterfaceItem
* item
;
20601 const unsigned int flagsMask
= IFF_UP
| IFF_MULTICAST
| IFF_POINTOPOINT
;
20602 const unsigned int flagsNeeded
= IFF_UP
| IFF_MULTICAST
;
20604 if( ( ifa
->ifa_flags
& flagsMask
) != flagsNeeded
) continue;
20605 if( !ifa
->ifa_addr
|| !ifa
->ifa_name
) continue;
20606 family
= ifa
->ifa_addr
->sa_family
;
20607 if( ( family
!= AF_INET
) && ( family
!= AF_INET6
) ) continue;
20609 for( item
= interfaceList
; item
&& ( strcmp( item
->ifName
, ifa
->ifa_name
) != 0 ); item
= item
->next
) {}
20612 NetTransportType type
;
20614 const char * const ifName
= ifa
->ifa_name
;
20616 if( _MDNSInterfaceIsBlacklisted( infoSock
, ifName
) ) continue;
20617 err
= SocketGetInterfaceInfo( infoSock
, ifName
, NULL
, &ifIndex
, NULL
, NULL
, NULL
, NULL
, NULL
, &type
);
20618 require_noerr( err
, exit
);
20620 if( ifIndex
== 0 ) continue;
20621 if( type
== kNetTransportType_AWDL
)
20623 if( inSubset
== kMDNSInterfaceSubset_NonAWDL
) continue;
20627 if( inSubset
== kMDNSInterfaceSubset_AWDL
) continue;
20629 item
= (MDNSInterfaceItem
*) calloc( 1, inItemSize
);
20630 require_action( item
, exit
, err
= kNoMemoryErr
);
20635 item
->ifName
= strdup( ifName
);
20636 require_action( item
->ifName
, exit
, err
= kNoMemoryErr
);
20638 item
->ifIndex
= ifIndex
;
20639 if( type
== kNetTransportType_AWDL
) item
->isAWDL
= true;
20640 else if( type
== kNetTransportType_WiFi
) item
->isWiFi
= true;
20642 if( family
== AF_INET
) item
->hasIPv4
= true;
20643 else item
->hasIPv6
= true;
20645 require_action_quiet( interfaceList
, exit
, err
= kNotFoundErr
);
20649 *outList
= interfaceList
;
20650 interfaceList
= NULL
;
20654 if( ifaList
) freeifaddrs( ifaList
);
20655 _MDNSInterfaceListFree( interfaceList
);
20656 ForgetSocket( &infoSock
);
20660 static Boolean
_MDNSInterfaceIsBlacklisted( SocketRef inInfoSock
, const char *inIfName
)
20664 static const char * const kMDNSInterfacePrefixBlacklist
[] = { "llw" };
20667 // Check if the interface name's prefix matches the prefix blacklist.
20669 for( i
= 0; i
< (int) countof( kMDNSInterfacePrefixBlacklist
); ++i
)
20671 const char * const prefix
= kMDNSInterfacePrefixBlacklist
[ i
];
20673 if( strcmp_prefix( inIfName
, prefix
) == 0 )
20675 const char * ptr
= &inIfName
[ strlen( prefix
) ];
20677 while( isdigit_safe( *ptr
) ) ++ptr
;
20678 if( *ptr
== '\0' ) return( true );
20682 // Check if the interface is used for inter-(co)processor networking.
20684 memset( &ifr
, 0, sizeof( ifr
) );
20685 strlcpy( ifr
.ifr_name
, inIfName
, sizeof( ifr
.ifr_name
) );
20686 err
= ioctl( inInfoSock
, SIOCGIFFUNCTIONALTYPE
, &ifr
);
20687 err
= map_global_value_errno( err
!= -1, err
);
20688 if( !err
&& ( ifr
.ifr_functional_type
== IFRTYPE_FUNCTIONAL_INTCOPROC
) ) return( true );
20693 //===========================================================================================================================
20694 // _MDNSInterfaceListFree
20695 //===========================================================================================================================
20697 static void _MDNSInterfaceListFree( MDNSInterfaceItem
*inList
)
20699 MDNSInterfaceItem
* item
;
20701 while( ( item
= inList
) != NULL
)
20703 inList
= item
->next
;
20704 FreeNullSafe( item
->ifName
);
20709 //===========================================================================================================================
20710 // _MDNSInterfaceGetAny
20711 //===========================================================================================================================
20713 static OSStatus
_MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset
, char inNameBuf
[ IF_NAMESIZE
+ 1 ], uint32_t *outIndex
)
20716 MDNSInterfaceItem
* list
;
20717 const MDNSInterfaceItem
* item
;
20720 err
= _MDNSInterfaceListCreate( inSubset
, 0, &list
);
20721 require_noerr_quiet( err
, exit
);
20722 require_action_quiet( list
, exit
, err
= kNotFoundErr
);
20724 for( item
= list
; item
; item
= item
->next
)
20726 if( item
->hasIPv4
&& item
->hasIPv6
) break;
20728 if( !item
) item
= list
;
20729 if( inNameBuf
) strlcpy( inNameBuf
, item
->ifName
, IF_NAMESIZE
+ 1 );
20730 if( outIndex
) *outIndex
= item
->ifIndex
;
20733 _MDNSInterfaceListFree( list
);
20737 //===========================================================================================================================
20738 // _SetComputerName
20739 //===========================================================================================================================
20741 static OSStatus
_SetComputerName( CFStringRef inComputerName
, CFStringEncoding inEncoding
)
20744 SCPreferencesRef prefs
;
20747 prefs
= SCPreferencesCreateWithAuthorization( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
20748 err
= map_scerror( prefs
);
20749 require_noerr_quiet( err
, exit
);
20751 ok
= SCPreferencesSetComputerName( prefs
, inComputerName
, inEncoding
);
20752 err
= map_scerror( ok
);
20753 require_noerr_quiet( err
, exit
);
20755 ok
= SCPreferencesCommitChanges( prefs
);
20756 err
= map_scerror( ok
);
20757 require_noerr_quiet( err
, exit
);
20759 ok
= SCPreferencesApplyChanges( prefs
);
20760 err
= map_scerror( ok
);
20761 require_noerr_quiet( err
, exit
);
20764 CFReleaseNullSafe( prefs
);
20768 //===========================================================================================================================
20769 // _SetComputerNameWithUTF8CString
20770 //===========================================================================================================================
20772 static OSStatus
_SetComputerNameWithUTF8CString( const char *inComputerName
)
20775 CFStringRef computerName
;
20777 computerName
= CFStringCreateWithCString( NULL
, inComputerName
, kCFStringEncodingUTF8
);
20778 require_action( computerName
, exit
, err
= kNoMemoryErr
);
20780 err
= _SetComputerName( computerName
, kCFStringEncodingUTF8
);
20781 require_noerr_quiet( err
, exit
);
20784 CFReleaseNullSafe( computerName
);
20788 //===========================================================================================================================
20789 // _SetLocalHostName
20790 //===========================================================================================================================
20792 static OSStatus
_SetLocalHostName( CFStringRef inLocalHostName
)
20795 SCPreferencesRef prefs
;
20798 prefs
= SCPreferencesCreateWithAuthorization( NULL
, CFSTR( kDNSSDUtilIdentifier
), NULL
, NULL
);
20799 err
= map_scerror( prefs
);
20800 require_noerr_quiet( err
, exit
);
20802 ok
= SCPreferencesSetLocalHostName( prefs
, inLocalHostName
);
20803 err
= map_scerror( ok
);
20804 require_noerr_quiet( err
, exit
);
20806 ok
= SCPreferencesCommitChanges( prefs
);
20807 err
= map_scerror( ok
);
20808 require_noerr_quiet( err
, exit
);
20810 ok
= SCPreferencesApplyChanges( prefs
);
20811 err
= map_scerror( ok
);
20812 require_noerr_quiet( err
, exit
);
20815 CFReleaseNullSafe( prefs
);
20819 //===========================================================================================================================
20820 // _SetLocalHostNameWithUTF8CString
20821 //===========================================================================================================================
20823 static OSStatus
_SetLocalHostNameWithUTF8CString( const char *inLocalHostName
)
20826 CFStringRef localHostName
;
20828 localHostName
= CFStringCreateWithCString( NULL
, inLocalHostName
, kCFStringEncodingUTF8
);
20829 require_action( localHostName
, exit
, err
= kNoMemoryErr
);
20831 err
= _SetLocalHostName( localHostName
);
20832 require_noerr_quiet( err
, exit
);
20835 CFReleaseNullSafe( localHostName
);
20839 //===========================================================================================================================
20840 // MDNSColliderCreate
20841 //===========================================================================================================================
20845 kMDNSColliderOpCode_Invalid
= 0,
20846 kMDNSColliderOpCode_Send
= 1,
20847 kMDNSColliderOpCode_Wait
= 2,
20848 kMDNSColliderOpCode_SetProbeActions
= 3,
20849 kMDNSColliderOpCode_LoopPush
= 4,
20850 kMDNSColliderOpCode_LoopPop
= 5,
20851 kMDNSColliderOpCode_Exit
= 6
20853 } MDNSColliderOpCode
;
20857 MDNSColliderOpCode opcode
;
20860 } MDNSCInstruction
;
20862 #define kMaxLoopDepth 16
20864 struct MDNSColliderPrivate
20866 CFRuntimeBase base
; // CF object base.
20867 dispatch_queue_t queue
; // Queue for collider's events.
20868 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
20869 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
20870 SocketRef sockV4
; // IPv4 UDP socket for mDNS.
20871 SocketRef sockV6
; // IPv6 UDP socket for mDNS.
20872 uint8_t * target
; // Record name being targeted. (malloced)
20873 uint8_t * responsePtr
; // Response message pointer. (malloced)
20874 size_t responseLen
; // Response message length.
20875 uint8_t * probePtr
; // Probe query message pointer. (malloced)
20876 size_t probeLen
; // Probe query message length.
20877 unsigned int probeCount
; // Count of probe queries received for collider's record.
20878 uint32_t probeActionMap
; // Bitmap of actions to take for
20879 MDNSCInstruction
* program
; // Program to execute.
20880 uint32_t pc
; // Program's program counter.
20881 uint32_t loopCounts
[ kMaxLoopDepth
]; // Stack of loop counters.
20882 uint32_t loopDepth
; // Current loop depth.
20883 dispatch_source_t waitTimer
; // Timer for program's wait commands.
20884 uint32_t interfaceIndex
; // Interface over which to send and receive mDNS msgs.
20885 MDNSColliderStopHandler_f stopHandler
; // User's stop handler.
20886 void * stopContext
; // User's stop handler context.
20887 MDNSColliderProtocols protocols
; // Protocols to use, i.e., IPv4, IPv6.
20888 Boolean stopped
; // True if the collider has been stopped.
20889 uint8_t msgBuf
[ kMDNSMessageSizeMax
]; // mDNS message buffer.
20892 static void _MDNSColliderStop( MDNSColliderRef inCollider
, OSStatus inError
);
20893 static void _MDNSColliderReadHandler( void *inContext
);
20894 static void _MDNSColliderExecuteProgram( void *inContext
);
20895 static OSStatus
_MDNSColliderSendResponse( MDNSColliderRef inCollider
, SocketRef inSock
, const struct sockaddr
*inDest
);
20896 static OSStatus
_MDNSColliderSendProbe( MDNSColliderRef inCollider
, SocketRef inSock
, const struct sockaddr
*inDest
);
20898 CF_CLASS_DEFINE( MDNSCollider
);
20900 ulog_define_ex( kDNSSDUtilIdentifier
, MDNSCollider
, kLogLevelInfo
, kLogFlags_None
, "MDNSCollider", NULL
);
20901 #define mc_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSCollider ), (LEVEL), __VA_ARGS__ )
20903 static OSStatus
MDNSColliderCreate( dispatch_queue_t inQueue
, MDNSColliderRef
*outCollider
)
20906 MDNSColliderRef obj
= NULL
;
20908 CF_OBJECT_CREATE( MDNSCollider
, obj
, err
, exit
);
20910 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
20911 obj
->sockV4
= kInvalidSocketRef
;
20912 obj
->sockV6
= kInvalidSocketRef
;
20914 *outCollider
= obj
;
20921 //===========================================================================================================================
20922 // _MDNSColliderFinalize
20923 //===========================================================================================================================
20925 static void _MDNSColliderFinalize( CFTypeRef inObj
)
20927 MDNSColliderRef
const me
= (MDNSColliderRef
) inObj
;
20929 check( !me
->waitTimer
);
20930 check( !me
->readSourceV4
);
20931 check( !me
->readSourceV6
);
20932 check( !IsValidSocket( me
->sockV4
) );
20933 check( !IsValidSocket( me
->sockV6
) );
20934 ForgetMem( &me
->target
);
20935 ForgetMem( &me
->responsePtr
);
20936 ForgetMem( &me
->probePtr
);
20937 ForgetMem( &me
->program
);
20938 dispatch_forget( &me
->queue
);
20941 //===========================================================================================================================
20942 // MDNSColliderStart
20943 //===========================================================================================================================
20945 static void _MDNSColliderStart( void *inContext
);
20947 static OSStatus
MDNSColliderStart( MDNSColliderRef me
)
20951 require_action_quiet( me
->target
, exit
, err
= kNotPreparedErr
);
20952 require_action_quiet( me
->responsePtr
, exit
, err
= kNotPreparedErr
);
20953 require_action_quiet( me
->probePtr
, exit
, err
= kNotPreparedErr
);
20954 require_action_quiet( me
->program
, exit
, err
= kNotPreparedErr
);
20955 require_action_quiet( me
->interfaceIndex
, exit
, err
= kNotPreparedErr
);
20956 require_action_quiet( me
->protocols
, exit
, err
= kNotPreparedErr
);
20959 dispatch_async_f( me
->queue
, me
, _MDNSColliderStart
);
20966 static void _MDNSColliderStart( void *inContext
)
20969 MDNSColliderRef
const me
= (MDNSColliderRef
) inContext
;
20970 SocketRef sock
= kInvalidSocketRef
;
20971 SocketContext
* sockCtx
= NULL
;
20973 if( me
->protocols
& kMDNSColliderProtocol_IPv4
)
20975 err
= CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort
, NULL
, me
->interfaceIndex
, true, NULL
, &sock
);
20976 require_noerr( err
, exit
);
20978 err
= SocketContextCreate( sock
, me
, &sockCtx
);
20979 require_noerr( err
, exit
);
20980 sock
= kInvalidSocketRef
;
20982 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _MDNSColliderReadHandler
, SocketContextCancelHandler
,
20983 sockCtx
, &me
->readSourceV4
);
20984 require_noerr( err
, exit
);
20985 me
->sockV4
= sockCtx
->sock
;
20988 dispatch_resume( me
->readSourceV4
);
20991 if( me
->protocols
& kMDNSColliderProtocol_IPv6
)
20993 err
= CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort
, NULL
, me
->interfaceIndex
, true, NULL
, &sock
);
20994 require_noerr( err
, exit
);
20996 err
= SocketContextCreate( sock
, me
, &sockCtx
);
20997 require_noerr( err
, exit
);
20998 sock
= kInvalidSocketRef
;
21000 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _MDNSColliderReadHandler
, SocketContextCancelHandler
,
21001 sockCtx
, &me
->readSourceV6
);
21002 require_noerr( err
, exit
);
21003 me
->sockV6
= sockCtx
->sock
;
21006 dispatch_resume( me
->readSourceV6
);
21009 _MDNSColliderExecuteProgram( me
);
21013 ForgetSocket( &sock
);
21014 ForgetSocketContext( &sockCtx
);
21015 if( err
) _MDNSColliderStop( me
, err
);
21018 //===========================================================================================================================
21019 // MDNSColliderStop
21020 //===========================================================================================================================
21022 static void _MDNSColliderUserStop( void *inContext
);
21024 static void MDNSColliderStop( MDNSColliderRef me
)
21027 dispatch_async_f( me
->queue
, me
, _MDNSColliderUserStop
);
21030 static void _MDNSColliderUserStop( void *inContext
)
21032 MDNSColliderRef
const me
= (MDNSColliderRef
) inContext
;
21034 _MDNSColliderStop( me
, kCanceledErr
);
21038 //===========================================================================================================================
21039 // MDNSColliderSetProtocols
21040 //===========================================================================================================================
21042 static void MDNSColliderSetProtocols( MDNSColliderRef me
, MDNSColliderProtocols inProtocols
)
21044 me
->protocols
= inProtocols
;
21047 //===========================================================================================================================
21048 // MDNSColliderSetInterfaceIndex
21049 //===========================================================================================================================
21051 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef me
, uint32_t inInterfaceIndex
)
21053 me
->interfaceIndex
= inInterfaceIndex
;
21056 //===========================================================================================================================
21057 // MDNSColliderSetProgram
21058 //===========================================================================================================================
21060 #define kMDNSColliderProgCmd_Done "done"
21061 #define kMDNSColliderProgCmd_Loop "loop"
21062 #define kMDNSColliderProgCmd_Send "send"
21063 #define kMDNSColliderProgCmd_Probes "probes"
21064 #define kMDNSColliderProgCmd_Wait "wait"
21066 typedef uint32_t MDNSColliderProbeAction
;
21068 #define kMDNSColliderProbeAction_None 0
21069 #define kMDNSColliderProbeAction_Respond 1
21070 #define kMDNSColliderProbeAction_RespondUnicast 2
21071 #define kMDNSColliderProbeAction_RespondMulticast 3
21072 #define kMDNSColliderProbeAction_Probe 4
21073 #define kMDNSColliderProbeAction_MaxValue kMDNSColliderProbeAction_Probe
21075 #define kMDNSColliderProbeActionBits_Count 3
21076 #define kMDNSColliderProbeActionBits_Mask ( ( 1U << kMDNSColliderProbeActionBits_Count ) - 1 )
21077 #define kMDNSColliderProbeActionMaxProbeCount ( 32 / kMDNSColliderProbeActionBits_Count )
21079 check_compile_time( kMDNSColliderProbeAction_MaxValue
<= kMDNSColliderProbeActionBits_Mask
);
21081 static OSStatus
_MDNSColliderParseProbeActionString( const char *inString
, size_t inLen
, uint32_t *outBitmap
);
21083 static OSStatus
MDNSColliderSetProgram( MDNSColliderRef me
, const char *inProgramStr
)
21087 unsigned int loopDepth
;
21091 MDNSCInstruction
* program
= NULL
;
21092 uint32_t loopStart
[ kMaxLoopDepth
];
21095 for( cmd
= inProgramStr
; *cmd
; cmd
= next
)
21097 for( end
= cmd
; *end
&& ( *end
!= ';' ); ++end
) {}
21098 require_action_quiet( end
!= cmd
, exit
, err
= kMalformedErr
);
21099 next
= ( *end
== ';' ) ? ( end
+ 1 ) : end
;
21103 program
= (MDNSCInstruction
*) calloc( insCount
+ 1, sizeof( *program
) );
21104 require_action( program
, exit
, err
= kNoMemoryErr
);
21108 for( cmd
= inProgramStr
; *cmd
; cmd
= next
)
21115 MDNSCInstruction
* const ins
= &program
[ insCount
];
21117 while( isspace_safe( *cmd
) ) ++cmd
;
21118 for( end
= cmd
; *end
&& ( *end
!= ';' ); ++end
) {}
21119 next
= ( *end
== ';' ) ? ( end
+ 1 ) : end
;
21121 for( ptr
= cmd
; ( ptr
< end
) && !isspace_safe( *ptr
); ++ptr
) {}
21122 cmdLen
= (size_t)( ptr
- cmd
);
21126 if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Done
) == 0 )
21128 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
21129 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
21131 require_action_quiet( loopDepth
> 0, exit
, err
= kMalformedErr
);
21133 ins
->opcode
= kMDNSColliderOpCode_LoopPop
;
21134 ins
->operand
= loopStart
[ --loopDepth
];
21139 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Loop
) == 0 )
21141 for( arg
= ptr
; ( arg
< end
) && isspace_safe( *arg
); ++arg
) {}
21142 err
= DecimalTextToUInt32( arg
, end
, &value
, &ptr
);
21143 require_noerr_quiet( err
, exit
);
21144 require_action_quiet( value
> 0, exit
, err
= kValueErr
);
21146 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
21147 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
21149 ins
->opcode
= kMDNSColliderOpCode_LoopPush
;
21150 ins
->operand
= value
;
21152 require_action_quiet( loopDepth
< kMaxLoopDepth
, exit
, err
= kNoSpaceErr
);
21153 loopStart
[ loopDepth
++ ] = insCount
+ 1;
21158 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Probes
) == 0 )
21160 for( arg
= ptr
; ( arg
< end
) && isspace_safe( *arg
); ++arg
) {}
21161 for( ptr
= arg
; ( ptr
< end
) && !isspace_safe( *ptr
); ++ptr
) {}
21162 argLen
= (size_t)( ptr
- arg
);
21165 err
= _MDNSColliderParseProbeActionString( arg
, argLen
, &value
);
21166 require_noerr_quiet( err
, exit
);
21173 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
21174 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
21176 ins
->opcode
= kMDNSColliderOpCode_SetProbeActions
;
21177 ins
->operand
= value
;
21182 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Send
) == 0 )
21184 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
21185 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
21187 ins
->opcode
= kMDNSColliderOpCode_Send
;
21192 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Wait
) == 0 )
21194 for( arg
= ptr
; ( arg
< end
) && isspace_safe( *arg
); ++arg
) {}
21195 err
= DecimalTextToUInt32( arg
, end
, &value
, &ptr
);
21196 require_noerr_quiet( err
, exit
);
21198 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
21199 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
21201 ins
->opcode
= kMDNSColliderOpCode_Wait
;
21202 ins
->operand
= value
;
21205 // Unrecognized command
21214 require_action_quiet( loopDepth
== 0, exit
, err
= kMalformedErr
);
21216 program
[ insCount
].opcode
= kMDNSColliderOpCode_Exit
;
21218 FreeNullSafe( me
->program
);
21219 me
->program
= program
;
21224 FreeNullSafe( program
);
21228 static OSStatus
_MDNSColliderParseProbeActionString( const char *inString
, size_t inLen
, uint32_t *outBitmap
)
21232 const char * const end
= &inString
[ inLen
];
21242 MDNSColliderProbeAction action
;
21245 if( isdigit_safe( c
) )
21250 count
= ( count
* 10 ) + ( c
- '0' );
21251 require_action_quiet( count
<= ( kMDNSColliderProbeActionMaxProbeCount
- index
), exit
, err
= kCountErr
);
21252 require_action_quiet( ptr
< end
, exit
, err
= kUnderrunErr
);
21255 } while( isdigit_safe( c
) );
21256 require_action_quiet( count
> 0, exit
, err
= kCountErr
);
21260 require_action_quiet( index
< kMDNSColliderProbeActionMaxProbeCount
, exit
, err
= kMalformedErr
);
21266 case 'n': action
= kMDNSColliderProbeAction_None
; break;
21267 case 'r': action
= kMDNSColliderProbeAction_Respond
; break;
21268 case 'u': action
= kMDNSColliderProbeAction_RespondUnicast
; break;
21269 case 'm': action
= kMDNSColliderProbeAction_RespondMulticast
; break;
21270 case 'p': action
= kMDNSColliderProbeAction_Probe
; break;
21271 default: err
= kMalformedErr
; goto exit
;
21276 require_action_quiet( ( c
== '-' ) && ( ptr
< end
), exit
, err
= kMalformedErr
);
21278 while( count
-- > 0 )
21280 bitmap
|= ( action
<< ( index
* kMDNSColliderProbeActionBits_Count
) );
21285 *outBitmap
= bitmap
;
21292 //===========================================================================================================================
21293 // MDNSColliderSetStopHandler
21294 //===========================================================================================================================
21296 static void MDNSColliderSetStopHandler( MDNSColliderRef me
, MDNSColliderStopHandler_f inStopHandler
, void *inStopContext
)
21298 me
->stopHandler
= inStopHandler
;
21299 me
->stopContext
= inStopContext
;
21302 //===========================================================================================================================
21303 // MDNSColliderSetRecord
21304 //===========================================================================================================================
21306 #define kMDNSColliderDummyStr "\x16" "mdnscollider-sent-this" "\x05" "local"
21307 #define kMDNSColliderDummyName ( (const uint8_t *) kMDNSColliderDummyStr )
21308 #define kMDNSColliderDummyNameLen sizeof( kMDNSColliderDummyStr )
21311 MDNSColliderSetRecord(
21312 MDNSColliderRef me
,
21313 const uint8_t * inName
,
21315 const void * inRDataPtr
,
21316 size_t inRDataLen
)
21321 uint8_t * targetPtr
= NULL
;
21323 uint8_t * responsePtr
= NULL
;
21324 size_t responseLen
;
21325 uint8_t * probePtr
= NULL
;
21328 DataBuffer_Init( &msgDB
, NULL
, 0, kMDNSMessageSizeMax
);
21330 err
= DomainNameDup( inName
, &targetPtr
, &targetLen
);
21331 require_noerr_quiet( err
, exit
);
21333 // Create response message.
21335 memset( &header
, 0, sizeof( header
) );
21336 DNSHeaderSetFlags( &header
, kDNSHeaderFlag_Response
| kDNSHeaderFlag_AuthAnswer
);
21337 DNSHeaderSetAnswerCount( &header
, 1 );
21339 err
= DataBuffer_Append( &msgDB
, &header
, sizeof( header
) );
21340 require_noerr( err
, exit
);
21342 err
= _DataBuffer_AppendDNSRecord( &msgDB
, targetPtr
, targetLen
, inType
, kDNSServiceClass_IN
| kRRClassCacheFlushBit
,
21343 1976, inRDataPtr
, inRDataLen
);
21344 require_noerr( err
, exit
);
21346 err
= DataBuffer_Detach( &msgDB
, &responsePtr
, &responseLen
);
21347 require_noerr( err
, exit
);
21349 // Create probe message.
21351 memset( &header
, 0, sizeof( header
) );
21352 DNSHeaderSetQuestionCount( &header
, 2 );
21353 DNSHeaderSetAuthorityCount( &header
, 1 );
21355 err
= DataBuffer_Append( &msgDB
, &header
, sizeof( header
) );
21356 require_noerr( err
, exit
);
21358 err
= _DataBuffer_AppendDNSQuestion( &msgDB
, targetPtr
, targetLen
, kDNSServiceType_ANY
, kDNSServiceClass_IN
);
21359 require_noerr( err
, exit
);
21361 err
= _DataBuffer_AppendDNSQuestion( &msgDB
, kMDNSColliderDummyName
, kMDNSColliderDummyNameLen
,
21362 kDNSServiceType_NULL
, kDNSServiceClass_IN
);
21363 require_noerr( err
, exit
);
21365 err
= _DataBuffer_AppendDNSRecord( &msgDB
, targetPtr
, targetLen
, inType
, kDNSServiceClass_IN
,
21366 1976, inRDataPtr
, inRDataLen
);
21367 require_noerr( err
, exit
);
21369 err
= DataBuffer_Detach( &msgDB
, &probePtr
, &probeLen
);
21370 require_noerr( err
, exit
);
21372 FreeNullSafe( me
->target
);
21373 me
->target
= targetPtr
;
21376 FreeNullSafe( me
->responsePtr
);
21377 me
->responsePtr
= responsePtr
;
21378 me
->responseLen
= responseLen
;
21379 responsePtr
= NULL
;
21381 FreeNullSafe( me
->probePtr
);
21382 me
->probePtr
= probePtr
;
21383 me
->probeLen
= probeLen
;
21387 DataBuffer_Free( &msgDB
);
21388 FreeNullSafe( targetPtr
);
21389 FreeNullSafe( responsePtr
);
21390 FreeNullSafe( probePtr
);
21394 //===========================================================================================================================
21395 // _MDNSColliderStop
21396 //===========================================================================================================================
21398 static void _MDNSColliderStop( MDNSColliderRef me
, OSStatus inError
)
21400 dispatch_source_forget( &me
->waitTimer
);
21401 dispatch_source_forget( &me
->readSourceV4
);
21402 dispatch_source_forget( &me
->readSourceV6
);
21403 me
->sockV4
= kInvalidSocketRef
;
21404 me
->sockV6
= kInvalidSocketRef
;
21408 me
->stopped
= true;
21409 if( me
->stopHandler
) me
->stopHandler( me
->stopContext
, inError
);
21414 //===========================================================================================================================
21415 // _MDNSColliderReadHandler
21416 //===========================================================================================================================
21418 static MDNSColliderProbeAction
_MDNSColliderGetProbeAction( uint32_t inBitmap
, unsigned int inProbeNumber
);
21419 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction
);
21421 static void _MDNSColliderReadHandler( void *inContext
)
21424 struct timeval now
;
21425 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
21426 MDNSColliderRef
const me
= (MDNSColliderRef
) sockCtx
->userContext
;
21428 sockaddr_ip sender
;
21429 const DNSHeader
* hdr
;
21430 const uint8_t * ptr
;
21431 const struct sockaddr
* dest
;
21432 int probeFound
, probeIsQU
;
21433 unsigned int qCount
, i
;
21434 MDNSColliderProbeAction action
;
21436 gettimeofday( &now
, NULL
);
21438 err
= SocketRecvFrom( sockCtx
->sock
, me
->msgBuf
, sizeof( me
->msgBuf
), &msgLen
, &sender
, sizeof( sender
),
21439 NULL
, NULL
, NULL
, NULL
);
21440 require_noerr( err
, exit
);
21442 require_quiet( msgLen
>= kDNSHeaderLength
, exit
);
21443 hdr
= (const DNSHeader
*) me
->msgBuf
;
21445 probeFound
= false;
21447 qCount
= DNSHeaderGetQuestionCount( hdr
);
21448 ptr
= (const uint8_t *) &hdr
[ 1 ];
21449 for( i
= 0; i
< qCount
; ++i
)
21451 uint16_t qtype
, qclass
;
21452 uint8_t qname
[ kDomainNameLengthMax
];
21454 err
= DNSMessageExtractQuestion( me
->msgBuf
, msgLen
, ptr
, qname
, &qtype
, &qclass
, &ptr
);
21455 require_noerr_quiet( err
, exit
);
21457 if( ( qtype
== kDNSServiceType_NULL
) && ( qclass
== kDNSServiceClass_IN
) &&
21458 DomainNameEqual( qname
, kMDNSColliderDummyName
) )
21460 probeFound
= false;
21464 if( qtype
!= kDNSServiceType_ANY
) continue;
21465 if( ( qclass
& ~kQClassUnicastResponseBit
) != kDNSServiceClass_IN
) continue;
21466 if( !DomainNameEqual( qname
, me
->target
) ) continue;
21471 probeIsQU
= ( qclass
& kQClassUnicastResponseBit
) ? true : false;
21474 require_quiet( probeFound
, exit
);
21477 action
= _MDNSColliderGetProbeAction( me
->probeActionMap
, me
->probeCount
);
21479 mc_ulog( kLogLevelInfo
, "Received probe from %##a at %{du:time} (action: %s):\n\n%#1{du:dnsmsg}",
21480 &sender
, &now
, _MDNSColliderProbeActionToString( action
), me
->msgBuf
, msgLen
);
21482 if( ( action
== kMDNSColliderProbeAction_Respond
) ||
21483 ( action
== kMDNSColliderProbeAction_RespondUnicast
) ||
21484 ( action
== kMDNSColliderProbeAction_RespondMulticast
) )
21486 if( ( ( action
== kMDNSColliderProbeAction_Respond
) && probeIsQU
) ||
21487 ( action
== kMDNSColliderProbeAction_RespondUnicast
) )
21491 else if( ( ( action
== kMDNSColliderProbeAction_Respond
) && !probeIsQU
) ||
21492 ( action
== kMDNSColliderProbeAction_RespondMulticast
) )
21494 dest
= ( sender
.sa
.sa_family
== AF_INET
) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
21497 err
= _MDNSColliderSendResponse( me
, sockCtx
->sock
, dest
);
21498 require_noerr( err
, exit
);
21500 else if( action
== kMDNSColliderProbeAction_Probe
)
21502 dest
= ( sender
.sa
.sa_family
== AF_INET
) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
21504 err
= _MDNSColliderSendProbe( me
, sockCtx
->sock
, dest
);
21505 require_noerr( err
, exit
);
21512 static MDNSColliderProbeAction
_MDNSColliderGetProbeAction( uint32_t inBitmap
, unsigned int inProbeNumber
)
21514 MDNSColliderProbeAction action
;
21516 if( ( inProbeNumber
>= 1 ) && ( inProbeNumber
<= kMDNSColliderProbeActionMaxProbeCount
) )
21518 action
= ( inBitmap
>> ( ( inProbeNumber
- 1 ) * kMDNSColliderProbeActionBits_Count
) ) &
21519 kMDNSColliderProbeActionBits_Mask
;
21523 action
= kMDNSColliderProbeAction_None
;
21528 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction
)
21532 case kMDNSColliderProbeAction_None
: return( "None" );
21533 case kMDNSColliderProbeAction_Respond
: return( "Respond" );
21534 case kMDNSColliderProbeAction_RespondUnicast
: return( "Respond (unicast)" );
21535 case kMDNSColliderProbeAction_RespondMulticast
: return( "Respond (multicast)" );
21536 case kMDNSColliderProbeAction_Probe
: return( "Probe" );
21537 default: return( "???" );
21541 //===========================================================================================================================
21542 // _MDNSColliderExecuteProgram
21543 //===========================================================================================================================
21545 static void _MDNSColliderExecuteProgram( void *inContext
)
21548 MDNSColliderRef
const me
= (MDNSColliderRef
) inContext
;
21551 dispatch_forget( &me
->waitTimer
);
21556 const MDNSCInstruction
* const ins
= &me
->program
[ me
->pc
++ ];
21559 switch( ins
->opcode
)
21561 case kMDNSColliderOpCode_Send
:
21562 if( IsValidSocket( me
->sockV4
) )
21564 err
= _MDNSColliderSendResponse( me
, me
->sockV4
, GetMDNSMulticastAddrV4() );
21565 require_noerr( err
, exit
);
21567 if( IsValidSocket( me
->sockV6
) )
21569 err
= _MDNSColliderSendResponse( me
, me
->sockV6
, GetMDNSMulticastAddrV6() );
21570 require_noerr( err
, exit
);
21574 case kMDNSColliderOpCode_Wait
:
21575 waitMs
= ins
->operand
;
21578 err
= DispatchTimerOneShotCreate( dispatch_time_milliseconds( waitMs
), 1, me
->queue
,
21579 _MDNSColliderExecuteProgram
, me
, &me
->waitTimer
);
21580 require_noerr( err
, exit
);
21581 dispatch_resume( me
->waitTimer
);
21586 case kMDNSColliderOpCode_SetProbeActions
:
21587 me
->probeCount
= 0;
21588 me
->probeActionMap
= ins
->operand
;
21591 case kMDNSColliderOpCode_LoopPush
:
21592 check( me
->loopDepth
< kMaxLoopDepth
);
21593 me
->loopCounts
[ me
->loopDepth
++ ] = ins
->operand
;
21596 case kMDNSColliderOpCode_LoopPop
:
21597 check( me
->loopDepth
> 0 );
21598 if( --me
->loopCounts
[ me
->loopDepth
- 1 ] > 0 )
21600 me
->pc
= ins
->operand
;
21608 case kMDNSColliderOpCode_Exit
:
21614 dlogassert( "Unhandled opcode %u\n", ins
->opcode
);
21621 if( err
|| stop
) _MDNSColliderStop( me
, err
);
21624 //===========================================================================================================================
21625 // _MDNSColliderSendResponse
21626 //===========================================================================================================================
21628 static OSStatus
_MDNSColliderSendResponse( MDNSColliderRef me
, SocketRef inSock
, const struct sockaddr
*inDest
)
21633 n
= sendto( inSock
, (char *) me
->responsePtr
, me
->responseLen
, 0, inDest
, SockAddrGetSize( inDest
) );
21634 err
= map_socket_value_errno( inSock
, n
== (ssize_t
) me
->responseLen
, n
);
21638 //===========================================================================================================================
21639 // _MDNSColliderSendProbe
21640 //===========================================================================================================================
21642 static OSStatus
_MDNSColliderSendProbe( MDNSColliderRef me
, SocketRef inSock
, const struct sockaddr
*inDest
)
21647 n
= sendto( inSock
, (char *) me
->probePtr
, me
->probeLen
, 0, inDest
, SockAddrGetSize( inDest
) );
21648 err
= map_socket_value_errno( inSock
, n
== (ssize_t
) me
->probeLen
, n
);
21652 //===========================================================================================================================
21653 // ServiceBrowserCreate
21654 //===========================================================================================================================
21656 typedef struct SBDomain SBDomain
;
21657 typedef struct SBServiceType SBServiceType
;
21658 typedef struct SBServiceBrowse SBServiceBrowse
;
21659 typedef struct SBServiceInstance SBServiceInstance
;
21660 typedef struct SBIPAddress SBIPAddress
;
21662 struct ServiceBrowserPrivate
21664 CFRuntimeBase base
; // CF object base.
21665 dispatch_queue_t queue
; // Queue for service browser's events.
21666 DNSServiceRef connection
; // Shared connection for DNS-SD ops.
21667 DNSServiceRef domainsQuery
; // Query for recommended browsing domains.
21668 char * domain
; // If non-null, then browsing is limited to this domain.
21669 StringListItem
* serviceTypeList
; // If non-null, then browsing is limited to these service types.
21670 ServiceBrowserCallback_f userCallback
; // User's callback. Called when browsing stops.
21671 void * userContext
; // User's callback context.
21672 SBDomain
* domainList
; // List of domains and their browse results.
21673 dispatch_source_t stopTimer
; // Timer to stop browsing after browseTimeSecs.
21674 uint32_t ifIndex
; // If non-zero, then browsing is limited to this interface.
21675 unsigned int browseTimeSecs
; // Amount of time to spend browsing in seconds.
21676 Boolean includeAWDL
; // True if the IncludeAWDL flag should be used for DNS-SD ops that
21677 // use the "any" interface.
21682 SBDomain
* next
; // Next domain object in list.
21683 ServiceBrowserRef browser
; // Pointer to parent service browser.
21684 char * name
; // Name of the domain.
21685 DNSServiceRef servicesQuery
; // Query for services (_services._dns-sd._udp.<domain> PTR record) in domain.
21686 SBServiceType
* typeList
; // List of service types to browse for in this domain.
21689 struct SBServiceType
21691 SBServiceType
* next
; // Next service type object in list.
21692 char * name
; // Name of the service type.
21693 SBServiceBrowse
* browseList
; // List of browses for this service type.
21696 struct SBServiceBrowse
21698 SBServiceBrowse
* next
; // Next browse object in list.
21699 ServiceBrowserRef browser
; // Pointer to parent service browser.
21700 DNSServiceRef browse
; // Reference to DNSServiceBrowse op.
21701 SBServiceInstance
* instanceList
; // List of service instances that were discovered by this browse.
21702 uint64_t startTicks
; // Value of UpTicks() when the browse op began.
21703 uint32_t ifIndex
; // If non-zero, then the browse is limited to this interface.
21706 struct SBServiceInstance
21708 SBServiceInstance
* next
; // Next service instance object in list.
21709 ServiceBrowserRef browser
; // Pointer to parent service browser.
21710 char * name
; // Name of the service instance.
21711 char * fqdn
; // Fully qualified domain name of service instance (for logging/debugging).
21712 uint32_t ifIndex
; // Index of interface over which this service instance was discovered.
21713 uint64_t discoverTimeUs
; // Time it took to discover this service instance in microseconds.
21714 DNSServiceRef resolve
; // Reference to DNSServiceResolve op for this service instance.
21715 uint64_t resolveStartTicks
; // Value of UpTicks() when the DNSServiceResolve op began.
21716 uint64_t resolveTimeUs
; // Time it took to resolve this service instance.
21717 char * hostname
; // Service instance's hostname. Result of DNSServiceResolve.
21718 uint16_t port
; // Service instance's port number. Result of DNSServiceResolve.
21719 uint8_t * txtPtr
; // Service instance's TXT record data. Result of DNSServiceResolve.
21720 size_t txtLen
; // Length of service instance's TXT record data.
21721 DNSServiceRef getAddrInfo
; // Reference to DNSServiceGetAddrInfo op for service instance's hostname.
21722 uint64_t gaiStartTicks
; // Value of UpTicks() when the DNSServiceGetAddrInfo op began.
21723 SBIPAddress
* ipaddrList
; // List of IP addresses that the hostname resolved to.
21728 SBIPAddress
* next
; // Next IP address object in list.
21729 sockaddr_ip sip
; // IPv4 or IPv6 address.
21730 uint64_t resolveTimeUs
; // Time it took to resolve this IP address in microseconds.
21735 SBRDomain
* domainList
; // List of domains in which services were found.
21736 int32_t refCount
; // This object's reference count.
21738 } ServiceBrowserResultsPrivate
;
21740 static void _ServiceBrowserStop( ServiceBrowserRef me
, OSStatus inError
);
21741 static OSStatus
_ServiceBrowserAddDomain( ServiceBrowserRef inBrowser
, const char *inDomain
);
21742 static OSStatus
_ServiceBrowserRemoveDomain( ServiceBrowserRef inBrowser
, const char *inName
);
21743 static void _ServiceBrowserTimerHandler( void *inContext
);
21744 static void DNSSD_API
21745 _ServiceBrowserDomainsQueryCallback(
21746 DNSServiceRef inSDRef
,
21747 DNSServiceFlags inFlags
,
21748 uint32_t inInterfaceIndex
,
21749 DNSServiceErrorType inError
,
21750 const char * inFullName
,
21753 uint16_t inRDataLen
,
21754 const void * inRDataPtr
,
21756 void * inContext
);
21757 static void DNSSD_API
21758 _ServiceBrowserServicesQueryCallback(
21759 DNSServiceRef inSDRef
,
21760 DNSServiceFlags inFlags
,
21761 uint32_t inInterfaceIndex
,
21762 DNSServiceErrorType inError
,
21763 const char * inFullName
,
21766 uint16_t inRDataLen
,
21767 const void * inRDataPtr
,
21769 void * inContext
);
21770 static void DNSSD_API
21771 _ServiceBrowserBrowseCallback(
21772 DNSServiceRef inSDRef
,
21773 DNSServiceFlags inFlags
,
21774 uint32_t inInterfaceIndex
,
21775 DNSServiceErrorType inError
,
21776 const char * inName
,
21777 const char * inRegType
,
21778 const char * inDomain
,
21779 void * inContext
);
21780 static void DNSSD_API
21781 _ServiceBrowserResolveCallback(
21782 DNSServiceRef inSDRef
,
21783 DNSServiceFlags inFlags
,
21784 uint32_t inInterfaceIndex
,
21785 DNSServiceErrorType inError
,
21786 const char * inFullName
,
21787 const char * inHostname
,
21790 const unsigned char * inTXTPtr
,
21791 void * inContext
);
21792 static void DNSSD_API
21793 _ServiceBrowserGAICallback(
21794 DNSServiceRef inSDRef
,
21795 DNSServiceFlags inFlags
,
21796 uint32_t inInterfaceIndex
,
21797 DNSServiceErrorType inError
,
21798 const char * inHostname
,
21799 const struct sockaddr
* inSockAddr
,
21801 void * inContext
);
21803 _ServiceBrowserAddServiceType(
21804 ServiceBrowserRef inBrowser
,
21805 SBDomain
* inDomain
,
21806 const char * inName
,
21807 uint32_t inIfIndex
);
21809 _ServiceBrowserRemoveServiceType(
21810 ServiceBrowserRef inBrowser
,
21811 SBDomain
* inDomain
,
21812 const char * inName
,
21813 uint32_t inIfIndex
);
21815 _ServiceBrowserAddServiceInstance(
21816 ServiceBrowserRef inBrowser
,
21817 SBServiceBrowse
* inBrowse
,
21818 uint32_t inIfIndex
,
21819 const char * inName
,
21820 const char * inRegType
,
21821 const char * inDomain
,
21822 uint64_t inDiscoverTimeUs
);
21824 _ServiceBrowserRemoveServiceInstance(
21825 ServiceBrowserRef inBrowser
,
21826 SBServiceBrowse
* inBrowse
,
21827 const char * inName
,
21828 uint32_t inIfIndex
);
21830 _ServiceBrowserAddIPAddress(
21831 ServiceBrowserRef inBrowser
,
21832 SBServiceInstance
* inInstance
,
21833 const struct sockaddr
* inSockAddr
,
21834 uint64_t inResolveTimeUs
);
21836 _ServiceBrowserRemoveIPAddress(
21837 ServiceBrowserRef inBrowser
,
21838 SBServiceInstance
* inInstance
,
21839 const struct sockaddr
* inSockAddr
);
21840 static OSStatus
_ServiceBrowserCreateResults( ServiceBrowserRef me
, ServiceBrowserResults
**outResults
);
21841 static OSStatus
_SBDomainCreate( const char *inName
, ServiceBrowserRef inBrowser
, SBDomain
**outDomain
);
21842 static void _SBDomainFree( SBDomain
*inDomain
);
21843 static OSStatus
_SBServiceTypeCreate( const char *inName
, SBServiceType
**outType
);
21844 static void _SBServiceTypeFree( SBServiceType
*inType
);
21845 static OSStatus
_SBServiceBrowseCreate( uint32_t inIfIndex
, ServiceBrowserRef inBrowser
, SBServiceBrowse
**outBrowse
);
21846 static void _SBServiceBrowseFree( SBServiceBrowse
*inBrowse
);
21848 _SBServiceInstanceCreate(
21849 const char * inName
,
21850 const char * inType
,
21851 const char * inDomain
,
21852 uint32_t inIfIndex
,
21853 uint64_t inDiscoverTimeUs
,
21854 ServiceBrowserRef inBrowser
,
21855 SBServiceInstance
** outInstance
);
21856 static void _SBServiceInstanceFree( SBServiceInstance
*inInstance
);
21858 _SBIPAddressCreate(
21859 const struct sockaddr
* inSockAddr
,
21860 uint64_t inResolveTimeUs
,
21861 SBIPAddress
** outIPAddress
);
21862 static void _SBIPAddressFree( SBIPAddress
*inIPAddress
);
21863 static void _SBIPAddressFreeList( SBIPAddress
*inList
);
21864 static OSStatus
_SBRDomainCreate( const char *inName
, SBRDomain
**outDomain
);
21865 static void _SBRDomainFree( SBRDomain
*inDomain
);
21866 static OSStatus
_SBRServiceTypeCreate( const char *inName
, SBRServiceType
**outType
);
21867 static void _SBRServiceTypeFree( SBRServiceType
*inType
);
21869 _SBRServiceInstanceCreate(
21870 const char * inName
,
21871 uint32_t inInterfaceIndex
,
21872 const char * inHostname
,
21874 const uint8_t * inTXTPtr
,
21876 uint64_t inDiscoverTimeUs
,
21877 uint64_t inResolveTimeUs
,
21878 SBRServiceInstance
** outInstance
);
21879 static void _SBRServiceInstanceFree( SBRServiceInstance
*inInstance
);
21881 _SBRIPAddressCreate(
21882 const struct sockaddr
* inSockAddr
,
21883 uint64_t inResolveTimeUs
,
21884 SBRIPAddress
** outIPAddress
);
21885 static void _SBRIPAddressFree( SBRIPAddress
*inIPAddress
);
21887 #define ForgetSBIPAddressList( X ) ForgetCustom( X, _SBIPAddressFreeList )
21889 CF_CLASS_DEFINE( ServiceBrowser
);
21891 ulog_define_ex( kDNSSDUtilIdentifier
, ServiceBrowser
, kLogLevelTrace
, kLogFlags_None
, "ServiceBrowser", NULL
);
21892 #define sb_ulog( LEVEL, ... ) ulog( &log_category_from_name( ServiceBrowser ), (LEVEL), __VA_ARGS__ )
21895 ServiceBrowserCreate(
21896 dispatch_queue_t inQueue
,
21897 uint32_t inInterfaceIndex
,
21898 const char * inDomain
,
21899 unsigned int inBrowseTimeSecs
,
21900 Boolean inIncludeAWDL
,
21901 ServiceBrowserRef
* outBrowser
)
21904 ServiceBrowserRef obj
;
21906 CF_OBJECT_CREATE( ServiceBrowser
, obj
, err
, exit
);
21908 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
21909 obj
->ifIndex
= inInterfaceIndex
;
21912 obj
->domain
= strdup( inDomain
);
21913 require_action( obj
->domain
, exit
, err
= kNoMemoryErr
);
21915 obj
->browseTimeSecs
= inBrowseTimeSecs
;
21916 obj
->includeAWDL
= inIncludeAWDL
;
21923 CFReleaseNullSafe( obj
);
21927 //===========================================================================================================================
21928 // _ServiceBrowserFinalize
21929 //===========================================================================================================================
21931 static void _ServiceBrowserFinalize( CFTypeRef inObj
)
21933 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inObj
;
21934 StringListItem
* serviceType
;
21936 dispatch_forget( &me
->queue
);
21937 check( !me
->connection
);
21938 check( !me
->domainsQuery
);
21939 ForgetMem( &me
->domain
);
21940 while( ( serviceType
= me
->serviceTypeList
) != NULL
)
21942 me
->serviceTypeList
= serviceType
->next
;
21943 ForgetMem( &serviceType
->str
);
21944 free( serviceType
);
21946 check( !me
->domainList
);
21947 check( !me
->stopTimer
);
21950 //===========================================================================================================================
21951 // ServiceBrowserStart
21952 //===========================================================================================================================
21954 static void _ServiceBrowserStart( void *inContext
);
21956 static void ServiceBrowserStart( ServiceBrowserRef me
)
21959 dispatch_async_f( me
->queue
, me
, _ServiceBrowserStart
);
21962 static void _ServiceBrowserStart( void *inContext
)
21965 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inContext
;
21967 err
= DNSServiceCreateConnection( &me
->connection
);
21968 require_noerr( err
, exit
);
21970 err
= DNSServiceSetDispatchQueue( me
->connection
, me
->queue
);
21971 require_noerr( err
, exit
);
21975 err
= _ServiceBrowserAddDomain( me
, me
->domain
);
21976 require_noerr( err
, exit
);
21980 DNSServiceRef sdRef
;
21981 const char * const recordName
= "b._dns-sd._udp.local.";
21982 const uint32_t ifIndex
= kDNSServiceInterfaceIndexLocalOnly
;
21984 // Perform PTR meta-query for "b._dns-sd._udp.local." to enumerate recommended browsing domains.
21985 // See <https://tools.ietf.org/html/rfc6763#section-11>.
21987 sb_ulog( kLogLevelTrace
, "Starting PTR QueryRecord on interface %d for %s", (int32_t) ifIndex
, recordName
);
21989 sdRef
= me
->connection
;
21990 err
= DNSServiceQueryRecord( &sdRef
, kDNSServiceFlagsShareConnection
, ifIndex
, recordName
,
21991 kDNSServiceType_PTR
, kDNSServiceClass_IN
, _ServiceBrowserDomainsQueryCallback
, me
);
21992 require_noerr( err
, exit
);
21994 me
->domainsQuery
= sdRef
;
21997 err
= DispatchTimerCreate( dispatch_time_seconds( me
->browseTimeSecs
), DISPATCH_TIME_FOREVER
,
21998 100 * kNanosecondsPerMillisecond
, me
->queue
, _ServiceBrowserTimerHandler
, NULL
, me
, &me
->stopTimer
);
21999 require_noerr( err
, exit
);
22000 dispatch_resume( me
->stopTimer
);
22003 if( err
) _ServiceBrowserStop( me
, err
);
22006 //===========================================================================================================================
22007 // ServiceBrowserAddServiceType
22008 //===========================================================================================================================
22010 static OSStatus
ServiceBrowserAddServiceType( ServiceBrowserRef me
, const char *inServiceType
)
22013 StringListItem
* item
;
22014 StringListItem
** itemPtr
;
22015 StringListItem
* newItem
= NULL
;
22017 for( itemPtr
= &me
->serviceTypeList
; ( item
= *itemPtr
) != NULL
; itemPtr
= &item
->next
)
22019 if( strcmp( item
->str
, inServiceType
) == 0 ) break;
22023 newItem
= (StringListItem
*) calloc( 1, sizeof( *newItem
) );
22024 require_action( newItem
, exit
, err
= kNoMemoryErr
);
22026 newItem
->str
= strdup( inServiceType
);
22027 require_action( newItem
->str
, exit
, err
= kNoMemoryErr
);
22029 *itemPtr
= newItem
;
22035 FreeNullSafe( newItem
);
22039 //===========================================================================================================================
22040 // ServiceBrowserSetCallback
22041 //===========================================================================================================================
22043 static void ServiceBrowserSetCallback( ServiceBrowserRef me
, ServiceBrowserCallback_f inCallback
, void *inContext
)
22045 me
->userCallback
= inCallback
;
22046 me
->userContext
= inContext
;
22049 //===========================================================================================================================
22050 // ServiceBrowserResultsRetain
22051 //===========================================================================================================================
22053 static void ServiceBrowserResultsRetain( ServiceBrowserResults
*inResults
)
22055 ServiceBrowserResultsPrivate
* const results
= (ServiceBrowserResultsPrivate
*) inResults
;
22057 atomic_add_32( &results
->refCount
, 1 );
22060 //===========================================================================================================================
22061 // ServiceBrowserResultsRelease
22062 //===========================================================================================================================
22064 static void ServiceBrowserResultsRelease( ServiceBrowserResults
*inResults
)
22066 ServiceBrowserResultsPrivate
* const results
= (ServiceBrowserResultsPrivate
*) inResults
;
22067 SBRDomain
* domain
;
22069 if( atomic_add_and_fetch_32( &results
->refCount
, -1 ) == 0 )
22071 while( ( domain
= inResults
->domainList
) != NULL
)
22073 inResults
->domainList
= domain
->next
;
22074 _SBRDomainFree( domain
);
22080 //===========================================================================================================================
22081 // _ServiceBrowserStop
22082 //===========================================================================================================================
22084 static void _ServiceBrowserStop( ServiceBrowserRef me
, OSStatus inError
)
22089 SBServiceBrowse
* b
;
22090 SBServiceInstance
* i
;
22092 dispatch_source_forget( &me
->stopTimer
);
22093 DNSServiceForget( &me
->domainsQuery
);
22094 for( d
= me
->domainList
; d
; d
= d
->next
)
22096 DNSServiceForget( &d
->servicesQuery
);
22097 for( t
= d
->typeList
; t
; t
= t
->next
)
22099 for( b
= t
->browseList
; b
; b
= b
->next
)
22101 DNSServiceForget( &b
->browse
);
22102 for( i
= b
->instanceList
; i
; i
= i
->next
)
22104 DNSServiceForget( &i
->resolve
);
22105 DNSServiceForget( &i
->getAddrInfo
);
22110 DNSServiceForget( &me
->connection
);
22112 if( me
->userCallback
)
22114 ServiceBrowserResults
* results
= NULL
;
22116 err
= _ServiceBrowserCreateResults( me
, &results
);
22117 if( !err
) err
= inError
;
22119 me
->userCallback( results
, err
, me
->userContext
);
22120 me
->userCallback
= NULL
;
22121 me
->userContext
= NULL
;
22122 if( results
) ServiceBrowserResultsRelease( results
);
22125 while( ( d
= me
->domainList
) != NULL
)
22127 me
->domainList
= d
->next
;
22128 _SBDomainFree( d
);
22133 //===========================================================================================================================
22134 // _ServiceBrowserAddDomain
22135 //===========================================================================================================================
22137 static OSStatus
_ServiceBrowserAddDomain( ServiceBrowserRef me
, const char *inDomain
)
22141 SBDomain
** domainPtr
;
22142 SBDomain
* newDomain
= NULL
;
22144 for( domainPtr
= &me
->domainList
; ( domain
= *domainPtr
) != NULL
; domainPtr
= &domain
->next
)
22146 if( strcasecmp( domain
->name
, inDomain
) == 0 ) break;
22148 require_action_quiet( !domain
, exit
, err
= kDuplicateErr
);
22150 err
= _SBDomainCreate( inDomain
, me
, &newDomain
);
22151 require_noerr_quiet( err
, exit
);
22153 if( me
->serviceTypeList
)
22155 const StringListItem
* item
;
22157 for( item
= me
->serviceTypeList
; item
; item
= item
->next
)
22159 err
= _ServiceBrowserAddServiceType( me
, newDomain
, item
->str
, me
->ifIndex
);
22160 if( err
== kDuplicateErr
) err
= kNoErr
;
22161 require_noerr( err
, exit
);
22167 DNSServiceRef sdRef
;
22168 DNSServiceFlags flags
;
22170 // Perform PTR meta-query for _services._dns-sd._udp.<domain> to enumerate service types in domain.
22171 // See <https://tools.ietf.org/html/rfc6763#section-9>.
22173 ASPrintF( &recordName
, "_services._dns-sd._udp.%s", newDomain
->name
);
22174 require_action( recordName
, exit
, err
= kNoMemoryErr
);
22176 flags
= kDNSServiceFlagsShareConnection
;
22177 if( ( me
->ifIndex
== kDNSServiceInterfaceIndexAny
) && me
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
22179 sb_ulog( kLogLevelTrace
, "Starting PTR QueryRecord on interface %d for %s", (int32_t) me
->ifIndex
, recordName
);
22181 sdRef
= newDomain
->browser
->connection
;
22182 err
= DNSServiceQueryRecord( &sdRef
, flags
, me
->ifIndex
, recordName
, kDNSServiceType_PTR
, kDNSServiceClass_IN
,
22183 _ServiceBrowserServicesQueryCallback
, newDomain
);
22184 free( recordName
);
22185 require_noerr( err
, exit
);
22187 newDomain
->servicesQuery
= sdRef
;
22190 *domainPtr
= newDomain
;
22195 if( newDomain
) _SBDomainFree( newDomain
);
22199 //===========================================================================================================================
22200 // _ServiceBrowserRemoveDomain
22201 //===========================================================================================================================
22203 static OSStatus
_ServiceBrowserRemoveDomain( ServiceBrowserRef me
, const char *inName
)
22207 SBDomain
** domainPtr
;
22209 for( domainPtr
= &me
->domainList
; ( domain
= *domainPtr
) != NULL
; domainPtr
= &domain
->next
)
22211 if( strcasecmp( domain
->name
, inName
) == 0 ) break;
22216 *domainPtr
= domain
->next
;
22217 _SBDomainFree( domain
);
22222 err
= kNotFoundErr
;
22228 //===========================================================================================================================
22229 // _ServiceBrowserTimerHandler
22230 //===========================================================================================================================
22232 static void _ServiceBrowserTimerHandler( void *inContext
)
22234 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inContext
;
22236 _ServiceBrowserStop( me
, kNoErr
);
22239 //===========================================================================================================================
22240 // _ServiceBrowserDomainsQueryCallback
22241 //===========================================================================================================================
22243 static void DNSSD_API
22244 _ServiceBrowserDomainsQueryCallback(
22245 DNSServiceRef inSDRef
,
22246 DNSServiceFlags inFlags
,
22247 uint32_t inInterfaceIndex
,
22248 DNSServiceErrorType inError
,
22249 const char * inFullName
,
22252 uint16_t inRDataLen
,
22253 const void * inRDataPtr
,
22257 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inContext
;
22259 char domainStr
[ kDNSServiceMaxDomainName
];
22265 sb_ulog( kLogLevelTrace
, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
22266 DNSServiceFlagsToAddRmvStr( inFlags
), (int32_t) inInterfaceIndex
, inFullName
, inType
, inRDataPtr
, inRDataLen
,
22267 !inError
, inError
);
22269 require_noerr( inError
, exit
);
22271 err
= DomainNameToString( inRDataPtr
, ( (const uint8_t *) inRDataPtr
) + inRDataLen
, domainStr
, NULL
);
22272 require_noerr( err
, exit
);
22274 if( inFlags
& kDNSServiceFlagsAdd
)
22276 err
= _ServiceBrowserAddDomain( me
, domainStr
);
22277 if( err
== kDuplicateErr
) err
= kNoErr
;
22278 require_noerr( err
, exit
);
22282 err
= _ServiceBrowserRemoveDomain( me
, domainStr
);
22283 if( err
== kNotFoundErr
) err
= kNoErr
;
22284 require_noerr( err
, exit
);
22291 //===========================================================================================================================
22292 // _ServiceBrowserServicesQueryCallback
22293 //===========================================================================================================================
22295 static void DNSSD_API
22296 _ServiceBrowserServicesQueryCallback(
22297 DNSServiceRef inSDRef
,
22298 DNSServiceFlags inFlags
,
22299 uint32_t inInterfaceIndex
,
22300 DNSServiceErrorType inError
,
22301 const char * inFullName
,
22304 uint16_t inRDataLen
,
22305 const void * inRDataPtr
,
22310 SBDomain
* const domain
= (SBDomain
*) inContext
;
22311 ServiceBrowserRef
const me
= domain
->browser
;
22312 const uint8_t * src
;
22313 const uint8_t * end
;
22316 uint8_t serviceType
[ 2 * ( 1 + kDomainLabelLengthMax
) + 1 ];
22317 char serviceTypeStr
[ kDNSServiceMaxDomainName
];
22323 sb_ulog( kLogLevelTrace
, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
22324 DNSServiceFlagsToAddRmvStr( inFlags
), (int32_t) inInterfaceIndex
, inFullName
, inType
, inRDataPtr
, inRDataLen
,
22325 !inError
, inError
);
22327 require_noerr( inError
, exit
);
22329 check( inType
== kDNSServiceType_PTR
);
22330 check( inClass
== kDNSServiceClass_IN
);
22332 // The first two labels of the domain name in the RDATA describe a service type.
22333 // See <https://tools.ietf.org/html/rfc6763#section-9>.
22335 src
= (const uint8_t *) inRDataPtr
;
22336 end
= src
+ inRDataLen
;
22338 for( i
= 0; i
< 2; ++i
)
22342 require_action_quiet( ( end
- src
) > 0, exit
, err
= kUnderrunErr
);
22345 require_action_quiet( ( labelLen
> 0 ) && ( labelLen
<= kDomainLabelLengthMax
), exit
, err
= kMalformedErr
);
22346 require_action_quiet( ( (size_t)( end
- src
) ) >= ( 1 + labelLen
), exit
, err
= kUnderrunErr
);
22348 memcpy( dst
, src
, 1 + labelLen
);
22349 src
+= 1 + labelLen
;
22350 dst
+= 1 + labelLen
;
22354 err
= DomainNameToString( serviceType
, NULL
, serviceTypeStr
, NULL
);
22355 require_noerr( err
, exit
);
22357 if( inFlags
& kDNSServiceFlagsAdd
)
22359 err
= _ServiceBrowserAddServiceType( me
, domain
, serviceTypeStr
, inInterfaceIndex
);
22360 if( err
== kDuplicateErr
) err
= kNoErr
;
22361 require_noerr( err
, exit
);
22365 err
= _ServiceBrowserRemoveServiceType( me
, domain
, serviceTypeStr
, inInterfaceIndex
);
22366 if( err
== kNotFoundErr
) err
= kNoErr
;
22367 require_noerr( err
, exit
);
22374 //===========================================================================================================================
22375 // _ServiceBrowserBrowseCallback
22376 //===========================================================================================================================
22378 static void DNSSD_API
22379 _ServiceBrowserBrowseCallback(
22380 DNSServiceRef inSDRef
,
22381 DNSServiceFlags inFlags
,
22382 uint32_t inInterfaceIndex
,
22383 DNSServiceErrorType inError
,
22384 const char * inName
,
22385 const char * inRegType
,
22386 const char * inDomain
,
22390 const uint64_t nowTicks
= UpTicks();
22391 SBServiceBrowse
* const browse
= (SBServiceBrowse
*) inContext
;
22392 ServiceBrowserRef
const me
= (ServiceBrowserRef
) browse
->browser
;
22396 sb_ulog( kLogLevelTrace
, "Browse result: %s on interface %d for %s.%s%s%?{end} (error: %#m)",
22397 DNSServiceFlagsToAddRmvStr( inFlags
), (int32_t) inInterfaceIndex
, inName
, inRegType
, inDomain
, !inError
, inError
);
22399 require_noerr( inError
, exit
);
22401 if( inFlags
& kDNSServiceFlagsAdd
)
22403 err
= _ServiceBrowserAddServiceInstance( me
, browse
, inInterfaceIndex
, inName
, inRegType
, inDomain
,
22404 UpTicksToMicroseconds( nowTicks
- browse
->startTicks
) );
22405 if( err
== kDuplicateErr
) err
= kNoErr
;
22406 require_noerr( err
, exit
);
22410 err
= _ServiceBrowserRemoveServiceInstance( me
, browse
, inName
, inInterfaceIndex
);
22411 if( err
== kNotFoundErr
) err
= kNoErr
;
22412 require_noerr( err
, exit
);
22419 //===========================================================================================================================
22420 // _ServiceBrowserResolveCallback
22421 //===========================================================================================================================
22423 static void DNSSD_API
22424 _ServiceBrowserResolveCallback(
22425 DNSServiceRef inSDRef
,
22426 DNSServiceFlags inFlags
,
22427 uint32_t inInterfaceIndex
,
22428 DNSServiceErrorType inError
,
22429 const char * inFullName
,
22430 const char * inHostname
,
22433 const unsigned char * inTXTPtr
,
22437 const uint64_t nowTicks
= UpTicks();
22438 SBServiceInstance
* const instance
= (SBServiceInstance
*) inContext
;
22439 ServiceBrowserRef
const me
= (ServiceBrowserRef
) instance
->browser
;
22444 sb_ulog( kLogLevelTrace
, "Resolve result on interface %d for %s -> %s:%u %#{txt}%?{end} (error %#m)",
22445 (int32_t) inInterfaceIndex
, inFullName
, inHostname
, inPort
, inTXTPtr
, (size_t) inTXTLen
, !inError
, inError
);
22447 require_noerr( inError
, exit
);
22449 if( !MemEqual( instance
->txtPtr
, instance
->txtLen
, inTXTPtr
, inTXTLen
) )
22451 FreeNullSafe( instance
->txtPtr
);
22452 instance
->txtPtr
= _memdup( inTXTPtr
, inTXTLen
);
22453 require_action( instance
->txtPtr
, exit
, err
= kNoMemoryErr
);
22455 instance
->txtLen
= inTXTLen
;
22458 instance
->port
= ntohs( inPort
);
22460 if( !instance
->hostname
|| ( strcasecmp( instance
->hostname
, inHostname
) != 0 ) )
22462 DNSServiceRef sdRef
;
22464 if( !instance
->hostname
) instance
->resolveTimeUs
= UpTicksToMicroseconds( nowTicks
- instance
->resolveStartTicks
);
22466 err
= ReplaceString( &instance
->hostname
, NULL
, inHostname
, kSizeCString
);
22467 require_noerr( err
, exit
);
22469 DNSServiceForget( &instance
->getAddrInfo
);
22470 ForgetSBIPAddressList( &instance
->ipaddrList
);
22472 sb_ulog( kLogLevelTrace
, "Starting GetAddrInfo on interface %d for %s",
22473 (int32_t) instance
->ifIndex
, instance
->hostname
);
22475 sdRef
= me
->connection
;
22476 instance
->gaiStartTicks
= UpTicks();
22477 err
= DNSServiceGetAddrInfo( &sdRef
, kDNSServiceFlagsShareConnection
, instance
->ifIndex
,
22478 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, instance
->hostname
, _ServiceBrowserGAICallback
, instance
);
22479 require_noerr( err
, exit
);
22481 instance
->getAddrInfo
= sdRef
;
22488 //===========================================================================================================================
22489 // _ServiceBrowserGAICallback
22490 //===========================================================================================================================
22492 static void DNSSD_API
22493 _ServiceBrowserGAICallback(
22494 DNSServiceRef inSDRef
,
22495 DNSServiceFlags inFlags
,
22496 uint32_t inInterfaceIndex
,
22497 DNSServiceErrorType inError
,
22498 const char * inHostname
,
22499 const struct sockaddr
* inSockAddr
,
22504 const uint64_t nowTicks
= UpTicks();
22505 SBServiceInstance
* const instance
= (SBServiceInstance
*) inContext
;
22506 ServiceBrowserRef
const me
= (ServiceBrowserRef
) instance
->browser
;
22511 sb_ulog( kLogLevelTrace
, "GetAddrInfo result: %s on interface %d for (%s ->) %s -> %##a%?{end} (error: %#m)",
22512 DNSServiceFlagsToAddRmvStr( inFlags
), (int32_t) inInterfaceIndex
, instance
->fqdn
, inHostname
, inSockAddr
,
22513 !inError
, inError
);
22515 require_noerr( inError
, exit
);
22517 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
22519 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
22523 if( inFlags
& kDNSServiceFlagsAdd
)
22525 err
= _ServiceBrowserAddIPAddress( me
, instance
, inSockAddr
,
22526 UpTicksToMicroseconds( nowTicks
- instance
->gaiStartTicks
) );
22527 if( err
== kDuplicateErr
) err
= kNoErr
;
22528 require_noerr( err
, exit
);
22532 err
= _ServiceBrowserRemoveIPAddress( me
, instance
, inSockAddr
);
22533 if( err
== kNotFoundErr
) err
= kNoErr
;
22534 require_noerr( err
, exit
);
22541 //===========================================================================================================================
22542 // _ServiceBrowserAddServiceType
22543 //===========================================================================================================================
22546 _ServiceBrowserAddServiceType(
22547 ServiceBrowserRef me
,
22548 SBDomain
* inDomain
,
22549 const char * inName
,
22550 uint32_t inIfIndex
)
22553 SBServiceType
* type
;
22554 SBServiceType
** typePtr
;
22555 SBServiceType
* newType
= NULL
;
22556 SBServiceBrowse
* browse
;
22557 SBServiceBrowse
** browsePtr
;
22558 SBServiceBrowse
* newBrowse
= NULL
;
22559 DNSServiceRef sdRef
;
22560 DNSServiceFlags flags
;
22562 for( typePtr
= &inDomain
->typeList
; ( type
= *typePtr
) != NULL
; typePtr
= &type
->next
)
22564 if( strcasecmp( type
->name
, inName
) == 0 ) break;
22568 err
= _SBServiceTypeCreate( inName
, &newType
);
22569 require_noerr_quiet( err
, exit
);
22574 for( browsePtr
= &type
->browseList
; ( browse
= *browsePtr
) != NULL
; browsePtr
= &browse
->next
)
22576 if( browse
->ifIndex
== inIfIndex
) break;
22578 require_action_quiet( !browse
, exit
, err
= kDuplicateErr
);
22580 err
= _SBServiceBrowseCreate( inIfIndex
, me
, &newBrowse
);
22581 require_noerr_quiet( err
, exit
);
22583 flags
= kDNSServiceFlagsShareConnection
;
22584 if( ( newBrowse
->ifIndex
== kDNSServiceInterfaceIndexAny
) && me
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
22586 sb_ulog( kLogLevelTrace
, "Starting Browse on interface %d for %s%s",
22587 (int32_t) newBrowse
->ifIndex
, type
->name
, inDomain
->name
);
22589 sdRef
= me
->connection
;
22590 newBrowse
->startTicks
= UpTicks();
22591 err
= DNSServiceBrowse( &sdRef
, flags
, newBrowse
->ifIndex
, type
->name
, inDomain
->name
, _ServiceBrowserBrowseCallback
,
22593 require_noerr( err
, exit
);
22595 newBrowse
->browse
= sdRef
;
22596 *browsePtr
= newBrowse
;
22601 *typePtr
= newType
;
22606 if( newBrowse
) _SBServiceBrowseFree( newBrowse
);
22607 if( newType
) _SBServiceTypeFree( newType
);
22611 //===========================================================================================================================
22612 // _ServiceBrowserRemoveServiceType
22613 //===========================================================================================================================
22616 _ServiceBrowserRemoveServiceType(
22617 ServiceBrowserRef me
,
22618 SBDomain
* inDomain
,
22619 const char * inName
,
22620 uint32_t inIfIndex
)
22623 SBServiceType
* type
;
22624 SBServiceType
** typePtr
;
22625 SBServiceBrowse
* browse
;
22626 SBServiceBrowse
** browsePtr
;
22630 for( typePtr
= &inDomain
->typeList
; ( type
= *typePtr
) != NULL
; typePtr
= &type
->next
)
22632 if( strcasecmp( type
->name
, inName
) == 0 ) break;
22634 require_action_quiet( type
, exit
, err
= kNotFoundErr
);
22636 for( browsePtr
= &type
->browseList
; ( browse
= *browsePtr
) != NULL
; browsePtr
= &browse
->next
)
22638 if( browse
->ifIndex
== inIfIndex
) break;
22640 require_action_quiet( browse
, exit
, err
= kNotFoundErr
);
22642 *browsePtr
= browse
->next
;
22643 _SBServiceBrowseFree( browse
);
22644 if( !type
->browseList
)
22646 *typePtr
= type
->next
;
22647 _SBServiceTypeFree( type
);
22655 //===========================================================================================================================
22656 // _ServiceBrowserAddServiceInstance
22657 //===========================================================================================================================
22660 _ServiceBrowserAddServiceInstance(
22661 ServiceBrowserRef me
,
22662 SBServiceBrowse
* inBrowse
,
22663 uint32_t inIfIndex
,
22664 const char * inName
,
22665 const char * inRegType
,
22666 const char * inDomain
,
22667 uint64_t inDiscoverTimeUs
)
22670 DNSServiceRef sdRef
;
22671 SBServiceInstance
* instance
;
22672 SBServiceInstance
** instancePtr
;
22673 SBServiceInstance
* newInstance
= NULL
;
22675 for( instancePtr
= &inBrowse
->instanceList
; ( instance
= *instancePtr
) != NULL
; instancePtr
= &instance
->next
)
22677 if( ( instance
->ifIndex
== inIfIndex
) && ( strcasecmp( instance
->name
, inName
) == 0 ) ) break;
22679 require_action_quiet( !instance
, exit
, err
= kDuplicateErr
);
22681 err
= _SBServiceInstanceCreate( inName
, inRegType
, inDomain
, inIfIndex
, inDiscoverTimeUs
, me
, &newInstance
);
22682 require_noerr_quiet( err
, exit
);
22684 sb_ulog( kLogLevelTrace
, "Starting Resolve on interface %d for %s.%s%s",
22685 (int32_t) newInstance
->ifIndex
, inName
, inRegType
, inDomain
);
22687 sdRef
= me
->connection
;
22688 newInstance
->resolveStartTicks
= UpTicks();
22689 err
= DNSServiceResolve( &sdRef
, kDNSServiceFlagsShareConnection
, newInstance
->ifIndex
, inName
, inRegType
, inDomain
,
22690 _ServiceBrowserResolveCallback
, newInstance
);
22691 require_noerr( err
, exit
);
22693 newInstance
->resolve
= sdRef
;
22694 *instancePtr
= newInstance
;
22695 newInstance
= NULL
;
22698 if( newInstance
) _SBServiceInstanceFree( newInstance
);
22702 //===========================================================================================================================
22703 // _ServiceBrowserRemoveServiceInstance
22704 //===========================================================================================================================
22707 _ServiceBrowserRemoveServiceInstance(
22708 ServiceBrowserRef me
,
22709 SBServiceBrowse
* inBrowse
,
22710 const char * inName
,
22711 uint32_t inIfIndex
)
22714 SBServiceInstance
* instance
;
22715 SBServiceInstance
** ptr
;
22719 for( ptr
= &inBrowse
->instanceList
; ( instance
= *ptr
) != NULL
; ptr
= &instance
->next
)
22721 if( ( instance
->ifIndex
== inIfIndex
) && ( strcasecmp( instance
->name
, inName
) == 0 ) ) break;
22723 require_action_quiet( instance
, exit
, err
= kNotFoundErr
);
22725 *ptr
= instance
->next
;
22726 _SBServiceInstanceFree( instance
);
22733 //===========================================================================================================================
22734 // _ServiceBrowserAddIPAddress
22735 //===========================================================================================================================
22738 _ServiceBrowserAddIPAddress(
22739 ServiceBrowserRef me
,
22740 SBServiceInstance
* inInstance
,
22741 const struct sockaddr
* inSockAddr
,
22742 uint64_t inResolveTimeUs
)
22745 SBIPAddress
* ipaddr
;
22746 SBIPAddress
** ipaddrPtr
;
22747 SBIPAddress
* newIPAddr
= NULL
;
22751 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
22753 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
22758 for( ipaddrPtr
= &inInstance
->ipaddrList
; ( ipaddr
= *ipaddrPtr
) != NULL
; ipaddrPtr
= &ipaddr
->next
)
22760 if( SockAddrCompareAddr( &ipaddr
->sip
, inSockAddr
) == 0 ) break;
22762 require_action_quiet( !ipaddr
, exit
, err
= kDuplicateErr
);
22764 err
= _SBIPAddressCreate( inSockAddr
, inResolveTimeUs
, &newIPAddr
);
22765 require_noerr_quiet( err
, exit
);
22767 *ipaddrPtr
= newIPAddr
;
22772 if( newIPAddr
) _SBIPAddressFree( newIPAddr
);
22776 //===========================================================================================================================
22777 // _ServiceBrowserRemoveIPAddress
22778 //===========================================================================================================================
22781 _ServiceBrowserRemoveIPAddress(
22782 ServiceBrowserRef me
,
22783 SBServiceInstance
* inInstance
,
22784 const struct sockaddr
* inSockAddr
)
22787 SBIPAddress
* ipaddr
;
22788 SBIPAddress
** ipaddrPtr
;
22792 for( ipaddrPtr
= &inInstance
->ipaddrList
; ( ipaddr
= *ipaddrPtr
) != NULL
; ipaddrPtr
= &ipaddr
->next
)
22794 if( SockAddrCompareAddr( &ipaddr
->sip
.sa
, inSockAddr
) == 0 ) break;
22796 require_action_quiet( ipaddr
, exit
, err
= kNotFoundErr
);
22798 *ipaddrPtr
= ipaddr
->next
;
22799 _SBIPAddressFree( ipaddr
);
22806 //===========================================================================================================================
22807 // _ServiceBrowserCreateResults
22808 //===========================================================================================================================
22810 static OSStatus
_ServiceBrowserCreateResults( ServiceBrowserRef me
, ServiceBrowserResults
**outResults
)
22815 SBServiceBrowse
* b
;
22816 SBServiceInstance
* i
;
22818 ServiceBrowserResultsPrivate
* results
;
22819 SBRDomain
** domainPtr
;
22821 results
= (ServiceBrowserResultsPrivate
*) calloc( 1, sizeof( *results
) );
22822 require_action( results
, exit
, err
= kNoMemoryErr
);
22824 results
->refCount
= 1;
22826 domainPtr
= &results
->domainList
;
22827 for( d
= me
->domainList
; d
; d
= d
->next
)
22829 SBRDomain
* domain
;
22830 SBRServiceType
** typePtr
;
22832 err
= _SBRDomainCreate( d
->name
, &domain
);
22833 require_noerr_quiet( err
, exit
);
22834 *domainPtr
= domain
;
22835 domainPtr
= &domain
->next
;
22837 typePtr
= &domain
->typeList
;
22838 for( t
= d
->typeList
; t
; t
= t
->next
)
22840 SBRServiceType
* type
;
22841 SBRServiceInstance
** instancePtr
;
22843 err
= _SBRServiceTypeCreate( t
->name
, &type
);
22844 require_noerr_quiet( err
, exit
);
22846 typePtr
= &type
->next
;
22848 instancePtr
= &type
->instanceList
;
22849 for( b
= t
->browseList
; b
; b
= b
->next
)
22851 for( i
= b
->instanceList
; i
; i
= i
->next
)
22853 SBRServiceInstance
* instance
;
22854 SBRIPAddress
** ipaddrPtr
;
22856 err
= _SBRServiceInstanceCreate( i
->name
, i
->ifIndex
, i
->hostname
, i
->port
, i
->txtPtr
, i
->txtLen
,
22857 i
->discoverTimeUs
, i
->resolveTimeUs
, &instance
);
22858 require_noerr_quiet( err
, exit
);
22859 *instancePtr
= instance
;
22860 instancePtr
= &instance
->next
;
22862 ipaddrPtr
= &instance
->ipaddrList
;
22863 for( a
= i
->ipaddrList
; a
; a
= a
->next
)
22865 SBRIPAddress
* ipaddr
;
22867 err
= _SBRIPAddressCreate( &a
->sip
.sa
, a
->resolveTimeUs
, &ipaddr
);
22868 require_noerr_quiet( err
, exit
);
22870 *ipaddrPtr
= ipaddr
;
22871 ipaddrPtr
= &ipaddr
->next
;
22878 *outResults
= (ServiceBrowserResults
*) results
;
22883 if( results
) ServiceBrowserResultsRelease( (ServiceBrowserResults
*) results
);
22887 //===========================================================================================================================
22889 //===========================================================================================================================
22891 static OSStatus
_SBDomainCreate( const char *inName
, ServiceBrowserRef inBrowser
, SBDomain
**outDomain
)
22896 obj
= (SBDomain
*) calloc( 1, sizeof( *obj
) );
22897 require_action( obj
, exit
, err
= kNoMemoryErr
);
22899 obj
->name
= strdup( inName
);
22900 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
22902 obj
->browser
= inBrowser
;
22909 if( obj
) _SBDomainFree( obj
);
22913 //===========================================================================================================================
22915 //===========================================================================================================================
22917 static void _SBDomainFree( SBDomain
*inDomain
)
22919 SBServiceType
* type
;
22921 ForgetMem( &inDomain
->name
);
22922 DNSServiceForget( &inDomain
->servicesQuery
);
22923 while( ( type
= inDomain
->typeList
) != NULL
)
22925 inDomain
->typeList
= type
->next
;
22926 _SBServiceTypeFree( type
);
22931 //===========================================================================================================================
22932 // _SBServiceTypeCreate
22933 //===========================================================================================================================
22935 static OSStatus
_SBServiceTypeCreate( const char *inName
, SBServiceType
**outType
)
22938 SBServiceType
* obj
;
22940 obj
= (SBServiceType
*) calloc( 1, sizeof( *obj
) );
22941 require_action( obj
, exit
, err
= kNoMemoryErr
);
22943 obj
->name
= strdup( inName
);
22944 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
22951 if( obj
) _SBServiceTypeFree( obj
);
22955 //===========================================================================================================================
22956 // _SBServiceTypeFree
22957 //===========================================================================================================================
22959 static void _SBServiceTypeFree( SBServiceType
*inType
)
22961 SBServiceBrowse
* browse
;
22963 ForgetMem( &inType
->name
);
22964 while( ( browse
= inType
->browseList
) != NULL
)
22966 inType
->browseList
= browse
->next
;
22967 _SBServiceBrowseFree( browse
);
22972 //===========================================================================================================================
22973 // _SBServiceBrowseCreate
22974 //===========================================================================================================================
22976 static OSStatus
_SBServiceBrowseCreate( uint32_t inIfIndex
, ServiceBrowserRef inBrowser
, SBServiceBrowse
**outBrowse
)
22979 SBServiceBrowse
* obj
;
22981 obj
= (SBServiceBrowse
*) calloc( 1, sizeof( *obj
) );
22982 require_action( obj
, exit
, err
= kNoMemoryErr
);
22984 obj
->ifIndex
= inIfIndex
;
22985 obj
->browser
= inBrowser
;
22993 //===========================================================================================================================
22994 // _SBServiceBrowseFree
22995 //===========================================================================================================================
22997 static void _SBServiceBrowseFree( SBServiceBrowse
*inBrowse
)
22999 SBServiceInstance
* instance
;
23001 DNSServiceForget( &inBrowse
->browse
);
23002 while( ( instance
= inBrowse
->instanceList
) != NULL
)
23004 inBrowse
->instanceList
= instance
->next
;
23005 _SBServiceInstanceFree( instance
);
23010 //===========================================================================================================================
23011 // _SBServiceInstanceCreate
23012 //===========================================================================================================================
23015 _SBServiceInstanceCreate(
23016 const char * inName
,
23017 const char * inType
,
23018 const char * inDomain
,
23019 uint32_t inIfIndex
,
23020 uint64_t inDiscoverTimeUs
,
23021 ServiceBrowserRef inBrowser
,
23022 SBServiceInstance
** outInstance
)
23025 SBServiceInstance
* obj
;
23027 obj
= (SBServiceInstance
*) calloc( 1, sizeof( *obj
) );
23028 require_action( obj
, exit
, err
= kNoMemoryErr
);
23030 obj
->name
= strdup( inName
);
23031 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
23033 ASPrintF( &obj
->fqdn
, "%s.%s%s", obj
->name
, inType
, inDomain
);
23034 require_action( obj
->fqdn
, exit
, err
= kNoMemoryErr
);
23036 obj
->ifIndex
= inIfIndex
;
23037 obj
->discoverTimeUs
= inDiscoverTimeUs
;
23038 obj
->browser
= inBrowser
;
23040 *outInstance
= obj
;
23045 if( obj
) _SBServiceInstanceFree( obj
);
23049 //===========================================================================================================================
23050 // _SBServiceInstanceFree
23051 //===========================================================================================================================
23053 static void _SBServiceInstanceFree( SBServiceInstance
*inInstance
)
23055 ForgetMem( &inInstance
->name
);
23056 ForgetMem( &inInstance
->fqdn
);
23057 DNSServiceForget( &inInstance
->resolve
);
23058 ForgetMem( &inInstance
->hostname
);
23059 ForgetMem( &inInstance
->txtPtr
);
23060 DNSServiceForget( &inInstance
->getAddrInfo
);
23061 ForgetSBIPAddressList( &inInstance
->ipaddrList
);
23062 free( inInstance
);
23065 //===========================================================================================================================
23066 // _SBIPAddressCreate
23067 //===========================================================================================================================
23069 static OSStatus
_SBIPAddressCreate( const struct sockaddr
*inSockAddr
, uint64_t inResolveTimeUs
, SBIPAddress
**outIPAddress
)
23074 obj
= (SBIPAddress
*) calloc( 1, sizeof( *obj
) );
23075 require_action( obj
, exit
, err
= kNoMemoryErr
);
23077 SockAddrCopy( inSockAddr
, &obj
->sip
);
23078 obj
->resolveTimeUs
= inResolveTimeUs
;
23080 *outIPAddress
= obj
;
23087 //===========================================================================================================================
23088 // _SBIPAddressFree
23089 //===========================================================================================================================
23091 static void _SBIPAddressFree( SBIPAddress
*inIPAddress
)
23093 free( inIPAddress
);
23096 //===========================================================================================================================
23097 // _SBIPAddressFreeList
23098 //===========================================================================================================================
23100 static void _SBIPAddressFreeList( SBIPAddress
*inList
)
23102 SBIPAddress
* ipaddr
;
23104 while( ( ipaddr
= inList
) != NULL
)
23106 inList
= ipaddr
->next
;
23107 _SBIPAddressFree( ipaddr
);
23111 //===========================================================================================================================
23112 // _SBRDomainCreate
23113 //===========================================================================================================================
23115 static OSStatus
_SBRDomainCreate( const char *inName
, SBRDomain
**outDomain
)
23120 obj
= (SBRDomain
*) calloc( 1, sizeof( *obj
) );
23121 require_action( obj
, exit
, err
= kNoMemoryErr
);
23123 obj
->name
= strdup( inName
);
23124 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
23131 if( obj
) _SBRDomainFree( obj
);
23135 //===========================================================================================================================
23137 //===========================================================================================================================
23139 static void _SBRDomainFree( SBRDomain
*inDomain
)
23141 SBRServiceType
* type
;
23143 ForgetMem( &inDomain
->name
);
23144 while( ( type
= inDomain
->typeList
) != NULL
)
23146 inDomain
->typeList
= type
->next
;
23147 _SBRServiceTypeFree( type
);
23152 //===========================================================================================================================
23153 // _SBRServiceTypeCreate
23154 //===========================================================================================================================
23156 static OSStatus
_SBRServiceTypeCreate( const char *inName
, SBRServiceType
**outType
)
23159 SBRServiceType
* obj
;
23161 obj
= (SBRServiceType
*) calloc( 1, sizeof( *obj
) );
23162 require_action( obj
, exit
, err
= kNoMemoryErr
);
23164 obj
->name
= strdup( inName
);
23165 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
23172 if( obj
) _SBRServiceTypeFree( obj
);
23176 //===========================================================================================================================
23177 // _SBRServiceTypeFree
23178 //===========================================================================================================================
23180 static void _SBRServiceTypeFree( SBRServiceType
*inType
)
23182 SBRServiceInstance
* instance
;
23184 ForgetMem( &inType
->name
);
23185 while( ( instance
= inType
->instanceList
) != NULL
)
23187 inType
->instanceList
= instance
->next
;
23188 _SBRServiceInstanceFree( instance
);
23193 //===========================================================================================================================
23194 // _SBRServiceInstanceCreate
23195 //===========================================================================================================================
23198 _SBRServiceInstanceCreate(
23199 const char * inName
,
23200 uint32_t inInterfaceIndex
,
23201 const char * inHostname
,
23203 const uint8_t * inTXTPtr
,
23205 uint64_t inDiscoverTimeUs
,
23206 uint64_t inResolveTimeUs
,
23207 SBRServiceInstance
** outInstance
)
23210 SBRServiceInstance
* obj
;
23212 obj
= (SBRServiceInstance
*) calloc( 1, sizeof( *obj
) );
23213 require_action( obj
, exit
, err
= kNoMemoryErr
);
23215 obj
->name
= strdup( inName
);
23216 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
23220 obj
->hostname
= strdup( inHostname
);
23221 require_action( obj
->hostname
, exit
, err
= kNoMemoryErr
);
23225 obj
->txtPtr
= (uint8_t *) _memdup( inTXTPtr
, inTXTLen
);
23226 require_action( obj
->txtPtr
, exit
, err
= kNoMemoryErr
);
23227 obj
->txtLen
= inTXTLen
;
23229 obj
->discoverTimeUs
= inDiscoverTimeUs
;
23230 obj
->resolveTimeUs
= inResolveTimeUs
;
23231 obj
->ifIndex
= inInterfaceIndex
;
23232 obj
->port
= inPort
;
23234 *outInstance
= obj
;
23239 if( obj
) _SBRServiceInstanceFree( obj
);
23243 //===========================================================================================================================
23244 // _SBRServiceInstanceFree
23245 //===========================================================================================================================
23247 static void _SBRServiceInstanceFree( SBRServiceInstance
*inInstance
)
23249 SBRIPAddress
* ipaddr
;
23251 ForgetMem( &inInstance
->name
);
23252 ForgetMem( &inInstance
->hostname
);
23253 ForgetMem( &inInstance
->txtPtr
);
23254 while( ( ipaddr
= inInstance
->ipaddrList
) != NULL
)
23256 inInstance
->ipaddrList
= ipaddr
->next
;
23257 _SBRIPAddressFree( ipaddr
);
23259 free( inInstance
);
23262 //===========================================================================================================================
23263 // _SBRIPAddressCreate
23264 //===========================================================================================================================
23267 _SBRIPAddressCreate(
23268 const struct sockaddr
* inSockAddr
,
23269 uint64_t inResolveTimeUs
,
23270 SBRIPAddress
** outIPAddress
)
23273 SBRIPAddress
* obj
;
23275 obj
= (SBRIPAddress
*) calloc( 1, sizeof( *obj
) );
23276 require_action( obj
, exit
, err
= kNoMemoryErr
);
23278 SockAddrCopy( inSockAddr
, &obj
->sip
);
23279 obj
->resolveTimeUs
= inResolveTimeUs
;
23281 *outIPAddress
= obj
;
23288 //===========================================================================================================================
23289 // _SBRIPAddressFree
23290 //===========================================================================================================================
23292 static void _SBRIPAddressFree( SBRIPAddress
*inIPAddress
)
23294 free( inIPAddress
);
23297 //===========================================================================================================================
23300 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
23301 //===========================================================================================================================
23303 static OSStatus
_SocketWriteAll( SocketRef inSock
, const void *inData
, size_t inSize
, int32_t inTimeoutSecs
)
23306 const uint8_t * src
;
23307 const uint8_t * end
;
23309 struct timeval timeout
;
23312 FD_ZERO( &writeSet
);
23313 src
= (const uint8_t *) inData
;
23314 end
= src
+ inSize
;
23317 FD_SET( inSock
, &writeSet
);
23318 timeout
.tv_sec
= inTimeoutSecs
;
23319 timeout
.tv_usec
= 0;
23320 n
= select( (int)( inSock
+ 1 ), NULL
, &writeSet
, NULL
, &timeout
);
23321 if( n
== 0 ) { err
= kTimeoutErr
; goto exit
; }
23322 err
= map_socket_value_errno( inSock
, n
> 0, n
);
23323 require_noerr( err
, exit
);
23325 n
= send( inSock
, (char *) src
, (size_t)( end
- src
), 0 );
23326 err
= map_socket_value_errno( inSock
, n
>= 0, n
);
23327 if( err
== EINTR
) continue;
23328 require_noerr( err
, exit
);
23338 //===========================================================================================================================
23339 // _ParseIPv4Address
23341 // Warning: "inBuffer" may be modified even in error cases.
23343 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
23344 //===========================================================================================================================
23346 static OSStatus
_ParseIPv4Address( const char *inStr
, uint8_t inBuffer
[ 4 ], const char **outStr
)
23362 for( ; ( c
= *inStr
) != '\0'; ++inStr
)
23364 if( isdigit_safe( c
) )
23366 v
= ( *dst
* 10 ) + ( c
- '0' );
23367 require_action_quiet( v
<= 255, exit
, err
= kRangeErr
);
23368 *dst
= (uint8_t) v
;
23372 require_action_quiet( segments
<= 4, exit
, err
= kOverrunErr
);
23376 else if( ( c
== '.' ) && sawDigit
)
23378 require_action_quiet( segments
< 4, exit
, err
= kMalformedErr
);
23387 require_action_quiet( segments
== 4, exit
, err
= kUnderrunErr
);
23396 //===========================================================================================================================
23397 // _StringToIPv4Address
23399 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
23400 //===========================================================================================================================
23403 _StringToIPv4Address(
23404 const char * inStr
,
23405 StringToIPAddressFlags inFlags
,
23408 uint32_t * outSubnet
,
23409 uint32_t * outRouter
,
23410 const char ** outStr
)
23420 uint32_t subnetMask
;
23423 require_action( inStr
, exit
, err
= kParamErr
);
23425 // Parse the address-only part of the address (e.g. "1.2.3.4").
23427 err
= _ParseIPv4Address( inStr
, buf
, &inStr
);
23428 require_noerr_quiet( err
, exit
);
23429 ip
= (uint32_t)( ( buf
[ 0 ] << 24 ) | ( buf
[ 1 ] << 16 ) | ( buf
[ 2 ] << 8 ) | buf
[ 3 ] );
23432 // Parse the port (if any).
23438 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPort
), exit
, err
= kUnexpectedErr
);
23439 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) port
= ( port
* 10 ) + ( c
- '0' );
23440 require_action_quiet( port
<= 65535, exit
, err
= kRangeErr
);
23444 // Parse the prefix length (if any).
23452 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPrefix
), exit
, err
= kUnexpectedErr
);
23453 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) prefix
= ( prefix
* 10 ) + ( c
- '0' );
23454 require_action_quiet( ( prefix
>= 0 ) && ( prefix
<= 32 ), exit
, err
= kRangeErr
);
23457 subnetMask
= ( prefix
> 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix
) ) : 0;
23458 router
= ( ip
& subnetMask
) | 1;
23461 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
23463 if( outIP
) *outIP
= ip
;
23464 if( outPort
&& hasPort
) *outPort
= port
;
23465 if( outSubnet
&& hasPrefix
) *outSubnet
= subnetMask
;
23466 if( outRouter
&& hasPrefix
) *outRouter
= router
;
23467 if( outStr
) *outStr
= inStr
;
23474 //===========================================================================================================================
23475 // _ParseIPv6Address
23477 // Note: Parsed according to the rules specified in RFC 3513.
23478 // Warning: "inBuffer" may be modified even in error cases.
23480 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23481 //===========================================================================================================================
23483 static OSStatus
_ParseIPv6Address( const char *inStr
, int inAllowV4Mapped
, uint8_t inBuffer
[ 16 ], const char **outStr
)
23485 // Table to map uppercase hex characters - '0' to their numeric values.
23486 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
23487 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 };
23492 uint8_t * colonPtr
;
23499 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
23501 for( i
= 0; i
< 16; ++i
) inBuffer
[ i
] = 0;
23503 // Special case leading :: (e.g. "::1") to simplify processing later.
23505 if( *inStr
== ':' )
23508 require_action_quiet( *inStr
== ':', exit
, err
= kMalformedErr
);
23511 // Parse the address.
23519 while( ( ( c
= *inStr
++ ) != '\0' ) && ( c
!= '%' ) && ( c
!= '/' ) && ( c
!= ']' ) )
23521 if( ( c
>= 'a' ) && ( c
<= 'f' ) ) c
-= ( 'a' - 'A' );
23522 if( ( ( c
>= '0' ) && ( c
<= '9' ) ) || ( ( c
>= 'A' ) && ( c
<= 'F' ) ) )
23525 check( c
< (int) countof( kASCIItoHexTable
) );
23526 v
= ( v
<< 4 ) | kASCIItoHexTable
[ c
];
23527 require_action_quiet( v
<= 0xFFFF, exit
, err
= kRangeErr
);
23536 require_action_quiet( !colonPtr
, exit
, err
= kMalformedErr
);
23540 require_action_quiet( *inStr
!= '\0', exit
, err
= kUnderrunErr
);
23541 require_action_quiet( ( dst
+ 2 ) <= lim
, exit
, err
= kOverrunErr
);
23542 *dst
++ = (uint8_t)( ( v
>> 8 ) & 0xFF );
23543 *dst
++ = (uint8_t)( v
& 0xFF );
23549 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
23551 if( inAllowV4Mapped
&& ( c
== '.' ) && ( ( dst
+ 4 ) <= lim
) )
23553 err
= _ParseIPv4Address( ptr
, dst
, &inStr
);
23554 require_noerr_quiet( err
, exit
);
23557 ++inStr
; // Increment because the code below expects the end to be at "inStr - 1".
23563 require_action_quiet( ( dst
+ 2 ) <= lim
, exit
, err
= kOverrunErr
);
23564 *dst
++ = (uint8_t)( ( v
>> 8 ) & 0xFF );
23565 *dst
++ = (uint8_t)( v
& 0xFF );
23567 check( dst
<= lim
);
23570 require_action_quiet( dst
< lim
, exit
, err
= kOverrunErr
);
23571 n
= (int)( dst
- colonPtr
);
23572 for( i
= 1; i
<= n
; ++i
)
23574 lim
[ -i
] = colonPtr
[ n
- i
];
23575 colonPtr
[ n
- i
] = 0;
23579 require_action_quiet( dst
== lim
, exit
, err
= kUnderrunErr
);
23581 *outStr
= inStr
- 1;
23588 //===========================================================================================================================
23591 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23592 //===========================================================================================================================
23594 static OSStatus
_ParseIPv6Scope( const char *inStr
, uint32_t *outScope
, const char **outStr
)
23596 #if( TARGET_OS_POSIX )
23598 char scopeStr
[ 64 ];
23605 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
23608 lim
= dst
+ ( countof( scopeStr
) - 1 );
23609 while( ( ( c
= *inStr
) != '\0' ) && ( c
!= ':' ) && ( c
!= '/' ) && ( c
!= ']' ) && ( dst
< lim
) )
23614 check( dst
<= lim
);
23616 // First try to map as a name and if that fails, treat it as a numeric scope.
23618 scope
= if_nametoindex( scopeStr
);
23621 for( ptr
= scopeStr
; ( ( c
= *ptr
) >= '0' ) && ( c
<= '9' ); ++ptr
)
23623 scope
= ( scope
* 10 ) + ( ( (uint8_t) c
) - '0' );
23625 require_action_quiet( c
== '\0', exit
, err
= kMalformedErr
);
23626 require_action_quiet( ( ptr
!= scopeStr
) && ( ( (int)( ptr
- scopeStr
) ) <= 10 ), exit
, err
= kMalformedErr
);
23638 const char * start
;
23642 for( start
= inStr
; ( ( c
= *inStr
) >= '0' ) && ( c
<= '9' ); ++inStr
)
23644 scope
= ( scope
* 10 ) + ( c
- '0' );
23646 require_action_quiet( ( inStr
!= start
) && ( ( (int)( inStr
- start
) ) <= 10 ), exit
, err
= kMalformedErr
);
23657 //===========================================================================================================================
23658 // _StringToIPv6Address
23660 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
23661 //===========================================================================================================================
23664 _StringToIPv6Address(
23665 const char * inStr
,
23666 StringToIPAddressFlags inFlags
,
23667 uint8_t outIPv6
[ 16 ],
23668 uint32_t * outScope
,
23671 const char ** outStr
)
23674 uint8_t ipv6
[ 16 ];
23685 require_action( inStr
, exit
, err
= kParamErr
);
23687 if( *inStr
== '[' ) ++inStr
; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
23689 // Parse the address-only part of the address (e.g. "1::1").
23691 err
= _ParseIPv6Address( inStr
, !( inFlags
& kStringToIPAddressFlagsNoIPv4Mapped
), ipv6
, &inStr
);
23692 require_noerr_quiet( err
, exit
);
23695 // Parse the scope, port, or prefix length.
23706 if( c
== '%' ) // Scope (e.g. "%en0" or "%5")
23708 require_action_quiet( !hasScope
, exit
, err
= kMalformedErr
);
23709 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoScope
), exit
, err
= kUnexpectedErr
);
23711 err
= _ParseIPv6Scope( inStr
, &scope
, &inStr
);
23712 require_noerr_quiet( err
, exit
);
23716 else if( c
== ':' ) // Port (e.g. ":80")
23718 require_action_quiet( !hasPort
, exit
, err
= kMalformedErr
);
23719 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPort
), exit
, err
= kUnexpectedErr
);
23720 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) port
= ( port
* 10 ) + ( c
- '0' );
23721 require_action_quiet( port
<= 65535, exit
, err
= kRangeErr
);
23724 else if( c
== '/' ) // Prefix Length (e.g. "/64")
23726 require_action_quiet( !hasPrefix
, exit
, err
= kMalformedErr
);
23727 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPrefix
), exit
, err
= kUnexpectedErr
);
23728 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) prefix
= ( prefix
* 10 ) + ( c
- '0' );
23729 require_action_quiet( ( prefix
>= 0 ) && ( prefix
<= 128 ), exit
, err
= kRangeErr
);
23732 else if( c
== ']' )
23734 require_action_quiet( !hasBracket
, exit
, err
= kMalformedErr
);
23744 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
23746 if( outIPv6
) for( i
= 0; i
< 16; ++i
) outIPv6
[ i
] = ipv6
[ i
];
23747 if( outScope
&& hasScope
) *outScope
= scope
;
23748 if( outPort
&& hasPort
) *outPort
= port
;
23749 if( outPrefix
&& hasPrefix
) *outPrefix
= prefix
;
23750 if( outStr
) *outStr
= inStr
;
23757 //===========================================================================================================================
23758 // _StringArray_Free
23760 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
23761 //===========================================================================================================================
23763 static void _StringArray_Free( char **inArray
, size_t inCount
)
23767 for( i
= 0; i
< inCount
; ++i
)
23769 free( inArray
[ i
] );
23771 if( inCount
> 0 ) free( inArray
);
23774 //===========================================================================================================================
23775 // _ParseQuotedEscapedString
23777 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
23778 //===========================================================================================================================
23781 _ParseQuotedEscapedString(
23782 const char * inSrc
,
23783 const char * inEnd
,
23784 const char * inDelimiters
,
23787 size_t * outCopiedLen
,
23788 size_t * outTotalLen
,
23789 const char ** outSrc
)
23791 const unsigned char * src
;
23792 const unsigned char * end
;
23793 unsigned char * dst
;
23794 unsigned char * lim
;
23798 Boolean singleQuote
;
23799 Boolean doubleQuote
;
23801 if( inEnd
== NULL
) inEnd
= inSrc
+ strlen( inSrc
);
23802 src
= (const unsigned char *) inSrc
;
23803 end
= (const unsigned char *) inEnd
;
23804 dst
= (unsigned char *) inBuf
;
23805 lim
= dst
+ inMaxLen
;
23806 while( ( src
< end
) && isspace_safe( *src
) ) ++src
; // Skip leading spaces.
23807 if( src
>= end
) return( false );
23809 // Parse each argument from the string.
23811 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
23814 singleQuote
= false;
23815 doubleQuote
= false;
23821 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
23825 singleQuote
= false;
23829 else if( doubleQuote
)
23831 // Double quotes protect everything except double quotes and backslashes. A backslash can be
23832 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
23833 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
23834 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
23835 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
23839 doubleQuote
= false;
23842 else if( c
== '\\' )
23847 if( ( c2
== '"' ) || ( c2
== '\\' ) )
23852 else if( c2
== '\n' )
23857 else if( ( c2
== 'x' ) || ( c2
== 'X' ) )
23861 if( ( ( end
- src
) >= 2 ) && IsHexPair( src
) )
23863 c
= HexPairToByte( src
);
23867 else if( isoctal_safe( c2
) )
23869 if( ( ( end
- src
) >= 3 ) && IsOctalTriple( src
) )
23871 c
= OctalTripleToByte( src
);
23878 else if( strchr( inDelimiters
, c
) )
23882 else if( c
== '\\' )
23884 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
23885 // A backslash followed by a newline disappears completely.
23886 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
23887 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
23897 else if( ( c
== 'x' ) || ( c
== 'X' ) )
23900 if( ( ( end
- src
) >= 2 ) && IsHexPair( src
) )
23902 c
= HexPairToByte( src
);
23906 else if( isoctal_safe( c
) )
23908 if( ( ( end
- src
) >= 3 ) && IsOctalTriple( src
) )
23910 c
= OctalTripleToByte( src
);
23924 else if( c
== '\'' )
23926 singleQuote
= true;
23929 else if( c
== '"' )
23931 doubleQuote
= true;
23937 if( inBuf
) *dst
= c
;
23943 if( outCopiedLen
) *outCopiedLen
= (size_t)( dst
- ( (unsigned char *) inBuf
) );
23944 if( outTotalLen
) *outTotalLen
= totalLen
;
23945 if( outSrc
) *outSrc
= (const char *) src
;
23949 //===========================================================================================================================
23950 // _ServerSocketOpenEx2
23952 // Note: Based on ServerSocketOpenEx() from CoreUtils. Added parameter to not use SO_REUSEPORT.
23953 //===========================================================================================================================
23956 _ServerSocketOpenEx2(
23960 const void * inAddr
,
23964 Boolean inNoPortReuse
,
23965 SocketRef
* outSock
)
23975 port
= ( inPort
< 0 ) ? -inPort
: inPort
; // Negated port number means "try this port, but allow dynamic".
23977 sock
= socket( inFamily
, inType
, inProtocol
);
23978 err
= map_socket_creation_errno( sock
);
23979 require_noerr_quiet( err
, exit
);
23981 #if( defined( SO_NOSIGPIPE ) )
23982 setsockopt( sock
, SOL_SOCKET
, SO_NOSIGPIPE
, &(int){ 1 }, (socklen_t
) sizeof( int ) );
23985 err
= SocketMakeNonBlocking( sock
);
23986 require_noerr( err
, exit
);
23988 // Set receive buffer size. This has to be done on the listening socket *before* listen is called because
23989 // accept does not return until after the window scale option is exchanged during the 3-way handshake.
23990 // Since accept returns a new socket, the only way to use a larger window scale option is to set the buffer
23991 // size on the listening socket since SO_RCVBUF is inherited by the accepted socket. See UNPv1e3 Section 7.5.
23993 err
= SocketSetBufferSize( sock
, SO_RCVBUF
, inRcvBufSize
);
23994 check_noerr( err
);
23996 // Allow port or address reuse because we may bind separate IPv4 and IPv6 sockets to the same port.
23998 if( ( inType
!= SOCK_DGRAM
) || !inNoPortReuse
)
24001 name
= ( inType
== SOCK_DGRAM
) ? SO_REUSEPORT
: SO_REUSEADDR
;
24002 err
= setsockopt( sock
, SOL_SOCKET
, name
, (char *) &option
, (socklen_t
) sizeof( option
) );
24003 err
= map_socket_noerr_errno( sock
, err
);
24004 require_noerr( err
, exit
);
24007 if( inFamily
== AF_INET
)
24009 // Bind to the port. If it fails, retry with a dynamic port.
24011 memset( &sip
.v4
, 0, sizeof( sip
.v4
) );
24012 SIN_LEN_SET( &sip
.v4
);
24013 sip
.v4
.sin_family
= AF_INET
;
24014 sip
.v4
.sin_port
= htons( (uint16_t) port
);
24015 sip
.v4
.sin_addr
.s_addr
= inAddr
? *( (const uint32_t *) inAddr
) : htonl( INADDR_ANY
);
24016 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v4
) );
24017 err
= map_socket_noerr_errno( sock
, err
);
24018 if( err
&& ( inPort
< 0 ) )
24020 sip
.v4
.sin_port
= 0;
24021 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v4
) );
24022 err
= map_socket_noerr_errno( sock
, err
);
24024 require_noerr( err
, exit
);
24026 #if( defined( AF_INET6 ) )
24027 else if( inFamily
== AF_INET6
)
24029 // Restrict this socket to IPv6 only because we're going to use a separate socket for IPv4.
24032 err
= setsockopt( sock
, IPPROTO_IPV6
, IPV6_V6ONLY
, (char *) &option
, (socklen_t
) sizeof( option
) );
24033 err
= map_socket_noerr_errno( sock
, err
);
24034 require_noerr( err
, exit
);
24036 // Bind to the port. If it fails, retry with a dynamic port.
24038 memset( &sip
.v6
, 0, sizeof( sip
.v6
) );
24039 SIN6_LEN_SET( &sip
.v6
);
24040 sip
.v6
.sin6_family
= AF_INET6
;
24041 sip
.v6
.sin6_port
= htons( (uint16_t) port
);
24042 sip
.v6
.sin6_addr
= inAddr
? *( (const struct in6_addr
*) inAddr
) : in6addr_any
;
24043 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v6
) );
24044 err
= map_socket_noerr_errno( sock
, err
);
24045 if( err
&& ( inPort
< 0 ) )
24047 sip
.v6
.sin6_port
= 0;
24048 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v6
) );
24049 err
= map_socket_noerr_errno( sock
, err
);
24051 require_noerr( err
, exit
);
24056 dlogassert( "Unsupported family: %d", inFamily
);
24057 err
= kUnsupportedErr
;
24061 if( inType
== SOCK_STREAM
)
24063 err
= listen( sock
, SOMAXCONN
);
24064 err
= map_socket_noerr_errno( sock
, err
);
24067 err
= listen( sock
, 5 );
24068 err
= map_socket_noerr_errno( sock
, err
);
24069 require_noerr( err
, exit
);
24075 len
= (socklen_t
) sizeof( sip
);
24076 err
= getsockname( sock
, &sip
.sa
, &len
);
24077 err
= map_socket_noerr_errno( sock
, err
);
24078 require_noerr( err
, exit
);
24080 *outPort
= SockAddrGetPort( &sip
);
24083 sock
= kInvalidSocketRef
;
24086 ForgetSocket( &sock
);
24090 //===========================================================================================================================
24093 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24094 //===========================================================================================================================
24096 static void * _memdup( const void *inPtr
, size_t inLen
)
24100 mem
= malloc( ( inLen
> 0 ) ? inLen
: 1 ); // If inLen is 0, use 1 since malloc( 0 ) is not well defined.
24101 require( mem
, exit
);
24102 if( inLen
> 0 ) memcpy( mem
, inPtr
, inLen
);
24108 //===========================================================================================================================
24111 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24112 //===========================================================================================================================
24114 static int _memicmp( const void *inP1
, const void *inP2
, size_t inLen
)
24116 const unsigned char * p1
;
24117 const unsigned char * e1
;
24118 const unsigned char * p2
;
24122 p1
= (const unsigned char *) inP1
;
24124 p2
= (const unsigned char *) inP2
;
24129 c1
= tolower( c1
);
24130 c2
= tolower( c2
);
24131 if( c1
< c2
) return( -1 );
24132 if( c1
> c2
) return( 1 );
24137 //===========================================================================================================================
24140 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
24141 //===========================================================================================================================
24143 static uint32_t _FNV1( const void *inData
, size_t inSize
)
24145 const uint8_t * src
= (const uint8_t *) inData
;
24146 const uint8_t * const end
= src
+ inSize
;
24149 hash
= 0x811c9dc5U
;
24150 while( src
!= end
)
24152 hash
*= 0x01000193;