2 Copyright (c) 2016-2018 Apple Inc. All rights reserved.
4 dnssdutil is a command-line utility for testing the DNS-SD API.
7 #include <CoreUtils/CommonServices.h> // Include early.
8 #include <CoreUtils/AsyncConnection.h>
9 #include <CoreUtils/AtomicUtils.h>
10 #include <CoreUtils/CFUtils.h>
11 #include <CoreUtils/CommandLineUtils.h>
12 #include <CoreUtils/DataBufferUtils.h>
13 #include <CoreUtils/DebugServices.h>
14 #include <CoreUtils/HTTPUtils.h>
15 #include <CoreUtils/JSONUtils.h>
16 #include <CoreUtils/LogUtils.h>
17 #include <CoreUtils/MiscUtils.h>
18 #include <CoreUtils/NetUtils.h>
19 #include <CoreUtils/PrintFUtils.h>
20 #include <CoreUtils/RandomNumberUtils.h>
21 #include <CoreUtils/SoftLinking.h>
22 #include <CoreUtils/StringUtils.h>
23 #include <CoreUtils/TickUtils.h>
24 #include <CoreUtils/TimeUtils.h>
26 #include <dns_sd_private.h>
28 #include CF_RUNTIME_HEADER
30 #if( TARGET_OS_DARWIN )
31 #include <CFNetwork/CFHost.h>
32 #include <CoreFoundation/CoreFoundation.h>
33 #include <SystemConfiguration/SCPrivate.h>
39 #include <sys/proc_info.h>
42 #if( TARGET_OS_POSIX )
43 #include <sys/resource.h>
46 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
47 #include "tweetnacl.h" // TweetNaCl from <https://tweetnacl.cr.yp.to/software.html>.
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 //===========================================================================================================================
62 //===========================================================================================================================
64 // DNS-SD API flag descriptors
66 #define kDNSServiceFlagsDescriptors \
67 "\x00" "AutoTrigger\0" \
70 "\x03" "NoAutoRename\0" \
73 "\x06" "BrowseDomains\0" \
74 "\x07" "RegistrationDomains\0" \
75 "\x08" "LongLivedQuery\0" \
76 "\x09" "AllowRemoteQuery\0" \
77 "\x0A" "ForceMulticast\0" \
78 "\x0B" "KnownUnique\0" \
79 "\x0C" "ReturnIntermediates\0" \
80 "\x0D" "NonBrowsable\0" \
81 "\x0E" "ShareConnection\0" \
82 "\x0F" "SuppressUnusable\0" \
84 "\x11" "IncludeP2P\0" \
85 "\x12" "WakeOnResolve\0" \
86 "\x13" "BackgroundTrafficClass\0" \
87 "\x14" "IncludeAWDL\0" \
89 "\x16" "UnicastResponse\0" \
90 "\x17" "ValidateOptional\0" \
91 "\x18" "WakeOnlyService\0" \
92 "\x19" "ThresholdOne\0" \
93 "\x1A" "ThresholdFinder\0" \
94 "\x1B" "DenyCellular\0" \
95 "\x1C" "ServiceIndex\0" \
96 "\x1D" "DenyExpensive\0" \
97 "\x1E" "PathEvaluationDone\0" \
98 "\x1F" "AllowExpiredAnswers\0" \
101 #define kDNSServiceProtocolDescriptors \
108 #define kBadDNSServiceRef ( (DNSServiceRef)(intptr_t) -1 )
110 //===========================================================================================================================
112 //===========================================================================================================================
115 #define kDNSMaxUDPMessageSize 512
116 #define kDNSMaxTCPMessageSize UINT16_MAX
118 #define kDomainLabelLengthMax 63
119 #define kDomainNameLengthMax 256
121 #define kDNSRecordDataLengthMax UINT16_MAX
127 uint8_t questionCount
[ 2 ];
128 uint8_t answerCount
[ 2 ];
129 uint8_t authorityCount
[ 2 ];
130 uint8_t additionalCount
[ 2 ];
134 #define kDNSHeaderLength 12
135 check_compile_time( sizeof( DNSHeader
) == kDNSHeaderLength
);
137 #define DNSHeaderGetID( HDR ) ReadBig16( ( HDR )->id )
138 #define DNSHeaderGetFlags( HDR ) ReadBig16( ( HDR )->flags )
139 #define DNSHeaderGetQuestionCount( HDR ) ReadBig16( ( HDR )->questionCount )
140 #define DNSHeaderGetAnswerCount( HDR ) ReadBig16( ( HDR )->answerCount )
141 #define DNSHeaderGetAuthorityCount( HDR ) ReadBig16( ( HDR )->authorityCount )
142 #define DNSHeaderGetAdditionalCount( HDR ) ReadBig16( ( HDR )->additionalCount )
144 #define DNSHeaderSetID( HDR, X ) WriteBig16( ( HDR )->id, (X) )
145 #define DNSHeaderSetFlags( HDR, X ) WriteBig16( ( HDR )->flags, (X) )
146 #define DNSHeaderSetQuestionCount( HDR, X ) WriteBig16( ( HDR )->questionCount, (X) )
147 #define DNSHeaderSetAnswerCount( HDR, X ) WriteBig16( ( HDR )->answerCount, (X) )
148 #define DNSHeaderSetAuthorityCount( HDR, X ) WriteBig16( ( HDR )->authorityCount, (X) )
149 #define DNSHeaderSetAdditionalCount( HDR, X ) WriteBig16( ( HDR )->additionalCount, (X) )
151 // Single-bit DNS header fields
153 #define kDNSHeaderFlag_Response ( 1 << 15 ) // QR (bit 15), Query (0)/Response (1)
154 #define kDNSHeaderFlag_AuthAnswer ( 1 << 10 ) // AA (bit 10), Authoritative Answer
155 #define kDNSHeaderFlag_Truncation ( 1 << 9 ) // TC (bit 9), TrunCation
156 #define kDNSHeaderFlag_RecursionDesired ( 1 << 8 ) // RD (bit 8), Recursion Desired
157 #define kDNSHeaderFlag_RecursionAvailable ( 1 << 7 ) // RA (bit 7), Recursion Available
158 #define kDNSHeaderFlag_Z ( 1 << 6 ) // Z (bit 6), Reserved (must be zero)
159 #define kDNSHeaderFlag_AuthenticData ( 1 << 5 ) // AD (bit 5), Authentic Data (RFC 2535, Section 6)
160 #define kDNSHeaderFlag_CheckingDisabled ( 1 << 4 ) // CD (bit 4), Checking Disabled (RFC 2535, Section 6)
162 // OPCODE (bits 14-11), Operation Code
164 #define DNSFlagsGetOpCode( FLAGS ) ( ( (FLAGS) >> 11 ) & 0x0FU )
165 #define DNSFlagsSetOpCode( FLAGS, OPCODE ) \
166 do{ (FLAGS) = ( (FLAGS) & ~0x7800U ) | ( ( (OPCODE) & 0x0FU ) << 11 ); } while( 0 )
168 #define kDNSOpCode_Query 0 // QUERY (standard query)
169 #define kDNSOpCode_InverseQuery 1 // IQUERY (inverse query)
170 #define kDNSOpCode_Status 2 // STATUS
171 #define kDNSOpCode_Notify 4 // NOTIFY
172 #define kDNSOpCode_Update 5 // UPDATE
174 // RCODE (bits 3-0), Response Code
176 #define DNSFlagsGetRCode( FLAGS ) ( (FLAGS) & 0x0FU )
177 #define DNSFlagsSetRCode( FLAGS, RCODE ) \
178 do{ (FLAGS) = ( (FLAGS) & ~0x000FU ) | ( (RCODE) & 0x0FU ); } while( 0 )
180 #define kDNSRCode_NoError 0
181 #define kDNSRCode_FormatError 1
182 #define kDNSRCode_ServerFailure 2
183 #define kDNSRCode_NXDomain 3
184 #define kDNSRCode_NotImplemented 4
185 #define kDNSRCode_Refused 5
192 } DNSQuestionFixedFields
;
194 check_compile_time( sizeof( DNSQuestionFixedFields
) == 4 );
196 #define DNSQuestionFixedFieldsInit( FIELDS, QTYPE, QCLASS ) \
197 do { WriteBig16( (FIELDS)->type, QTYPE ); WriteBig16( (FIELDS)->class, QCLASS ); } while( 0 )
199 #define DNSQuestionFixedFieldsGetType( FIELDS ) ReadBig16( (FIELDS)->type )
200 #define DNSQuestionFixedFieldsGetClass( FIELDS ) ReadBig16( (FIELDS)->class )
207 uint8_t rdlength
[ 2 ];
209 } DNSRecordFixedFields
;
211 check_compile_time( sizeof( DNSRecordFixedFields
) == 10 );
213 // SRV RDATA fixed-length fields. See <https://tools.ietf.org/html/rfc2782>.
217 uint8_t priority
[ 2 ];
221 } SRVRecordDataFixedFields
;
223 check_compile_time( sizeof( SRVRecordDataFixedFields
) == 6 );
225 // SOA RDATA fixed-length fields. See <https://tools.ietf.org/html/rfc1035#section-3.3.13>.
230 uint8_t refresh
[ 4 ];
233 uint8_t minimum
[ 4 ];
235 } SOARecordDataFixedFields
;
237 check_compile_time( sizeof( SOARecordDataFixedFields
) == 20 );
239 // DNS message compression. See <https://tools.ietf.org/html/rfc1035#section-4.1.4>.
241 #define kDNSCompressionOffsetMax 0x3FFF
243 #define IsCompressionByte( X ) ( ( ( X ) & 0xC0 ) == 0xC0 )
244 #define WriteDNSCompressionPtr( PTR, OFFSET ) \
247 ( (uint8_t *)(PTR) )[ 0 ] = (uint8_t)( ( ( (OFFSET) >> 8 ) & 0x3F ) | 0xC0 ); \
248 ( (uint8_t *)(PTR) )[ 1 ] = (uint8_t)( (OFFSET) & 0xFF ); \
252 #define NextLabel( LABEL ) ( ( (LABEL)[ 0 ] == 0 ) ? NULL : ( (LABEL) + 1 + (LABEL)[ 0 ] ) )
254 //===========================================================================================================================
256 //===========================================================================================================================
258 #define kMDNSPort 5353
260 #define kDefaultMDNSMessageID 0
261 #define kDefaultMDNSQueryFlags 0
263 #define kQClassUnicastResponseBit ( 1U << 15 )
264 #define kRRClassCacheFlushBit ( 1U << 15 )
266 // Recommended Resource Record TTL values. See <https://tools.ietf.org/html/rfc6762#section-10>.
268 #define kMDNSRecordTTL_Host 120 // TTL for resource records related to a host name, e.g., A, AAAA, SRV, etc.
269 #define kMDNSRecordTTL_Other 4500 // TTL for other resource records.
271 // Maximum mDNS Message Size. See <https://tools.ietf.org/html/rfc6762#section-17>.
273 #define kMDNSMessageSizeMax 8952 // 9000 B (Ethernet jumbo frame max size) - 40 B (IPv6 header) - 8 B (UDP header)
275 #define kLocalStr "\x05" "local"
276 #define kLocalName ( (const uint8_t *) kLocalStr )
277 #define kLocalNameLen sizeof( kLocalStr )
279 //===========================================================================================================================
280 // Test Address Blocks
281 //===========================================================================================================================
283 // IPv4 address block 203.0.113.0/24 (TEST-NET-3) is reserved for documentation. See <https://tools.ietf.org/html/rfc5737>.
285 #define kDNSServerBaseAddrV4 UINT32_C( 0xCB007100 ) // 203.0.113.0/24
287 // IPv6 address block 2001:db8::/32 is reserved for documentation. See <https://tools.ietf.org/html/rfc3849>.
289 static const uint8_t kDNSServerBaseAddrV6
[] =
291 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:1::/120
294 static const uint8_t kMDNSReplierBaseAddrV6
[] =
296 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 2001:db8:2::/96
299 check_compile_time( sizeof( kDNSServerBaseAddrV6
) == 16 );
300 check_compile_time( sizeof( kMDNSReplierBaseAddrV6
) == 16 );
302 // Bad IPv4 and IPv6 Address Blocks
303 // Used by the DNS server when it needs to respond with intentionally "bad" A/AAAA record data, i.e., IP addresses neither
304 // in 203.0.113.0/24 nor 2001:db8:1::/120.
306 #define kDNSServerBadBaseAddrV4 UINT32_C( 0x00000000 ) // 0.0.0.0/24
308 static const uint8_t kDNSServerBadBaseAddrV6
[] =
310 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 // ::ffff:0:0/120
313 check_compile_time( sizeof( kDNSServerBadBaseAddrV6
) == 16 );
315 //===========================================================================================================================
317 //===========================================================================================================================
319 #define kLowerAlphaNumericCharSet "abcdefghijklmnopqrstuvwxyz0123456789"
320 #define kLowerAlphaNumericCharSetSize sizeof_string( kLowerAlphaNumericCharSet )
322 // Note: strcpy_literal() appears in CoreUtils code, but isn't currently defined in framework headers.
324 #if( !defined( strcpy_literal ) )
325 #define strcpy_literal( DST, SRC ) memcpy( DST, SRC, sizeof( SRC ) )
328 #define _RandomStringExact( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, OUT_STRING ) \
329 RandomString( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, CHAR_COUNT, OUT_STRING )
331 #define kNoSuchRecordStr "No Such Record"
332 #define kNoSuchRecordAStr "No Such Record (A)"
333 #define kNoSuchRecordAAAAStr "No Such Record (AAAA)"
335 #define kRootLabel ( (const uint8_t *) "" )
337 //===========================================================================================================================
338 // Gerneral Command Options
339 //===========================================================================================================================
341 // Command option macros
343 #define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON ) \
344 CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None, \
347 #define kRequiredOptionSuffix " [REQUIRED]"
349 #define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
350 CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, \
351 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
352 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
354 #define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
355 MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
357 #define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
358 CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
359 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
360 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
362 #define DoubleOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
363 CLI_OPTION_DOUBLE_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
364 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
365 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
367 #define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \
368 CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL )
370 #define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
371 CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
372 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
373 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
375 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
376 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
378 #define CFStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
379 CLI_OPTION_CFSTRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
380 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
381 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
383 // DNS-SD API flag options
385 static int gDNSSDFlags
= 0;
386 static int gDNSSDFlag_AllowExpiredAnswers
= false;
387 static int gDNSSDFlag_BrowseDomains
= false;
388 static int gDNSSDFlag_DenyCellular
= false;
389 static int gDNSSDFlag_DenyExpensive
= false;
390 static int gDNSSDFlag_ForceMulticast
= false;
391 static int gDNSSDFlag_IncludeAWDL
= false;
392 static int gDNSSDFlag_NoAutoRename
= false;
393 static int gDNSSDFlag_PathEvaluationDone
= false;
394 static int gDNSSDFlag_RegistrationDomains
= false;
395 static int gDNSSDFlag_ReturnIntermediates
= false;
396 static int gDNSSDFlag_Shared
= false;
397 static int gDNSSDFlag_SuppressUnusable
= false;
398 static int gDNSSDFlag_Timeout
= false;
399 static int gDNSSDFlag_UnicastResponse
= false;
400 static int gDNSSDFlag_Unique
= false;
401 static int gDNSSDFlag_WakeOnResolve
= false;
403 #define DNSSDFlagsOption() \
404 IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \
405 "DNSServiceFlags as an integer. This value is bitwise ORed with other single flag options.", false )
407 #define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \
408 BooleanOption( SHORT_CHAR, # FLAG_NAME, &gDNSSDFlag_ ## FLAG_NAME, "Use kDNSServiceFlags" # FLAG_NAME "." )
410 #define DNSSDFlagsOption_AllowExpiredAnswers() DNSSDFlagOption( 'X', AllowExpiredAnswers )
411 #define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular )
412 #define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive )
413 #define DNSSDFlagsOption_ForceMulticast() DNSSDFlagOption( 'M', ForceMulticast )
414 #define DNSSDFlagsOption_IncludeAWDL() DNSSDFlagOption( 'A', IncludeAWDL )
415 #define DNSSDFlagsOption_NoAutoRename() DNSSDFlagOption( 'N', NoAutoRename )
416 #define DNSSDFlagsOption_PathEvalDone() DNSSDFlagOption( 'P', PathEvaluationDone )
417 #define DNSSDFlagsOption_ReturnIntermediates() DNSSDFlagOption( 'I', ReturnIntermediates )
418 #define DNSSDFlagsOption_Shared() DNSSDFlagOption( 'S', Shared )
419 #define DNSSDFlagsOption_SuppressUnusable() DNSSDFlagOption( 'S', SuppressUnusable )
420 #define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout )
421 #define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse )
422 #define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique )
423 #define DNSSDFlagsOption_WakeOnResolve() DNSSDFlagOption( 'W', WakeOnResolve )
427 static const char * gInterface
= NULL
;
429 #define InterfaceOption() \
430 StringOption( 'i', "interface", &gInterface, "interface", \
431 "Network interface by name or index. Use index -1 for local-only.", false )
433 // Connection options
435 #define kConnectionArg_Normal ""
436 #define kConnectionArgPrefix_PID "pid:"
437 #define kConnectionArgPrefix_UUID "uuid:"
439 static const char * gConnectionOpt
= kConnectionArg_Normal
;
441 #define ConnectionOptions() \
442 { kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type", \
443 kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL, \
444 "Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL }
446 #define kConnectionSection_Name "Connection Option"
447 #define kConnectionSection_Text \
448 "The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n" \
449 "the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n" \
450 "specifying the connection option without an argument, i.e.,\n" \
454 "To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n" \
456 " --connection=pid:<PID>\n" \
458 "to specify the delegator by PID, or use\n" \
460 " --connection=uuid:<UUID>\n" \
462 "to specify the delegator by UUID.\n" \
464 "To not use a main connection at all, but instead perform operations on their own implicit connections, use\n" \
468 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
470 // Help text for record data options
472 #define kRDataArgPrefix_Domain "domain:"
473 #define kRDataArgPrefix_File "file:"
474 #define kRDataArgPrefix_HexString "hex:"
475 #define kRDataArgPrefix_IPv4 "ipv4:"
476 #define kRDataArgPrefix_IPv6 "ipv6:"
477 #define kRDataArgPrefix_SRV "srv:"
478 #define kRDataArgPrefix_String "string:"
479 #define kRDataArgPrefix_TXT "txt:"
481 #define kRecordDataSection_Name "Record Data Arguments"
482 #define kRecordDataSection_Text \
483 "A record data argument is specified in one of the following formats:\n" \
485 "Format Syntax Example\n" \
486 "Domain name domain:<domain name> domain:demo._test._tcp.local\n" \
487 "File containing record data file:<file path> file:/path/to/rdata.bin\n" \
488 "Hexadecimal string hex:<hex string> hex:c0000201 or hex:'C0 00 02 01'\n" \
489 "IPv4 address ipv4:<IPv4 address> ipv4:192.0.2.1\n" \
490 "IPv6 address ipv6:<IPv6 address> ipv6:2001:db8::1\n" \
491 "SRV record srv:<priority>,<weight>,<port>,<target> srv:0,0,64206,example.local\n" \
492 "String (w/escaped hex bytes) string:<string> string:'\\x09color=red'\n" \
493 "TXT record keys and values txt:<comma-delimited keys and values> txt:'vers=1.0,lang=en\\,es\\,fr,passreq'\n"
495 #define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text )
497 //===========================================================================================================================
499 //===========================================================================================================================
501 #define kOutputFormatStr_JSON "json"
502 #define kOutputFormatStr_XML "xml"
503 #define kOutputFormatStr_Binary "binary"
507 kOutputFormatType_Invalid
= 0,
508 kOutputFormatType_JSON
= 1,
509 kOutputFormatType_XML
= 2,
510 kOutputFormatType_Binary
= 3
514 #define FormatOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP, IS_REQUIRED ) \
515 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, "format", SHORT_HELP, IS_REQUIRED, \
517 "Use '" kOutputFormatStr_JSON "' for JavaScript Object Notation (JSON).\n" \
518 "Use '" kOutputFormatStr_XML "' for property list XML version 1.0.\n" \
519 "Use '" kOutputFormatStr_Binary "' for property list binary version 1.0.\n" \
523 //===========================================================================================================================
524 // Browse Command Options
525 //===========================================================================================================================
527 static char ** gBrowse_ServiceTypes
= NULL
;
528 static size_t gBrowse_ServiceTypesCount
= 0;
529 static const char * gBrowse_Domain
= NULL
;
530 static int gBrowse_DoResolve
= false;
531 static int gBrowse_QueryTXT
= false;
532 static int gBrowse_TimeLimitSecs
= 0;
534 static CLIOption kBrowseOpts
[] =
537 MultiStringOption( 't', "type", &gBrowse_ServiceTypes
, &gBrowse_ServiceTypesCount
, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ),
538 StringOption( 'd', "domain", &gBrowse_Domain
, "domain", "Domain in which to browse for the service type(s).", false ),
540 CLI_OPTION_GROUP( "Flags" ),
542 DNSSDFlagsOption_IncludeAWDL(),
544 CLI_OPTION_GROUP( "Operation" ),
546 BooleanOption( 0 , "resolve", &gBrowse_DoResolve
, "Resolve service instances." ),
547 BooleanOption( 0 , "queryTXT", &gBrowse_QueryTXT
, "Query TXT records of service instances." ),
548 IntegerOption( 'l', "timeLimit", &gBrowse_TimeLimitSecs
, "seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ),
554 //===========================================================================================================================
555 // GetAddrInfo Command Options
556 //===========================================================================================================================
558 static const char * gGetAddrInfo_Name
= NULL
;
559 static int gGetAddrInfo_ProtocolIPv4
= false;
560 static int gGetAddrInfo_ProtocolIPv6
= false;
561 static int gGetAddrInfo_OneShot
= false;
562 static int gGetAddrInfo_TimeLimitSecs
= 0;
564 static CLIOption kGetAddrInfoOpts
[] =
567 StringOption( 'n', "name", &gGetAddrInfo_Name
, "domain name", "Domain name to resolve.", true ),
568 BooleanOption( 0 , "ipv4", &gGetAddrInfo_ProtocolIPv4
, "Use kDNSServiceProtocol_IPv4." ),
569 BooleanOption( 0 , "ipv6", &gGetAddrInfo_ProtocolIPv6
, "Use kDNSServiceProtocol_IPv6." ),
571 CLI_OPTION_GROUP( "Flags" ),
573 DNSSDFlagsOption_AllowExpiredAnswers(),
574 DNSSDFlagsOption_DenyCellular(),
575 DNSSDFlagsOption_DenyExpensive(),
576 DNSSDFlagsOption_PathEvalDone(),
577 DNSSDFlagsOption_ReturnIntermediates(),
578 DNSSDFlagsOption_SuppressUnusable(),
579 DNSSDFlagsOption_Timeout(),
581 CLI_OPTION_GROUP( "Operation" ),
583 BooleanOption( 'o', "oneshot", &gGetAddrInfo_OneShot
, "Finish after first set of results." ),
584 IntegerOption( 'l', "timeLimit", &gGetAddrInfo_TimeLimitSecs
, "seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ),
590 //===========================================================================================================================
591 // QueryRecord Command Options
592 //===========================================================================================================================
594 static const char * gQueryRecord_Name
= NULL
;
595 static const char * gQueryRecord_Type
= NULL
;
596 static int gQueryRecord_OneShot
= false;
597 static int gQueryRecord_TimeLimitSecs
= 0;
598 static int gQueryRecord_RawRData
= false;
600 static CLIOption kQueryRecordOpts
[] =
603 StringOption( 'n', "name", &gQueryRecord_Name
, "domain name", "Full domain name of record to query.", true ),
604 StringOption( 't', "type", &gQueryRecord_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
606 CLI_OPTION_GROUP( "Flags" ),
608 DNSSDFlagsOption_AllowExpiredAnswers(),
609 DNSSDFlagsOption_DenyCellular(),
610 DNSSDFlagsOption_DenyExpensive(),
611 DNSSDFlagsOption_ForceMulticast(),
612 DNSSDFlagsOption_IncludeAWDL(),
613 DNSSDFlagsOption_PathEvalDone(),
614 DNSSDFlagsOption_ReturnIntermediates(),
615 DNSSDFlagsOption_SuppressUnusable(),
616 DNSSDFlagsOption_Timeout(),
617 DNSSDFlagsOption_UnicastResponse(),
619 CLI_OPTION_GROUP( "Operation" ),
621 BooleanOption( 'o', "oneshot", &gQueryRecord_OneShot
, "Finish after first set of results." ),
622 IntegerOption( 'l', "timeLimit", &gQueryRecord_TimeLimitSecs
, "seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ),
623 BooleanOption( 0 , "raw", &gQueryRecord_RawRData
, "Show record data as a hexdump." ),
629 //===========================================================================================================================
630 // Register Command Options
631 //===========================================================================================================================
633 static const char * gRegister_Name
= NULL
;
634 static const char * gRegister_Type
= NULL
;
635 static const char * gRegister_Domain
= NULL
;
636 static int gRegister_Port
= 0;
637 static const char * gRegister_TXT
= NULL
;
638 static int gRegister_LifetimeMs
= -1;
639 static const char ** gAddRecord_Types
= NULL
;
640 static size_t gAddRecord_TypesCount
= 0;
641 static const char ** gAddRecord_Data
= NULL
;
642 static size_t gAddRecord_DataCount
= 0;
643 static const char ** gAddRecord_TTLs
= NULL
;
644 static size_t gAddRecord_TTLsCount
= 0;
645 static const char * gUpdateRecord_Data
= NULL
;
646 static int gUpdateRecord_DelayMs
= 0;
647 static int gUpdateRecord_TTL
= 0;
649 static CLIOption kRegisterOpts
[] =
652 StringOption( 'n', "name", &gRegister_Name
, "service name", "Name of service.", false ),
653 StringOption( 't', "type", &gRegister_Type
, "service type", "Service type, e.g., \"_ssh._tcp\".", true ),
654 StringOption( 'd', "domain", &gRegister_Domain
, "domain", "Domain in which to advertise the service.", false ),
655 IntegerOption( 'p', "port", &gRegister_Port
, "port number", "Service's port number.", true ),
656 StringOption( 0 , "txt", &gRegister_TXT
, "record data", "The TXT record data. See " kRecordDataSection_Name
" below.", false ),
658 CLI_OPTION_GROUP( "Flags" ),
660 DNSSDFlagsOption_IncludeAWDL(),
661 DNSSDFlagsOption_NoAutoRename(),
663 CLI_OPTION_GROUP( "Operation" ),
664 IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs
, "ms", "Lifetime of the service registration in milliseconds.", false ),
666 CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ),
667 StringOption( 0 , "updateData", &gUpdateRecord_Data
, "record data", "Record data for the record update. See " kRecordDataSection_Name
" below.", false ),
668 IntegerOption( 0 , "updateDelay", &gUpdateRecord_DelayMs
, "ms", "Number of milliseconds after registration to wait before record update.", false ),
669 IntegerOption( 0 , "updateTTL", &gUpdateRecord_TTL
, "seconds", "Time-to-live of the updated record.", false ),
671 CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ),
672 MultiStringOption( 0 , "addType", &gAddRecord_Types
, &gAddRecord_TypesCount
, "record type", "Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ),
673 MultiStringOptionEx( 0 , "addData", &gAddRecord_Data
, &gAddRecord_DataCount
, "record data", "Additional record's data. See " kRecordDataSection_Name
" below.", false, NULL
),
674 MultiStringOption( 0 , "addTTL", &gAddRecord_TTLs
, &gAddRecord_TTLsCount
, "seconds", "Time-to-live of additional record in seconds. Use '0' for default.", false ),
680 //===========================================================================================================================
681 // RegisterRecord Command Options
682 //===========================================================================================================================
684 static const char * gRegisterRecord_Name
= NULL
;
685 static const char * gRegisterRecord_Type
= NULL
;
686 static const char * gRegisterRecord_Data
= NULL
;
687 static int gRegisterRecord_TTL
= 0;
688 static int gRegisterRecord_LifetimeMs
= -1;
689 static const char * gRegisterRecord_UpdateData
= NULL
;
690 static int gRegisterRecord_UpdateDelayMs
= 0;
691 static int gRegisterRecord_UpdateTTL
= 0;
693 static CLIOption kRegisterRecordOpts
[] =
696 StringOption( 'n', "name", &gRegisterRecord_Name
, "record name", "Fully qualified domain name of record.", true ),
697 StringOption( 't', "type", &gRegisterRecord_Type
, "record type", "Record type by name (e.g., TXT, PTR, A) or number.", true ),
698 StringOption( 'd', "data", &gRegisterRecord_Data
, "record data", "The record data. See " kRecordDataSection_Name
" below.", false ),
699 IntegerOption( 0 , "ttl", &gRegisterRecord_TTL
, "seconds", "Time-to-live in seconds. Use '0' for default.", false ),
701 CLI_OPTION_GROUP( "Flags" ),
703 DNSSDFlagsOption_IncludeAWDL(),
704 DNSSDFlagsOption_Shared(),
705 DNSSDFlagsOption_Unique(),
707 CLI_OPTION_GROUP( "Operation" ),
708 IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs
, "ms", "Lifetime of the service registration in milliseconds.", false ),
710 CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ),
711 StringOption( 0 , "updateData", &gRegisterRecord_UpdateData
, "record data", "Record data for the record update.", false ),
712 IntegerOption( 0 , "updateDelay", &gRegisterRecord_UpdateDelayMs
, "ms", "Number of milliseconds after registration to wait before record update.", false ),
713 IntegerOption( 0 , "updateTTL", &gRegisterRecord_UpdateTTL
, "seconds", "Time-to-live of the updated record.", false ),
719 //===========================================================================================================================
720 // Resolve Command Options
721 //===========================================================================================================================
723 static char * gResolve_Name
= NULL
;
724 static char * gResolve_Type
= NULL
;
725 static char * gResolve_Domain
= NULL
;
726 static int gResolve_TimeLimitSecs
= 0;
728 static CLIOption kResolveOpts
[] =
731 StringOption( 'n', "name", &gResolve_Name
, "service name", "Name of the service instance to resolve.", true ),
732 StringOption( 't', "type", &gResolve_Type
, "service type", "Type of the service instance to resolve.", true ),
733 StringOption( 'd', "domain", &gResolve_Domain
, "domain", "Domain of the service instance to resolve.", true ),
735 CLI_OPTION_GROUP( "Flags" ),
737 DNSSDFlagsOption_ForceMulticast(),
738 DNSSDFlagsOption_IncludeAWDL(),
739 DNSSDFlagsOption_ReturnIntermediates(),
740 DNSSDFlagsOption_WakeOnResolve(),
742 CLI_OPTION_GROUP( "Operation" ),
744 IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs
, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ),
750 //===========================================================================================================================
751 // Reconfirm Command Options
752 //===========================================================================================================================
754 static const char * gReconfirmRecord_Name
= NULL
;
755 static const char * gReconfirmRecord_Type
= NULL
;
756 static const char * gReconfirmRecord_Class
= NULL
;
757 static const char * gReconfirmRecord_Data
= NULL
;
759 static CLIOption kReconfirmOpts
[] =
762 StringOption( 'n', "name", &gReconfirmRecord_Name
, "record name", "Full name of the record to reconfirm.", true ),
763 StringOption( 't', "type", &gReconfirmRecord_Type
, "record type", "Type of the record to reconfirm.", true ),
764 StringOption( 'c', "class", &gReconfirmRecord_Class
, "record class", "Class of the record to reconfirm. Default class is IN.", false ),
765 StringOption( 'd', "data", &gReconfirmRecord_Data
, "record data", "The record data. See " kRecordDataSection_Name
" below.", false ),
767 CLI_OPTION_GROUP( "Flags" ),
774 //===========================================================================================================================
775 // getaddrinfo-POSIX Command Options
776 //===========================================================================================================================
778 static const char * gGAIPOSIX_HostName
= NULL
;
779 static const char * gGAIPOSIX_ServName
= NULL
;
780 static const char * gGAIPOSIX_Family
= NULL
;
781 static int gGAIPOSIXFlag_AddrConfig
= false;
782 static int gGAIPOSIXFlag_All
= false;
783 static int gGAIPOSIXFlag_CanonName
= false;
784 static int gGAIPOSIXFlag_NumericHost
= false;
785 static int gGAIPOSIXFlag_NumericServ
= false;
786 static int gGAIPOSIXFlag_Passive
= false;
787 static int gGAIPOSIXFlag_V4Mapped
= false;
788 #if( defined( AI_V4MAPPED_CFG ) )
789 static int gGAIPOSIXFlag_V4MappedCFG
= false;
791 #if( defined( AI_DEFAULT ) )
792 static int gGAIPOSIXFlag_Default
= false;
794 #if( defined( AI_UNUSABLE ) )
795 static int gGAIPOSIXFlag_Unusable
= false;
798 static CLIOption kGetAddrInfoPOSIXOpts
[] =
800 StringOption( 'n', "hostname", &gGAIPOSIX_HostName
, "hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ),
801 StringOption( 's', "servname", &gGAIPOSIX_ServName
, "servname", "Port number in decimal or service name from services(5).", false ),
803 CLI_OPTION_GROUP( "Hints" ),
804 StringOptionEx( 'f', "family", &gGAIPOSIX_Family
, "address family", "Address family to use for hints ai_family field.", false,
806 "Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n"
807 "address family is specified, then AF_UNSPEC is used.\n"
809 BooleanOption( 0 , "flag-addrconfig", &gGAIPOSIXFlag_AddrConfig
, "In hints ai_flags field, set AI_ADDRCONFIG." ),
810 BooleanOption( 0 , "flag-all", &gGAIPOSIXFlag_All
, "In hints ai_flags field, set AI_ALL." ),
811 BooleanOption( 0 , "flag-canonname", &gGAIPOSIXFlag_CanonName
, "In hints ai_flags field, set AI_CANONNAME." ),
812 BooleanOption( 0 , "flag-numerichost", &gGAIPOSIXFlag_NumericHost
, "In hints ai_flags field, set AI_NUMERICHOST." ),
813 BooleanOption( 0 , "flag-numericserv", &gGAIPOSIXFlag_NumericServ
, "In hints ai_flags field, set AI_NUMERICSERV." ),
814 BooleanOption( 0 , "flag-passive", &gGAIPOSIXFlag_Passive
, "In hints ai_flags field, set AI_PASSIVE." ),
815 BooleanOption( 0 , "flag-v4mapped", &gGAIPOSIXFlag_V4Mapped
, "In hints ai_flags field, set AI_V4MAPPED." ),
816 #if( defined( AI_V4MAPPED_CFG ) )
817 BooleanOption( 0 , "flag-v4mappedcfg", &gGAIPOSIXFlag_V4MappedCFG
, "In hints ai_flags field, set AI_V4MAPPED_CFG." ),
819 #if( defined( AI_DEFAULT ) )
820 BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default
, "In hints ai_flags field, set AI_DEFAULT." ),
822 #if( defined( AI_UNUSABLE ) )
823 BooleanOption( 0 , "flag-unusable", &gGAIPOSIXFlag_Unusable
, "In hints ai_flags field, set AI_UNUSABLE." ),
826 CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ),
830 //===========================================================================================================================
831 // ReverseLookup Command Options
832 //===========================================================================================================================
834 static const char * gReverseLookup_IPAddr
= NULL
;
835 static int gReverseLookup_OneShot
= false;
836 static int gReverseLookup_TimeLimitSecs
= 0;
838 static CLIOption kReverseLookupOpts
[] =
841 StringOption( 'a', "address", &gReverseLookup_IPAddr
, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ),
843 CLI_OPTION_GROUP( "Flags" ),
845 DNSSDFlagsOption_ForceMulticast(),
846 DNSSDFlagsOption_ReturnIntermediates(),
847 DNSSDFlagsOption_SuppressUnusable(),
849 CLI_OPTION_GROUP( "Operation" ),
851 BooleanOption( 'o', "oneshot", &gReverseLookup_OneShot
, "Finish after first set of results." ),
852 IntegerOption( 'l', "timeLimit", &gReverseLookup_TimeLimitSecs
, "seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ),
858 //===========================================================================================================================
859 // PortMapping Command Options
860 //===========================================================================================================================
862 static int gPortMapping_ProtocolTCP
= false;
863 static int gPortMapping_ProtocolUDP
= false;
864 static int gPortMapping_InternalPort
= 0;
865 static int gPortMapping_ExternalPort
= 0;
866 static int gPortMapping_TTL
= 0;
868 static CLIOption kPortMappingOpts
[] =
871 BooleanOption( 0, "tcp", &gPortMapping_ProtocolTCP
, "Use kDNSServiceProtocol_TCP." ),
872 BooleanOption( 0, "udp", &gPortMapping_ProtocolUDP
, "Use kDNSServiceProtocol_UDP." ),
873 IntegerOption( 0, "internalPort", &gPortMapping_InternalPort
, "port number", "Internal port.", false ),
874 IntegerOption( 0, "externalPort", &gPortMapping_ExternalPort
, "port number", "Requested external port. Use '0' for any external port.", false ),
875 IntegerOption( 0, "ttl", &gPortMapping_TTL
, "seconds", "Requested TTL (renewal period) in seconds. Use '0' for a default value.", false ),
877 CLI_OPTION_GROUP( "Flags" ),
880 CLI_OPTION_GROUP( "Operation" ),
887 //===========================================================================================================================
888 // BrowseAll Command Options
889 //===========================================================================================================================
891 static const char * gBrowseAll_Domain
= NULL
;
892 static const char ** gBrowseAll_ServiceTypes
= NULL
;
893 static size_t gBrowseAll_ServiceTypesCount
= 0;
894 static int gBrowseAll_BrowseTimeSecs
= 5;
895 static int gBrowseAll_ConnectTimeout
= 0;
897 static CLIOption kBrowseAllOpts
[] =
900 StringOption( 'd', "domain", &gBrowseAll_Domain
, "domain", "Domain in which to browse for the service.", false ),
901 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 ),
903 CLI_OPTION_GROUP( "Flags" ),
904 DNSSDFlagsOption_IncludeAWDL(),
906 CLI_OPTION_GROUP( "Operation" ),
907 IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs
, "seconds", "Amount of time to spend browsing in seconds. (default: 5)", false ),
908 IntegerOption( 'c', "connectTimeout", &gBrowseAll_ConnectTimeout
, "seconds", "Timeout for connection attempts. If <= 0, no connections are attempted. (default: 0)", false ),
912 //===========================================================================================================================
913 // GetNameInfo Command Options
914 //===========================================================================================================================
916 static void GetNameInfoCmd( void );
918 static char * gGetNameInfo_IPAddress
= NULL
;
919 static int gGetNameInfoFlag_DGram
= false;
920 static int gGetNameInfoFlag_NameReqd
= false;
921 static int gGetNameInfoFlag_NoFQDN
= false;
922 static int gGetNameInfoFlag_NumericHost
= false;
923 static int gGetNameInfoFlag_NumericScope
= false;
924 static int gGetNameInfoFlag_NumericServ
= false;
926 static CLIOption kGetNameInfoOpts
[] =
928 StringOption( 'a', "address", &gGetNameInfo_IPAddress
, "IP address", "IPv4 or IPv6 address to use in sockaddr structure.", true ),
930 CLI_OPTION_GROUP( "Flags" ),
931 BooleanOption( 0 , "flag-dgram", &gGetNameInfoFlag_DGram
, "Use NI_DGRAM flag." ),
932 BooleanOption( 0 , "flag-namereqd", &gGetNameInfoFlag_NameReqd
, "Use NI_NAMEREQD flag." ),
933 BooleanOption( 0 , "flag-nofqdn", &gGetNameInfoFlag_NoFQDN
, "Use NI_NOFQDN flag." ),
934 BooleanOption( 0 , "flag-numerichost", &gGetNameInfoFlag_NumericHost
, "Use NI_NUMERICHOST flag." ),
935 BooleanOption( 0 , "flag-numericscope", &gGetNameInfoFlag_NumericScope
, "Use NI_NUMERICSCOPE flag." ),
936 BooleanOption( 0 , "flag-numericserv", &gGetNameInfoFlag_NumericServ
, "Use NI_NUMERICSERV flag." ),
938 CLI_SECTION( "Notes", "See getnameinfo(3) man page for more details.\n" ),
942 //===========================================================================================================================
943 // GetAddrInfoStress Command Options
944 //===========================================================================================================================
946 static int gGAIStress_TestDurationSecs
= 0;
947 static int gGAIStress_ConnectionCount
= 0;
948 static int gGAIStress_DurationMinMs
= 0;
949 static int gGAIStress_DurationMaxMs
= 0;
950 static int gGAIStress_RequestCountMax
= 0;
952 static CLIOption kGetAddrInfoStressOpts
[] =
956 CLI_OPTION_GROUP( "Flags" ),
957 DNSSDFlagsOption_ReturnIntermediates(),
958 DNSSDFlagsOption_SuppressUnusable(),
960 CLI_OPTION_GROUP( "Operation" ),
961 IntegerOption( 0, "testDuration", &gGAIStress_TestDurationSecs
, "seconds", "Stress test duration in seconds. Use '0' for forever.", false ),
962 IntegerOption( 0, "connectionCount", &gGAIStress_ConnectionCount
, "integer", "Number of simultaneous DNS-SD connections.", true ),
963 IntegerOption( 0, "requestDurationMin", &gGAIStress_DurationMinMs
, "ms", "Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
964 IntegerOption( 0, "requestDurationMax", &gGAIStress_DurationMaxMs
, "ms", "Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
965 IntegerOption( 0, "consecutiveRequestMax", &gGAIStress_RequestCountMax
, "integer", "Maximum number of requests on a connection before restarting it.", true ),
969 //===========================================================================================================================
970 // DNSQuery Command Options
971 //===========================================================================================================================
973 static char * gDNSQuery_Name
= NULL
;
974 static char * gDNSQuery_Type
= "A";
975 static char * gDNSQuery_Server
= NULL
;
976 static int gDNSQuery_TimeLimitSecs
= 5;
977 static int gDNSQuery_UseTCP
= false;
978 static int gDNSQuery_Flags
= kDNSHeaderFlag_RecursionDesired
;
979 static int gDNSQuery_RawRData
= false;
980 static int gDNSQuery_Verbose
= false;
982 #if( TARGET_OS_DARWIN )
983 #define kDNSQueryServerOptionIsRequired false
985 #define kDNSQueryServerOptionIsRequired true
988 static CLIOption kDNSQueryOpts
[] =
990 StringOption( 'n', "name", &gDNSQuery_Name
, "name", "Question name (QNAME) to put in DNS query message.", true ),
991 StringOption( 't', "type", &gDNSQuery_Type
, "type", "Question type (QTYPE) to put in DNS query message. Default value is 'A'.", false ),
992 StringOption( 's', "server", &gDNSQuery_Server
, "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired
),
993 IntegerOption( 'l', "timeLimit", &gDNSQuery_TimeLimitSecs
, "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ),
994 BooleanOption( 0 , "tcp", &gDNSQuery_UseTCP
, "Send the DNS query via TCP instead of UDP." ),
995 IntegerOption( 'f', "flags", &gDNSQuery_Flags
, "flags", "16-bit value for DNS header flags/codes field. Default value is 0x0100 (Recursion Desired).", false ),
996 BooleanOption( 0 , "raw", &gDNSQuery_RawRData
, "Present record data as a hexdump." ),
997 BooleanOption( 'v', "verbose", &gDNSQuery_Verbose
, "Prints the DNS message to be sent to the server." ),
1001 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
1002 //===========================================================================================================================
1003 // DNSCrypt Command Options
1004 //===========================================================================================================================
1006 static char * gDNSCrypt_ProviderName
= NULL
;
1007 static char * gDNSCrypt_ProviderKey
= NULL
;
1008 static char * gDNSCrypt_Name
= NULL
;
1009 static char * gDNSCrypt_Type
= NULL
;
1010 static char * gDNSCrypt_Server
= NULL
;
1011 static int gDNSCrypt_TimeLimitSecs
= 5;
1012 static int gDNSCrypt_RawRData
= false;
1013 static int gDNSCrypt_Verbose
= false;
1015 static CLIOption kDNSCryptOpts
[] =
1017 StringOption( 'p', "providerName", &gDNSCrypt_ProviderName
, "name", "The DNSCrypt provider name.", true ),
1018 StringOption( 'k', "providerKey", &gDNSCrypt_ProviderKey
, "hex string", "The DNSCrypt provider's public signing key.", true ),
1019 StringOption( 'n', "name", &gDNSCrypt_Name
, "name", "Question name (QNAME) to put in DNS query message.", true ),
1020 StringOption( 't', "type", &gDNSCrypt_Type
, "type", "Question type (QTYPE) to put in DNS query message.", true ),
1021 StringOption( 's', "server", &gDNSCrypt_Server
, "IP address", "DNS server's IPv4 or IPv6 address.", true ),
1022 IntegerOption( 'l', "timeLimit", &gDNSCrypt_TimeLimitSecs
, "seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ),
1023 BooleanOption( 0 , "raw", &gDNSCrypt_RawRData
, "Present record data as a hexdump." ),
1024 BooleanOption( 'v', "verbose", &gDNSCrypt_Verbose
, "Prints the DNS message to be sent to the server." ),
1029 //===========================================================================================================================
1030 // MDNSQuery Command Options
1031 //===========================================================================================================================
1033 static char * gMDNSQuery_Name
= NULL
;
1034 static char * gMDNSQuery_Type
= NULL
;
1035 static int gMDNSQuery_SourcePort
= 0;
1036 static int gMDNSQuery_IsQU
= false;
1037 static int gMDNSQuery_RawRData
= false;
1038 static int gMDNSQuery_UseIPv4
= false;
1039 static int gMDNSQuery_UseIPv6
= false;
1040 static int gMDNSQuery_AllResponses
= false;
1041 static int gMDNSQuery_ReceiveSecs
= 1;
1043 static CLIOption kMDNSQueryOpts
[] =
1045 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
1046 StringOption( 'n', "name", &gMDNSQuery_Name
, "name", "Question name (QNAME) to put in mDNS message.", true ),
1047 StringOption( 't', "type", &gMDNSQuery_Type
, "type", "Question type (QTYPE) to put in mDNS message.", true ),
1048 IntegerOption( 'p', "sourcePort", &gMDNSQuery_SourcePort
, "port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ),
1049 BooleanOption( 'u', "QU", &gMDNSQuery_IsQU
, "Set the unicast-response bit, i.e., send a QU question." ),
1050 BooleanOption( 0 , "raw", &gMDNSQuery_RawRData
, "Present record data as a hexdump." ),
1051 BooleanOption( 0 , "ipv4", &gMDNSQuery_UseIPv4
, "Use IPv4." ),
1052 BooleanOption( 0 , "ipv6", &gMDNSQuery_UseIPv6
, "Use IPv6." ),
1053 BooleanOption( 'a', "allResponses", &gMDNSQuery_AllResponses
, "Print all received mDNS messages, not just those containing answers." ),
1054 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 ),
1058 //===========================================================================================================================
1059 // MDNSCollider Command Options
1060 //===========================================================================================================================
1062 #define kMDNSColliderProgramSection_Intro \
1063 "Programs dictate when the collider sends out unsolicited response messages for its record and how the collider\n" \
1064 "ought to react to probe queries that match its record's name, if at all.\n" \
1066 "For example, suppose that the goal is to cause a specific unique record in the verified state to be renamed.\n" \
1067 "The collider should be invoked such that its record's name is equal to that of the record being targeted. Also,\n" \
1068 "the record's type and data should be such that no record with that name, type, and data combination currently\n" \
1069 "exists. If the mDNS responder that owns the record follows sections 8.1 and 9 of RFC 6762, then the goal can be\n" \
1070 "accomplished with the following program:\n" \
1072 " probes 3r; send; wait 5000\n" \
1074 "The first command, 'probes 3r', tells the collider to respond to the next three probe queries that match its\n" \
1075 "record's name. The second command, makes the collider send an unsolicited response message that contains its\n" \
1076 "record in the answer section. The third command makes the collider wait for five seconds before exiting, which\n" \
1077 "is more than enough time for the collider to respond to probe queries.\n" \
1079 "The send command will cause the targeted record to go into the probing state per section 9 since the collider's\n" \
1080 "record conflicts with target record. Per the probes command, the subsequent probe query sent during the probing\n" \
1081 "state will be answered by the collider, which will cause the record to be renamed per section 8.1.\n"
1083 #define kMDNSColliderProgramSection_Probes \
1084 "The probes command defines how the collider ought to react to probe queries that match its record's name.\n" \
1086 "Usage: probes [<action-string>]\n" \
1088 "The syntax for an action-string is\n" \
1090 " <action-string> ::= <action> | <action-string> \"-\" <action>\n" \
1091 " <action> ::= [<repeat-count>] <action-code>\n" \
1092 " <repeat-count> ::= \"1\" | \"2\" | ... | \"10\"\n" \
1093 " <action-code> ::= \"n\" | \"r\" | \"u\" | \"m\" | \"p\"\n" \
1095 "An expanded action-string is defined as\n" \
1097 " <expanded-action-string> ::= <action-code> | <expanded-action-string> \"-\" <action-code>\n" \
1099 "The action-string argument is converted into an expanded-action-string by expanding each action with a\n" \
1100 "repeat-count into an expanded-action-string consisting of exactly <repeat-count> <action-code>s. For example,\n" \
1101 "2n-r expands to n-n-r. Action-strings that expand to expanded-action-strings with more than 10 action-codes\n" \
1102 "are not allowed.\n" \
1104 "When the probes command is executed, it does two things. Firstly, it resets to zero the collider's count of\n" \
1105 "probe queries that match its record's name. Secondly, it defines how the collider ought to react to such probe\n" \
1106 "queries based on the action-string argument. Specifically, the nth action-code in the expanded version of the\n" \
1107 "action-string argument defines how the collider ought to react to the nth received probe query:\n" \
1111 " n Do nothing.\n" \
1112 " r Respond to the probe query.\n" \
1113 " u Respond to the probe query via unicast.\n" \
1114 " m Respond to the probe query via multicast.\n" \
1115 " p Multicast own probe query. (Useful for causing simultaneous probe scenarios.)\n" \
1117 "Note: If no action is defined for a received probe query, then the collider does nothing, i.e., it doesn't send\n" \
1118 "a response nor does it multicast its own probe query.\n"
1120 #define kMDNSColliderProgramSection_Send \
1121 "The send command multicasts an unsolicited mDNS response containing the collider's record in the answer\n" \
1122 "section, which can be used to force unique records with the same record name into the probing state.\n" \
1126 #define kMDNSColliderProgramSection_Wait \
1127 "The wait command pauses program execution for the interval of time specified by its argument.\n" \
1129 "Usage: wait <milliseconds>\n"
1131 #define kMDNSColliderProgramSection_Loop \
1132 "The loop command starts a counting loop. The done statement marks the end of the loop body. The loop command's\n" \
1133 "argument specifies the number of loop iterations. Note: Loop nesting is supported up to a depth of 16.\n" \
1135 "Usage: loop <non-zero count>; ... ; done\n" \
1137 "For example, the following program sends three unsolicited responses at an approximate rate of one per second:\n" \
1139 " loop 3; wait 1000; send; done"
1141 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
1143 static const char * gMDNSCollider_Name
= NULL
;
1144 static const char * gMDNSCollider_Type
= NULL
;
1145 static const char * gMDNSCollider_RecordData
= NULL
;
1146 static int gMDNSCollider_UseIPv4
= false;
1147 static int gMDNSCollider_UseIPv6
= false;
1148 static const char * gMDNSCollider_Program
= NULL
;
1150 static CLIOption kMDNSColliderOpts
[] =
1152 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
1153 StringOption( 'n', "name", &gMDNSCollider_Name
, "name", "Collider's record name.", true ),
1154 StringOption( 't', "type", &gMDNSCollider_Type
, "type", "Collider's record type.", true ),
1155 StringOption( 'd', "data", &gMDNSCollider_RecordData
, "record data", "Collider's record data. See " kRecordDataSection_Name
" below.", true ),
1156 StringOption( 'p', "program", &gMDNSCollider_Program
, "program", "Program to execute. See Program section below.", true ),
1157 BooleanOption( 0 , "ipv4", &gMDNSCollider_UseIPv4
, "Use IPv4." ),
1158 BooleanOption( 0 , "ipv6", &gMDNSCollider_UseIPv6
, "Use IPv6." ),
1160 RecordDataSection(),
1161 CLI_SECTION( "Program", kMDNSColliderProgramSection_Intro
),
1162 CLI_SECTION( "Program Command: probes", kMDNSColliderProgramSection_Probes
),
1163 CLI_SECTION( "Program Command: send", kMDNSColliderProgramSection_Send
),
1164 CLI_SECTION( "Program Command: wait", kMDNSColliderProgramSection_Wait
),
1165 CLI_SECTION( "Program Command: loop", kMDNSColliderProgramSection_Loop
),
1169 static void MDNSColliderCmd( void );
1171 //===========================================================================================================================
1172 // PIDToUUID Command Options
1173 //===========================================================================================================================
1175 static int gPIDToUUID_PID
= 0;
1177 static CLIOption kPIDToUUIDOpts
[] =
1179 IntegerOption( 'p', "pid", &gPIDToUUID_PID
, "PID", "Process ID.", true ),
1183 //===========================================================================================================================
1184 // DNSServer Command Options
1185 //===========================================================================================================================
1187 #define kDNSServerInfoText_Intro \
1188 "The DNS server answers certain queries in the d.test. domain. Responses are dynamically generated based on the\n" \
1189 "presence of special labels in the query's QNAME. There are currently eight types of special labels that can be\n" \
1190 "used to generate specific responses: Alias labels, Alias-TTL labels, Count labels, Tag labels, TTL labels, the\n" \
1191 "IPv4 label, the IPv6 label, and SRV labels.\n" \
1193 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n"
1195 #define kDNSServerInfoText_NameExistence \
1196 "A name is considered to exist if it's an Address name or an SRV name.\n" \
1198 "An Address name is defined as a name that ends with d.test., and the other labels, if any, and in no particular\n" \
1199 "order, unless otherwise noted, consist of\n" \
1201 " 1. at most one Alias or Alias-TTL label as the first label;\n" \
1202 " 2. at most one Count label;\n" \
1203 " 3. zero or more Tag labels;\n" \
1204 " 4. at most one TTL label; and\n" \
1205 " 5. at most one IPv4 or IPv6 label.\n" \
1207 "An SRV name is defined as a name with the following form:\n" \
1209 " _<service>._<proto>[.<parent domain>][.<SRV label 1>[.<target 1>][.<SRV label 2>[.<target 2>][...]]].d.test.\n" \
1211 "See \"SRV Names\" for details.\n"
1213 #define kDNSServerInfoText_ResourceRecords \
1214 "Currently, the server only supports CNAME, A, AAAA, and SRV records.\n" \
1216 "Address names that begin with an Alias or Alias-TTL label are aliases of canonical names, i.e., they're the\n" \
1217 "names of CNAME records. See \"Alias Labels\" and \"Alias-TTL Labels\" for details.\n" \
1219 "A canonical Address name can exclusively be the name of one or more A records, can exclusively be the name or\n" \
1220 "one or more AAAA records, or can be the name of both A and AAAA records. Address names that contain an IPv4\n" \
1221 "label have at least one A record, but no AAAA records. Address names that contain an IPv6 label, have at least\n" \
1222 "one AAAA record, but no A records. All other Address names have at least one A record and at least one AAAA\n" \
1223 "record. See \"Count Labels\" for how the number of address records for a given Address name is determined.\n" \
1225 "A records contain IPv4 addresses in the 203.0.113.0/24 block, while AAAA records contain IPv6 addresses in the\n" \
1226 "2001:db8:1::/120 block. Both of these address blocks are reserved for documentation. See\n" \
1227 "<https://tools.ietf.org/html/rfc5737> and <https://tools.ietf.org/html/rfc3849>.\n" \
1229 "SRV names are names of SRV records.\n" \
1231 "Unless otherwise specified, all resource records will use a default TTL. The default TTL can be set with the\n" \
1232 "--defaultTTL option. See \"Alias-TTL Labels\" and \"TTL Labels\" for details on how to query for CNAME, A, and\n" \
1233 "AAAA records with specific TTL values.\n"
1235 #define kDNSServerInfoText_AliasLabel \
1236 "Alias labels are of the form \"alias\" or \"alias-N\", where N is an integer in [2, 2^31 - 1].\n" \
1238 "If QNAME is an Address name and its first label is Alias label \"alias-N\", then the response will contain\n" \
1239 "exactly N CNAME records:\n" \
1241 " 1. For each i in [3, N], the response will contain a CNAME record whose name is identical to QNAME, except\n" \
1242 " that the first label is \"alias-i\" instead, and whose RDATA is the name of the other CNAME record whose\n" \
1243 " name has \"alias-(i - 1)\" as its first label.\n" \
1245 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1246 " is \"alias-2\" instead, and whose RDATA is the name identical to QNAME, except that the first label is\n" \
1247 " \"alias\" instead.\n" \
1249 " 3. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1250 " is \"alias\" instead, and whose RDATA is the name identical to QNAME minus its first label.\n" \
1252 "If QNAME is an Address name and its first label is Alias label \"alias\", then the response will contain a\n" \
1253 "single CNAME record. The CNAME record's name will be equal to QNAME and its RDATA will be the name identical to\n" \
1254 "QNAME minus its first label.\n" \
1256 "Example. A response to a query with a QNAME of alias-3.count-5.d.test will contain the following CNAME\n" \
1259 " alias-4.count-5.d.test. 60 IN CNAME alias-3.count-5.d.test.\n" \
1260 " alias-3.count-5.d.test. 60 IN CNAME alias-2.count-5.d.test.\n" \
1261 " alias-2.count-5.d.test. 60 IN CNAME alias.count-5.d.test.\n" \
1262 " alias.count-5.d.test. 60 IN CNAME count-5.d.test.\n"
1264 #define kDNSServerInfoText_AliasTTLLabel \
1265 "Alias-TTL labels are of the form \"alias-ttl-T_1[-T_2[...-T_N]]\", where each T_i is an integer in\n" \
1266 "[0, 2^31 - 1] and N is a positive integer bounded by the size of the maximum legal label length (63 octets).\n" \
1268 "If QNAME is an Address name and its first label is Alias-TTL label \"alias-ttl-T_1...-T_N\", then the response\n" \
1269 "will contain exactly N CNAME records:\n" \
1271 " 1. For each i in [1, N - 1], the response will contain a CNAME record whose name is identical to QNAME,\n" \
1272 " except that the first label is \"alias-ttl-T_i...-T_N\" instead, whose TTL value is T_i, and whose RDATA\n" \
1273 " is the name of the other CNAME record whose name has \"alias-ttl-T_(i+1)...-T_N\" as its first label.\n" \
1275 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1276 " is \"alias-ttl-T_N\", whose TTL is T_N, and whose RDATA is identical to QNAME stripped of its first\n" \
1279 "Example. A response to a query with a QNAME of alias-ttl-20-40-80.count-5.d.test will contain the following\n" \
1280 "CNAME records:\n" \
1282 " alias-ttl-20-40-80.count-5.d.test. 20 IN CNAME alias-ttl-40-80.count-5.d.test.\n" \
1283 " alias-ttl-40-80.count-5.d.test. 40 IN CNAME alias-ttl-80.count-5.d.test.\n" \
1284 " alias-ttl-80.count-5.d.test. 80 IN CNAME count-5.d.test.\n"
1286 #define kDNSServerInfoText_CountLabel \
1287 "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" \
1288 "an integer in [N_1, 255].\n" \
1290 "If QNAME is an Address name, contains Count label \"count-N\", and has the type of address records specified by\n" \
1291 "QTYPE, then the response will contain exactly N address records:\n" \
1293 " 1. For i in [1, N], the response will contain an address record of type QTYPE whose name is equal to QNAME\n" \
1294 " and whose RDATA is an address equal to a constant base address + i.\n" \
1296 " 2. The address records will be ordered by the address contained in RDATA in ascending order.\n" \
1298 "Example. A response to an A record query with a QNAME of alias.count-3.d.test will contain the following A\n" \
1301 " count-3.d.test. 60 IN A 203.0.113.1\n" \
1302 " count-3.d.test. 60 IN A 203.0.113.2\n" \
1303 " count-3.d.test. 60 IN A 203.0.113.3\n" \
1305 "If QNAME is an Address name, contains Count label \"count-N_1-N_2\", and has the type of address records\n" \
1306 "specified by QTYPE, then the response will contain exactly N_1 address records:\n" \
1308 " 1. Each of the address records will be of type QTYPE, have name equal to QNAME, and have as its RDATA a\n" \
1309 " unique address equal to a constant base address + i, where i is a randomly chosen integer in [1, N_2].\n" \
1311 " 2. The order of the address records will be random.\n" \
1313 "Example. A response to a AAAA record query with a QNAME of count-3-100.ttl-20.d.test could contain the\n" \
1314 "following AAAA records:\n" \
1316 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::c\n" \
1317 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::3a\n" \
1318 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8:1::4f\n" \
1320 "If QNAME is an Address name, but doesn't have the type of address records specified by QTYPE, then the response\n" \
1321 "will contain no address records, regardless of whether it contains a Count label.\n" \
1323 "Address names that don't have a Count label are treated as though they contain a count label equal to\n" \
1326 #define kDNSServerInfoText_TagLabel \
1327 "Tag labels are labels prefixed with \"tag-\" and contain zero or more arbitrary octets after the prefix.\n" \
1329 "This type of label exists to allow testers to \"uniquify\" domain names. Tag labels can also serve as padding\n" \
1330 "to increase the sizes of domain names.\n"
1332 #define kDNSServerInfoText_TTLLabel \
1333 "TTL labels are of the form \"ttl-T\", where T is an integer in [0, 2^31 - 1].\n" \
1335 "If QNAME is an Address name and contains TTL label \"ttl-T\", then all non-CNAME records contained in the\n" \
1336 "response will have a TTL value equal to T.\n"
1338 #define kDNSServerInfoText_IPv4Label \
1339 "The IPv4 label is \"ipv4\". See \"Resource Records\" for the affect of this label.\n"
1341 #define kDNSServerInfoText_IPv6Label \
1342 "The IPv6 label is \"ipv6\". See \"Resource Records\" for the affect of this label.\n"
1344 #define kDNSServerInfoText_SRVNames \
1345 "SRV labels are of the form \"srv-R-W-P\", where R, W, and P are integers in [0, 2^16 - 1].\n" \
1347 "After the first two labels, i.e., the service and protocol labels, the sequence of labels, which may be empty,\n" \
1348 "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" \
1349 "the target hostname of each of the SRV name's SRV records.\n" \
1351 "If QNAME is an SRV name and QTYPE is SRV, then for each SRV label, the response will contain an SRV record with\n" \
1352 "priority R, weight W, port P, and target hostname <target>[.<parent domain>]., where <target> is the sequence\n" \
1353 "of labels, which may be empty, that follows the SRV label leading up to either the next SRV label or the\n" \
1354 "d.test. labels, whichever comes first.\n" \
1356 "Example. A response to an SRV record query with a QNAME of\n" \
1357 "_http._tcp.example.com.srv-0-0-80.www.srv-1-0-8080.www.d.test. will contain the following SRV records:\n" \
1359 "_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" \
1360 "_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"
1362 #define kDNSServerInfoText_BadUDPMode \
1363 "The purpose of Bad UDP mode is to test mDNSResponder's TCP fallback mechanism by which mDNSResponder reissues a\n" \
1364 "UDP query as a TCP query if the UDP response contains the expected QNAME, QTYPE, and QCLASS, but a message ID\n" \
1365 "that's not equal to the query's message ID.\n" \
1367 "This mode is identical to the normal mode except that all responses sent via UDP have a message ID equal to the\n" \
1368 "query's message ID plus one. Also, in this mode, to aid in debugging, A records in responses sent via UDP have\n" \
1369 "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" \
1370 "base address, and AAAA records in responses sent via UDP have IPv6 addresses in the ::ffff:0:0/120 block\n" \
1371 "instead of the 2001:db8:1::/120 block, i.e., ::ffff:0:0 is used as the IPv6 base address.\n"
1373 static int gDNSServer_LoopbackOnly
= false;
1374 static int gDNSServer_Foreground
= false;
1375 static int gDNSServer_ResponseDelayMs
= 0;
1376 static int gDNSServer_DefaultTTL
= 60;
1377 static int gDNSServer_Port
= kDNSPort
;
1378 static const char * gDNSServer_DomainOverride
= NULL
;
1379 #if( TARGET_OS_DARWIN )
1380 static const char * gDNSServer_FollowPID
= NULL
;
1382 static int gDNSServer_BadUDPMode
= false;
1384 static CLIOption kDNSServerOpts
[] =
1386 BooleanOption( 'l', "loopback", &gDNSServer_LoopbackOnly
, "Bind to to the loopback interface." ),
1387 BooleanOption( 'f', "foreground", &gDNSServer_Foreground
, "Direct log output to stdout instead of system logging." ),
1388 IntegerOption( 'd', "responseDelay", &gDNSServer_ResponseDelayMs
, "ms", "The amount of additional delay in milliseconds to apply to responses. (default: 0)", false ),
1389 IntegerOption( 0 , "defaultTTL", &gDNSServer_DefaultTTL
, "seconds", "Resource record TTL value to use when unspecified. (default: 60)", false ),
1390 IntegerOption( 'p', "port", &gDNSServer_Port
, "port number", "UDP/TCP port number to use. Use 0 for any port. (default: 53)", false ),
1391 StringOption( 0 , "domain", &gDNSServer_DomainOverride
, "domain", "Used to override 'd.test.' as the server's domain.", false ),
1392 #if( TARGET_OS_DARWIN )
1393 StringOption( 0 , "follow", &gDNSServer_FollowPID
, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1395 BooleanOption( 0 , "badUDPMode", &gDNSServer_BadUDPMode
, "Run in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1397 CLI_SECTION( "Intro", kDNSServerInfoText_Intro
),
1398 CLI_SECTION( "Name Existence", kDNSServerInfoText_NameExistence
),
1399 CLI_SECTION( "Resource Records", kDNSServerInfoText_ResourceRecords
),
1400 CLI_SECTION( "Alias Labels", kDNSServerInfoText_AliasLabel
),
1401 CLI_SECTION( "Alias-TTL Labels", kDNSServerInfoText_AliasTTLLabel
),
1402 CLI_SECTION( "Count Labels", kDNSServerInfoText_CountLabel
),
1403 CLI_SECTION( "Tag Labels", kDNSServerInfoText_TagLabel
),
1404 CLI_SECTION( "TTL Labels", kDNSServerInfoText_TTLLabel
),
1405 CLI_SECTION( "IPv4 Label", kDNSServerInfoText_IPv4Label
),
1406 CLI_SECTION( "IPv6 Label", kDNSServerInfoText_IPv6Label
),
1407 CLI_SECTION( "SRV Names", kDNSServerInfoText_SRVNames
),
1408 CLI_SECTION( "Bad UDP Mode", kDNSServerInfoText_BadUDPMode
),
1412 static void DNSServerCmd( void );
1414 //===========================================================================================================================
1415 // MDNSReplier Command Options
1416 //===========================================================================================================================
1418 #define kMDNSReplierPortBase 50000
1420 #define kMDNSReplierInfoText_Intro \
1421 "The mDNS replier answers mDNS queries for its authoritative records. These records are of class IN and of types\n" \
1422 "PTR, SRV, TXT, A, and AAAA as described below.\n" \
1424 "Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n"
1426 #define kMDNSReplierInfoText_Parameters \
1427 "There are five parameters that control the replier's set of authoritative records.\n" \
1429 " 1. <hostname> is the base name used for service instance names and the names of A and AAAA records. This\n" \
1430 " parameter is specified with the --hostname option.\n" \
1431 " 2. <tag> is an arbitrary string used to uniquify service types. This parameter is specified with the --tag\n" \
1433 " 3. N_max in an integer in [1, 65535] and limits service types to those that have no more than N_max\n" \
1434 " instances. It also limits the number of hostnames to N_max, i.e., <hostname>.local.,\n" \
1435 " <hostname>-1.local., ..., <hostname>-N_max.local. This parameter is specified with the\n" \
1436 " --maxInstanceCount option.\n" \
1437 " 4. N_a is an integer in [1, 255] and the number of A records per hostname. This parameter is specified\n" \
1438 " with the --countA option.\n" \
1439 " 5. N_aaaa is an integer in [1, 255] and the number of AAAA records per hostname. This parameter is\n" \
1440 " specified with the --countAAAA option.\n"
1442 #define kMDNSReplierInfoText_PTR \
1443 "The replier's authoritative PTR records have names of the form _t-<tag>-<L>-<N>._tcp.local., where L is an\n" \
1444 "integer in [1, 65535], and N is an integer in [1, N_max].\n" \
1446 "For a given L and N, the replier has exactly N authoritative PTR records:\n" \
1448 " 1. The first PTR record is defined as\n" \
1450 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n" \
1454 " RDATA: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1456 " 2. For each i in [2, N], there is one PTR record defined as\n" \
1458 " NAME: _t-<tag>-<L>-<N>._tcp.local.\n" \
1462 " RDATA: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n"
1464 #define kMDNSReplierInfoText_SRV \
1465 "The replier's authoritative SRV records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n" \
1466 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n" \
1467 "\"<hostname> (<i>)\", where i is in [2, N].\n" \
1469 "For a given L and N, the replier has exactly N authoritative SRV records:\n" \
1471 " 1. The first SRV record is defined as\n" \
1473 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1480 " Port: (50000 + L) mod 2^16\n" \
1481 " Target: <hostname>.local.\n" \
1483 " 2. For each i in [2, N], there is one SRV record defined as:\n" \
1485 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n" \
1492 " Port: (50000 + L) mod 2^16\n" \
1493 " Target: <hostname>-<i>.local.\n"
1495 #define kMDNSReplierInfoText_TXT \
1496 "The replier's authoritative TXT records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n" \
1497 "where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n" \
1498 "\"<hostname> (<i>)\", where i is in [2, N].\n" \
1500 "For a given L and N, the replier has exactly N authoritative TXT records:\n" \
1502 " 1. The first TXT record is defined as\n" \
1504 " NAME: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n" \
1509 " RDATA: <one or more strings with an aggregate length of L octets>\n" \
1511 " 2. For each i in [2, N], there is one TXT record:\n" \
1513 " NAME: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n" \
1518 " RDATA: <one or more strings with an aggregate length of L octets>\n" \
1520 "The RDATA of each TXT record is exactly L octets and consists of a repeating series of the 15-byte string\n" \
1521 "\"hash=0x<32-bit FNV-1 hash of the record name as an 8-character hexadecimal string>\". The last instance of\n" \
1522 "the string may be truncated to satisfy the TXT record data's size requirement.\n"
1524 #define kMDNSReplierInfoText_A \
1525 "The replier has exactly N_max x N_a authoritative A records:\n" \
1527 " 1. For each j in [1, N_a], an A record is defined as\n" \
1529 " NAME: <hostname>.local.\n" \
1534 " RDATA: 0.0.1.<j>\n" \
1536 " 2. For each i in [2, N_max], for each j in [1, N_a], an A record is defined as\n" \
1538 " NAME: <hostname>-<i>.local.\n" \
1543 " RDATA: 0.<ceil(i / 256)>.<i mod 256>.<j>\n"
1545 #define kMDNSReplierInfoText_AAAA \
1546 "The replier has exactly N_max x N_aaaa authoritative AAAA records:\n" \
1548 " 1. For each j in [1, N_aaaa], a AAAA record is defined as\n" \
1550 " NAME: <hostname>.local.\n" \
1555 " RDATA: 2001:db8:2::1:<j>\n" \
1557 " 2. For each i in [2, N_max], for each j in [1, N_aaaa], a AAAA record is defined as\n" \
1559 " NAME: <hostname>-<i>.local.\n" \
1564 " RDATA: 2001:db8:2::<i>:<j>\n"
1566 #define kMDNSReplierInfoText_Responses \
1567 "When generating answers for a query message, any two records pertaining to the same hostname will be grouped\n" \
1568 "together in the same response message, and any two records pertaining to different hostnames will be in\n" \
1569 "separate response messages.\n"
1571 static const char * gMDNSReplier_Hostname
= NULL
;
1572 static const char * gMDNSReplier_ServiceTypeTag
= NULL
;
1573 static int gMDNSReplier_MaxInstanceCount
= 1000;
1574 static int gMDNSReplier_NoAdditionals
= false;
1575 static int gMDNSReplier_RecordCountA
= 1;
1576 static int gMDNSReplier_RecordCountAAAA
= 1;
1577 static double gMDNSReplier_UnicastDropRate
= 0.0;
1578 static double gMDNSReplier_MulticastDropRate
= 0.0;
1579 static int gMDNSReplier_MaxDropCount
= 0;
1580 static int gMDNSReplier_UseIPv4
= false;
1581 static int gMDNSReplier_UseIPv6
= false;
1582 static int gMDNSReplier_Foreground
= false;
1583 static const char * gMDNSReplier_FollowPID
= NULL
;
1585 static CLIOption kMDNSReplierOpts
[] =
1587 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
1588 StringOption( 'n', "hostname", &gMDNSReplier_Hostname
, "string", "Base name to use for hostnames and service instance names.", true ),
1589 StringOption( 't', "tag", &gMDNSReplier_ServiceTypeTag
, "string", "Tag to use for service types, e.g., _t-<tag>-<TXT size>-<count>._tcp.", true ),
1590 IntegerOption( 'c', "maxInstanceCount", &gMDNSReplier_MaxInstanceCount
, "count", "Maximum number of service instances. (default: 1000)", false ),
1591 BooleanOption( 0 , "noAdditionals", &gMDNSReplier_NoAdditionals
, "When answering queries, don't include any additional records." ),
1592 IntegerOption( 0 , "countA", &gMDNSReplier_RecordCountA
, "count", "Number of A records per hostname. (default: 1)", false ),
1593 IntegerOption( 0 , "countAAAA", &gMDNSReplier_RecordCountAAAA
, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1594 DoubleOption( 0 , "udrop", &gMDNSReplier_UnicastDropRate
, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1595 DoubleOption( 0 , "mdrop", &gMDNSReplier_MulticastDropRate
, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1596 IntegerOption( 0 , "maxDropCount", &gMDNSReplier_MaxDropCount
, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1597 BooleanOption( 0 , "ipv4", &gMDNSReplier_UseIPv4
, "Use IPv4." ),
1598 BooleanOption( 0 , "ipv6", &gMDNSReplier_UseIPv6
, "Use IPv6." ),
1599 BooleanOption( 'f', "foreground", &gMDNSReplier_Foreground
, "Direct log output to stdout instead of system logging." ),
1600 #if( TARGET_OS_DARWIN )
1601 StringOption( 0 , "follow", &gMDNSReplier_FollowPID
, "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1604 CLI_SECTION( "Intro", kMDNSReplierInfoText_Intro
),
1605 CLI_SECTION( "Authoritative Record Parameters", kMDNSReplierInfoText_Parameters
),
1606 CLI_SECTION( "Authoritative PTR Records", kMDNSReplierInfoText_PTR
),
1607 CLI_SECTION( "Authoritative SRV Records", kMDNSReplierInfoText_SRV
),
1608 CLI_SECTION( "Authoritative TXT Records", kMDNSReplierInfoText_TXT
),
1609 CLI_SECTION( "Authoritative A Records", kMDNSReplierInfoText_A
),
1610 CLI_SECTION( "Authoritative AAAA Records", kMDNSReplierInfoText_AAAA
),
1611 CLI_SECTION( "Responses", kMDNSReplierInfoText_Responses
),
1615 static void MDNSReplierCmd( void );
1617 //===========================================================================================================================
1618 // Test Command Options
1619 //===========================================================================================================================
1621 #define kTestExitStatusSection_Name "Exit Status"
1622 #define kTestExitStatusSection_Text \
1623 "This test command can exit with one of three status codes:\n" \
1625 "0 - The test ran to completion and passed.\n" \
1626 "1 - A fatal error prevented the test from completing.\n" \
1627 "2 - The test ran to completion, but it or a subtest failed. See test output for details.\n" \
1629 "Note: The pass/fail status applies to the correctness or results. It does not necessarily imply anything about\n" \
1632 #define TestExitStatusSection() CLI_SECTION( kTestExitStatusSection_Name, kTestExitStatusSection_Text )
1634 #define kGAIPerfTestSuiteName_Basic "basic"
1635 #define kGAIPerfTestSuiteName_Advanced "advanced"
1637 static const char * gGAIPerf_TestSuite
= NULL
;
1638 static int gGAIPerf_CallDelayMs
= 10;
1639 static int gGAIPerf_ServerDelayMs
= 10;
1640 static int gGAIPerf_SkipPathEvalulation
= false;
1641 static int gGAIPerf_BadUDPMode
= false;
1642 static int gGAIPerf_IterationCount
= 100;
1643 static const char * gGAIPerf_OutputFilePath
= NULL
;
1644 static const char * gGAIPerf_OutputFormat
= kOutputFormatStr_JSON
;
1645 static int gGAIPerf_OutputAppendNewline
= false;
1647 static void GAIPerfCmd( void );
1649 #define kGAIPerfSectionText_TestSuiteBasic \
1650 "This test suite consists of the following three test cases:\n" \
1652 "Test Case #1: Resolve a domain name with\n" \
1654 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1656 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1657 "server queries.\n" \
1659 "Test Case #2: Resolve a domain name with\n" \
1661 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1663 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1664 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1665 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1667 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1668 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1669 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1671 "Test Case #3: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1673 #define kGAIPerfSectionText_TestSuiteAdvanced \
1674 "This test suite consists of 33 test cases. Test cases 1 through 32 can be described in the following way\n" \
1676 "Test Case #N (where N is in [1, 32] and odd): Resolve a domain name with\n" \
1678 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1680 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1681 "server queries.\n" \
1683 "Test Case #N (where N is in [1, 32] and even): Resolve a domain name with\n" \
1685 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1687 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1688 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1689 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1691 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1692 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1693 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1695 "N_c and N_a take on the following values, depending on the value of N:\n" \
1697 " N_c is 0 if N is in [1, 8].\n" \
1698 " N_c is 1 if N is in [9, 16].\n" \
1699 " N_c is 2 if N is in [17, 24].\n" \
1700 " N_c is 4 if N is in [25, 32].\n" \
1702 " N_a is 1 if N mod 8 is 1 or 2.\n" \
1703 " N_a is 2 if N mod 8 is 3 or 4.\n" \
1704 " N_a is 4 if N mod 8 is 5 or 6.\n" \
1705 " N_a is 8 if N mod 8 is 7 or 0.\n" \
1709 "Test Case #33: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1711 static CLIOption kGAIPerfOpts
[] =
1713 StringOptionEx( 's', "suite", &gGAIPerf_TestSuite
, "name", "Name of the predefined test suite to run.", true,
1715 "There are currently two predefined test suites, '" kGAIPerfTestSuiteName_Basic
"' and '" kGAIPerfTestSuiteName_Advanced
"', which are described below.\n"
1718 StringOption( 'o', "output", &gGAIPerf_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1719 FormatOption( 'f', "format", &gGAIPerf_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1720 BooleanOption( 'n', "appendNewline", &gGAIPerf_OutputAppendNewline
, "If the output format is JSON, output a trailing newline character." ),
1721 IntegerOption( 0 , "callDelay", &gGAIPerf_CallDelayMs
, "ms", "Time to wait before calling DNSServiceGetAddrInfo() in milliseconds. (default: 10)", false ),
1722 BooleanOption( 0 , "skipPathEval", &gGAIPerf_SkipPathEvalulation
, "Use kDNSServiceFlagsPathEvaluationDone when calling DNSServiceGetAddrInfo()." ),
1723 IntegerOption( 'i', "iterations", &gGAIPerf_IterationCount
, "count", "The number of iterations per test case. (default: 100)", false ),
1725 CLI_OPTION_GROUP( "DNS Server Options" ),
1726 IntegerOption( 0 , "responseDelay", &gGAIPerf_ServerDelayMs
, "ms", "Additional delay in milliseconds to have the server apply to responses. (default: 10)", false ),
1727 BooleanOption( 0 , "badUDPMode", &gGAIPerf_BadUDPMode
, "Run server in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1729 CLI_SECTION( "Test Suite \"Basic\"", kGAIPerfSectionText_TestSuiteBasic
),
1730 CLI_SECTION( "Test Suite \"Advanced\"", kGAIPerfSectionText_TestSuiteAdvanced
),
1731 TestExitStatusSection(),
1735 static void MDNSDiscoveryTestCmd( void );
1737 static int gMDNSDiscoveryTest_InstanceCount
= 100;
1738 static int gMDNSDiscoveryTest_TXTSize
= 100;
1739 static int gMDNSDiscoveryTest_BrowseTimeSecs
= 2;
1740 static int gMDNSDiscoveryTest_FlushCache
= false;
1741 static char * gMDNSDiscoveryTest_Interface
= NULL
;
1742 static int gMDNSDiscoveryTest_NoAdditionals
= false;
1743 static int gMDNSDiscoveryTest_RecordCountA
= 1;
1744 static int gMDNSDiscoveryTest_RecordCountAAAA
= 1;
1745 static double gMDNSDiscoveryTest_UnicastDropRate
= 0.0;
1746 static double gMDNSDiscoveryTest_MulticastDropRate
= 0.0;
1747 static int gMDNSDiscoveryTest_MaxDropCount
= 0;
1748 static int gMDNSDiscoveryTest_UseIPv4
= false;
1749 static int gMDNSDiscoveryTest_UseIPv6
= false;
1750 static const char * gMDNSDiscoveryTest_OutputFormat
= kOutputFormatStr_JSON
;
1751 static int gMDNSDiscoveryTest_OutputAppendNewline
= false;
1752 static const char * gMDNSDiscoveryTest_OutputFilePath
= NULL
;
1754 static CLIOption kMDNSDiscoveryTestOpts
[] =
1756 IntegerOption( 'c', "instanceCount", &gMDNSDiscoveryTest_InstanceCount
, "count", "Number of service instances to discover. (default: 100)", false ),
1757 IntegerOption( 's', "txtSize", &gMDNSDiscoveryTest_TXTSize
, "bytes", "Desired size of each service instance's TXT record data. (default: 100)", false ),
1758 IntegerOption( 'b', "browseTime", &gMDNSDiscoveryTest_BrowseTimeSecs
, "seconds", "Amount of time to spend browsing in seconds. (default: 2)", false ),
1759 BooleanOption( 0 , "flushCache", &gMDNSDiscoveryTest_FlushCache
, "Flush mDNSResponder's record cache before browsing. Requires root privileges." ),
1761 CLI_OPTION_GROUP( "mDNS Replier Parameters" ),
1762 StringOption( 'i', "interface", &gMDNSDiscoveryTest_Interface
, "name or index", "Network interface. If unspecified, any available mDNS-capable interface will be used.", false ),
1763 BooleanOption( 0 , "noAdditionals", &gMDNSDiscoveryTest_NoAdditionals
, "When answering queries, don't include any additional records." ),
1764 IntegerOption( 0 , "countA", &gMDNSDiscoveryTest_RecordCountA
, "count", "Number of A records per hostname. (default: 1)", false ),
1765 IntegerOption( 0 , "countAAAA", &gMDNSDiscoveryTest_RecordCountAAAA
, "count", "Number of AAAA records per hostname. (default: 1)", false ),
1766 DoubleOption( 0 , "udrop", &gMDNSDiscoveryTest_UnicastDropRate
, "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1767 DoubleOption( 0 , "mdrop", &gMDNSDiscoveryTest_MulticastDropRate
, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1768 IntegerOption( 0 , "maxDropCount", &gMDNSDiscoveryTest_MaxDropCount
, "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1769 BooleanOption( 0 , "ipv4", &gMDNSDiscoveryTest_UseIPv4
, "Use IPv4." ),
1770 BooleanOption( 0 , "ipv6", &gMDNSDiscoveryTest_UseIPv6
, "Use IPv6." ),
1772 CLI_OPTION_GROUP( "Results" ),
1773 FormatOption( 'f', "format", &gMDNSDiscoveryTest_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1774 StringOption( 'o', "output", &gMDNSDiscoveryTest_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1775 BooleanOption( 'n', "appendNewline", &gMDNSDiscoveryTest_OutputAppendNewline
, "If the output format is JSON, output a trailing newline character." ),
1777 TestExitStatusSection(),
1781 static void DotLocalTestCmd( void );
1783 static const char * gDotLocalTest_Interface
= NULL
;
1784 static const char * gDotLocalTest_OutputFormat
= kOutputFormatStr_JSON
;
1785 static int gDotLocalTest_OutputAppendNewline
= false;
1786 static const char * gDotLocalTest_OutputFilePath
= NULL
;
1788 #define kDotLocalTestSubtestDesc_GAIMDNSOnly "GAI for a dotlocal name that has only MDNS A and AAAA records."
1789 #define kDotLocalTestSubtestDesc_GAIDNSOnly "GAI for a dotlocal name that has only DNS A and AAAA records."
1790 #define kDotLocalTestSubtestDesc_GAIBoth "GAI for a dotlocal name that has both mDNS and DNS A and AAAA records."
1791 #define kDotLocalTestSubtestDesc_GAINeither "GAI for a dotlocal name that has no A or AAAA records."
1792 #define kDotLocalTestSubtestDesc_GAINoSuchRecord \
1793 "GAI for a dotlocal name that has no A or AAAA records, but is a subdomain name of a search domain."
1794 #define kDotLocalTestSubtestDesc_QuerySRV "SRV query for a dotlocal name that has only a DNS SRV record."
1796 #define kDotLocalTestSectionText_Description \
1797 "The goal of the dotlocal test is to verify that mDNSResponder properly handles queries for domain names in the\n" \
1798 "local domain when a local SOA record exists. As part of the test setup, a test DNS server and an mdnsreplier are\n" \
1799 "spawned, and a dummy local SOA record is registered with DNSServiceRegisterRecord(). The server is invoked such\n" \
1800 "that its domain is a second-level subdomain of the local domain, i.e., <some label>.local, while the mdnsreplier is\n" \
1801 "invoked such that its base hostname is equal to the server's domain, e.g., if the server's domain is test.local.,\n" \
1802 "then the mdnsreplier's base hostname is test.local.\n" \
1804 "The dotlocal test consists of six subtests that perform either a DNSServiceGetAddrInfo (GAI) operation for a\n" \
1805 "hostname in the local domain or a DNSServiceQueryRecord operation to query for an SRV record in the local domain:\n" \
1807 "1. " kDotLocalTestSubtestDesc_GAIMDNSOnly "\n" \
1808 "2. " kDotLocalTestSubtestDesc_GAIDNSOnly "\n" \
1809 "3. " kDotLocalTestSubtestDesc_GAIBoth "\n" \
1810 "4. " kDotLocalTestSubtestDesc_GAINeither "\n" \
1811 "5. " kDotLocalTestSubtestDesc_GAINoSuchRecord "\n" \
1812 "6. " kDotLocalTestSubtestDesc_QuerySRV "\n" \
1814 "Each subtest runs for five seconds.\n"
1816 static CLIOption kDotLocalTestOpts
[] =
1818 StringOption( 'i', "interface", &gDotLocalTest_Interface
, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1820 CLI_OPTION_GROUP( "Results" ),
1821 FormatOption( 'f', "format", &gDotLocalTest_OutputFormat
, "Specifies the test results output format. (default: " kOutputFormatStr_JSON
")", false ),
1822 StringOption( 'o', "output", &gDotLocalTest_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1823 BooleanOption( 'n', "appendNewline", &gDotLocalTest_OutputAppendNewline
, "If the output format is JSON, output a trailing newline character." ),
1825 CLI_SECTION( "Description", kDotLocalTestSectionText_Description
),
1826 TestExitStatusSection(),
1830 static void ProbeConflictTestCmd( void );
1832 static const char * gProbeConflictTest_Interface
= NULL
;
1833 static int gProbeConflictTest_UseComputerName
= false;
1834 static const char * gProbeConflictTest_OutputFormat
= kOutputFormatStr_JSON
;
1835 static int gProbeConflictTest_OutputAppendNewline
= false;
1836 static const char * gProbeConflictTest_OutputFilePath
= NULL
;
1838 static CLIOption kProbeConflictTestOpts
[] =
1840 StringOption( 'i', "interface", &gProbeConflictTest_Interface
, "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1841 BooleanOption( 'c', "useComputerName", &gProbeConflictTest_UseComputerName
, "Use the device's \"computer name\" for the test service's name." ),
1843 CLI_OPTION_GROUP( "Results" ),
1844 FormatOption( 'f', "format", &gProbeConflictTest_OutputFormat
, "Specifies the test report output format. (default: " kOutputFormatStr_JSON
")", false ),
1845 StringOption( 'o', "output", &gProbeConflictTest_OutputFilePath
, "path", "Path of the file to write test report to instead of standard output (stdout).", false ),
1846 BooleanOption( 'n', "appendNewline", &gProbeConflictTest_OutputAppendNewline
, "If the output format is JSON, output a trailing newline character." ),
1848 TestExitStatusSection(),
1852 static CLIOption kTestOpts
[] =
1854 Command( "gaiperf", GAIPerfCmd
, kGAIPerfOpts
, "Runs DNSServiceGetAddrInfo() performance tests.", false ),
1855 Command( "mdnsdiscovery", MDNSDiscoveryTestCmd
, kMDNSDiscoveryTestOpts
, "Tests mDNS service discovery for correctness.", false ),
1856 Command( "dotlocal", DotLocalTestCmd
, kDotLocalTestOpts
, "Tests DNS and mDNS queries for domain names in the local domain.", false ),
1857 Command( "probeconflicts", ProbeConflictTestCmd
, kProbeConflictTestOpts
, "Tests various probing conflict scenarios.", false ),
1862 //===========================================================================================================================
1863 // SSDP Command Options
1864 //===========================================================================================================================
1866 static int gSSDPDiscover_MX
= 1;
1867 static const char * gSSDPDiscover_ST
= "ssdp:all";
1868 static int gSSDPDiscover_ReceiveSecs
= 1;
1869 static int gSSDPDiscover_UseIPv4
= false;
1870 static int gSSDPDiscover_UseIPv6
= false;
1871 static int gSSDPDiscover_Verbose
= false;
1873 static CLIOption kSSDPDiscoverOpts
[] =
1875 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
1876 IntegerOption( 'm', "mx", &gSSDPDiscover_MX
, "seconds", "MX value in search request, i.e., max response delay in seconds. (Default: 1 second)", false ),
1877 StringOption( 's', "st", &gSSDPDiscover_ST
, "string", "ST value in search request, i.e., the search target. (Default: \"ssdp:all\")", false ),
1878 IntegerOption( 'r', "receiveTime", &gSSDPDiscover_ReceiveSecs
, "seconds", "Amount of time to spend receiving responses. -1 means unlimited. (Default: 1 second)", false ),
1879 BooleanOption( 0 , "ipv4", &gSSDPDiscover_UseIPv4
, "Use IPv4, i.e., multicast to 239.255.255.250:1900." ),
1880 BooleanOption( 0 , "ipv6", &gSSDPDiscover_UseIPv6
, "Use IPv6, i.e., multicast to [ff02::c]:1900" ),
1881 BooleanOption( 'v', "verbose", &gSSDPDiscover_Verbose
, "Prints the search request(s) that were sent." ),
1885 static void SSDPDiscoverCmd( void );
1887 static CLIOption kSSDPOpts
[] =
1889 Command( "discover", SSDPDiscoverCmd
, kSSDPDiscoverOpts
, "Crafts and multicasts an SSDP search message.", false ),
1893 #if( TARGET_OS_DARWIN )
1894 //===========================================================================================================================
1895 // res_query Command Options
1896 //===========================================================================================================================
1898 static void ResQueryCmd( void );
1900 static const char * gResQuery_Name
= NULL
;
1901 static const char * gResQuery_Type
= NULL
;
1902 static const char * gResQuery_Class
= NULL
;
1903 static int gResQuery_UseLibInfo
= false;
1905 static CLIOption kResQueryOpts
[] =
1907 StringOption( 'n', "name", &gResQuery_Name
, "domain name", "Full domain name of record to query.", true ),
1908 StringOption( 't', "type", &gResQuery_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1909 StringOption( 'c', "class", &gResQuery_Class
, "record class", "Record class by name or number. Default class is IN.", false ),
1910 BooleanOption( 0 , "libinfo", &gResQuery_UseLibInfo
, "Use res_query from libinfo instead of libresolv." ),
1914 //===========================================================================================================================
1915 // dns_query Command Options
1916 //===========================================================================================================================
1918 static void ResolvDNSQueryCmd( void );
1920 static const char * gResolvDNSQuery_Name
= NULL
;
1921 static const char * gResolvDNSQuery_Type
= NULL
;
1922 static const char * gResolvDNSQuery_Class
= NULL
;
1923 static const char * gResolvDNSQuery_Path
= NULL
;
1925 static CLIOption kResolvDNSQueryOpts
[] =
1927 StringOption( 'n', "name", &gResolvDNSQuery_Name
, "domain name", "Full domain name of record to query.", true ),
1928 StringOption( 't', "type", &gResolvDNSQuery_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1929 StringOption( 'c', "class", &gResolvDNSQuery_Class
, "record class", "Record class by name or number. Default class is IN.", false ),
1930 StringOption( 'p', "path", &gResolvDNSQuery_Path
, "file path", "The path argument to pass to dns_open() before calling dns_query(). Default value is NULL.", false ),
1934 //===========================================================================================================================
1935 // CFHost Command Options
1936 //===========================================================================================================================
1938 static void CFHostCmd( void );
1940 static const char * gCFHost_Name
= NULL
;
1941 static int gCFHost_WaitSecs
= 0;
1943 static CLIOption kCFHostOpts
[] =
1945 StringOption( 'n', "name", &gCFHost_Name
, "hostname", "Hostname to resolve.", true ),
1946 IntegerOption( 'w', "wait", &gCFHost_WaitSecs
, "seconds", "Time in seconds to wait before a normal exit. (default: 0)", false ),
1950 static CLIOption kLegacyOpts
[] =
1952 Command( "res_query", ResQueryCmd
, kResQueryOpts
, "Uses res_query() from either libresolv or libinfo to query for a record.", true ),
1953 Command( "dns_query", ResolvDNSQueryCmd
, kResolvDNSQueryOpts
, "Uses dns_query() from libresolv to query for a record.", true ),
1954 Command( "cfhost", CFHostCmd
, kCFHostOpts
, "Uses CFHost to resolve a hostname.", true ),
1958 //===========================================================================================================================
1959 // DNSConfigAdd Command Options
1960 //===========================================================================================================================
1962 static void DNSConfigAddCmd( void );
1964 static CFStringRef gDNSConfigAdd_ID
= NULL
;
1965 static char ** gDNSConfigAdd_IPAddrArray
= NULL
;
1966 static size_t gDNSConfigAdd_IPAddrCount
= 0;
1967 static char ** gDNSConfigAdd_DomainArray
= NULL
;
1968 static size_t gDNSConfigAdd_DomainCount
= 0;
1969 static const char * gDNSConfigAdd_Interface
= NULL
;
1971 static CLIOption kDNSConfigAddOpts
[] =
1973 CFStringOption( 0 , "id", &gDNSConfigAdd_ID
, "ID", "Arbitrary ID to use for resolver entry.", true ),
1974 MultiStringOption( 'a', "address", &gDNSConfigAdd_IPAddrArray
, &gDNSConfigAdd_IPAddrCount
, "IP address", "DNS server IP address(es). Can be specified more than once.", true ),
1975 MultiStringOption( 'd', "domain", &gDNSConfigAdd_DomainArray
, &gDNSConfigAdd_DomainCount
, "domain", "Specific domain(s) for the resolver entry. Can be specified more than once.", false ),
1976 StringOption( 'i', "interface", &gDNSConfigAdd_Interface
, "interface name", "Specific interface for the resolver entry.", false ),
1978 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1982 //===========================================================================================================================
1983 // DNSConfigRemove Command Options
1984 //===========================================================================================================================
1986 static void DNSConfigRemoveCmd( void );
1988 static CFStringRef gDNSConfigRemove_ID
= NULL
;
1990 static CLIOption kDNSConfigRemoveOpts
[] =
1992 CFStringOption( 0, "id", &gDNSConfigRemove_ID
, "ID", "ID of resolver entry to remove.", true ),
1994 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1998 static CLIOption kDNSConfigOpts
[] =
2000 Command( "add", DNSConfigAddCmd
, kDNSConfigAddOpts
, "Add a supplemental resolver entry to the system's DNS configuration.", true ),
2001 Command( "remove", DNSConfigRemoveCmd
, kDNSConfigRemoveOpts
, "Remove a supplemental resolver entry from the system's DNS configuration.", true ),
2004 #endif // TARGET_OS_DARWIN
2006 //===========================================================================================================================
2008 //===========================================================================================================================
2010 static OSStatus
VersionOptionCallback( CLIOption
*inOption
, const char *inArg
, int inUnset
);
2012 static void BrowseCmd( void );
2013 static void GetAddrInfoCmd( void );
2014 static void QueryRecordCmd( void );
2015 static void RegisterCmd( void );
2016 static void RegisterRecordCmd( void );
2017 static void ResolveCmd( void );
2018 static void ReconfirmCmd( void );
2019 static void GetAddrInfoPOSIXCmd( void );
2020 static void ReverseLookupCmd( void );
2021 static void PortMappingCmd( void );
2022 static void BrowseAllCmd( void );
2023 static void GetAddrInfoStressCmd( void );
2024 static void DNSQueryCmd( void );
2025 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2026 static void DNSCryptCmd( void );
2028 static void MDNSQueryCmd( void );
2029 static void PIDToUUIDCmd( void );
2030 static void DaemonVersionCmd( void );
2032 static CLIOption kGlobalOpts
[] =
2034 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback
, NULL
, NULL
,
2035 kCLIOptionFlags_NoArgument
| kCLIOptionFlags_GlobalOnly
, "Displays the version of this tool.", NULL
),
2040 Command( "browse", BrowseCmd
, kBrowseOpts
, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
2041 Command( "getAddrInfo", GetAddrInfoCmd
, kGetAddrInfoOpts
, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
2042 Command( "queryRecord", QueryRecordCmd
, kQueryRecordOpts
, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
2043 Command( "register", RegisterCmd
, kRegisterOpts
, "Uses DNSServiceRegister() to register a service.", false ),
2044 Command( "registerRecord", RegisterRecordCmd
, kRegisterRecordOpts
, "Uses DNSServiceRegisterRecord() to register a record.", false ),
2045 Command( "resolve", ResolveCmd
, kResolveOpts
, "Uses DNSServiceResolve() to resolve a service.", false ),
2046 Command( "reconfirm", ReconfirmCmd
, kReconfirmOpts
, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
2047 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd
, kGetAddrInfoPOSIXOpts
, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
2048 Command( "reverseLookup", ReverseLookupCmd
, kReverseLookupOpts
, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
2049 Command( "portMapping", PortMappingCmd
, kPortMappingOpts
, "Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ),
2050 Command( "browseAll", BrowseAllCmd
, kBrowseAllOpts
, "Browse and resolve all (or specific) services and, optionally, attempt connections.", false ),
2052 // Uncommon commands.
2054 Command( "getnameinfo", GetNameInfoCmd
, kGetNameInfoOpts
, "Calls getnameinfo() and prints results.", true ),
2055 Command( "getAddrInfoStress", GetAddrInfoStressCmd
, kGetAddrInfoStressOpts
, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
2056 Command( "DNSQuery", DNSQueryCmd
, kDNSQueryOpts
, "Crafts and sends a DNS query.", true ),
2057 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2058 Command( "DNSCrypt", DNSCryptCmd
, kDNSCryptOpts
, "Crafts and sends a DNSCrypt query.", true ),
2060 Command( "mdnsquery", MDNSQueryCmd
, kMDNSQueryOpts
, "Crafts and sends an mDNS query over the specified interface.", true ),
2061 Command( "mdnscollider", MDNSColliderCmd
, kMDNSColliderOpts
, "Creates record name collision scenarios.", true ),
2062 Command( "pid2uuid", PIDToUUIDCmd
, kPIDToUUIDOpts
, "Prints the UUID of a process.", true ),
2063 Command( "server", DNSServerCmd
, kDNSServerOpts
, "DNS server for testing.", true ),
2064 Command( "mdnsreplier", MDNSReplierCmd
, kMDNSReplierOpts
, "Responds to mDNS queries for a set of authoritative resource records.", true ),
2065 Command( "test", NULL
, kTestOpts
, "Commands for testing DNS-SD.", true ),
2066 Command( "ssdp", NULL
, kSSDPOpts
, "Commands for testing Simple Service Discovery Protocol (SSDP).", true ),
2067 #if( TARGET_OS_DARWIN )
2068 Command( "legacy", NULL
, kLegacyOpts
, "Commands for legacy non-DNS-SD API.", true ),
2069 Command( "dnsconfig", NULL
, kDNSConfigOpts
, "Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ),
2071 Command( "daemonVersion", DaemonVersionCmd
, NULL
, "Prints the version of the DNS-SD daemon.", true ),
2077 //===========================================================================================================================
2078 // Helper Prototypes
2079 //===========================================================================================================================
2081 #define kExitReason_OneShotDone "one-shot done"
2082 #define kExitReason_ReceivedResponse "received response"
2083 #define kExitReason_SIGINT "interrupt signal"
2084 #define kExitReason_Timeout "timeout"
2085 #define kExitReason_TimeLimit "time limit"
2087 static void Exit( void *inContext
) ATTRIBUTE_NORETURN
;
2090 PrintFTimestampHandler(
2091 PrintFContext
* inContext
,
2092 PrintFFormat
* inFormat
,
2093 PrintFVAList
* inArgs
,
2094 void * inUserContext
);
2096 PrintFDNSMessageHandler(
2097 PrintFContext
* inContext
,
2098 PrintFFormat
* inFormat
,
2099 PrintFVAList
* inArgs
,
2100 void * inUserContext
);
2102 PrintFAddRmvFlagsHandler(
2103 PrintFContext
* inContext
,
2104 PrintFFormat
* inFormat
,
2105 PrintFVAList
* inArgs
,
2106 void * inUserContext
);
2108 static DNSServiceFlags
GetDNSSDFlagsFromOpts( void );
2112 kConnectionType_None
= 0,
2113 kConnectionType_Normal
= 1,
2114 kConnectionType_DelegatePID
= 2,
2115 kConnectionType_DelegateUUID
= 3
2121 ConnectionType type
;
2132 CreateConnectionFromArgString(
2133 const char * inString
,
2134 dispatch_queue_t inQueue
,
2135 DNSServiceRef
* outSDRef
,
2136 ConnectionDesc
* outDesc
);
2137 static OSStatus
InterfaceIndexFromArgString( const char *inString
, uint32_t *outIndex
);
2138 static OSStatus
RecordDataFromArgString( const char *inString
, uint8_t **outDataPtr
, size_t *outDataLen
);
2139 static OSStatus
RecordTypeFromArgString( const char *inString
, uint16_t *outValue
);
2140 static OSStatus
RecordClassFromArgString( const char *inString
, uint16_t *outValue
);
2142 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
2144 static char * InterfaceIndexToName( uint32_t inIfIndex
, char inNameBuf
[ kInterfaceNameBufLen
] );
2145 static const char * RecordTypeToString( unsigned int inValue
);
2148 DNSMessageExtractDomainName(
2149 const uint8_t * inMsgPtr
,
2151 const uint8_t * inNamePtr
,
2152 uint8_t inBuf
[ kDomainNameLengthMax
],
2153 const uint8_t ** outNextPtr
);
2155 DNSMessageExtractDomainNameString(
2156 const void * inMsgPtr
,
2158 const void * inNamePtr
,
2159 char inBuf
[ kDNSServiceMaxDomainName
],
2160 const uint8_t ** outNextPtr
);
2162 DNSMessageExtractQuestion(
2163 const uint8_t * inMsgPtr
,
2165 const uint8_t * inPtr
,
2166 uint8_t inNameBuf
[ kDomainNameLengthMax
],
2168 uint16_t * outClass
,
2169 const uint8_t ** outPtr
);
2171 DNSMessageExtractRecord(
2172 const uint8_t * inMsgPtr
,
2174 const uint8_t * inPtr
,
2175 uint8_t inNameBuf
[ kDomainNameLengthMax
],
2177 uint16_t * outClass
,
2179 const uint8_t ** outRDataPtr
,
2180 size_t * outRDataLen
,
2181 const uint8_t ** outPtr
);
2182 static OSStatus
DNSMessageGetAnswerSection( const uint8_t *inMsgPtr
, size_t inMsgLen
, const uint8_t **outPtr
);
2184 DNSRecordDataToString(
2185 const void * inRDataPtr
,
2187 unsigned int inRDataType
,
2188 const void * inMsgPtr
,
2190 char ** outString
);
2192 DomainNameAppendString(
2193 uint8_t inDomainName
[ kDomainNameLengthMax
],
2194 const char * inString
,
2195 uint8_t ** outEndPtr
);
2196 static Boolean
DomainNameEqual( const uint8_t *inName1
, const uint8_t *inName2
);
2197 static size_t DomainNameLength( const uint8_t *inName
);
2198 static OSStatus
DomainNameDupEx( const uint8_t *inName
, Boolean inLower
, uint8_t **outNamePtr
, size_t *outNameLen
);
2199 #define DomainNameDup( IN_NAME, OUT_NAME, OUT_LEN ) DomainNameDupEx( IN_NAME, false, OUT_NAME, OUT_LEN )
2200 #define DomainNameDupLower( IN_NAME, OUT_NAME, OUT_LEN ) DomainNameDupEx( IN_NAME, true, OUT_NAME, OUT_LEN )
2203 DomainNameFromString(
2204 uint8_t inDomainName
[ kDomainNameLengthMax
],
2205 const char * inString
,
2206 uint8_t ** outEndPtr
);
2209 const uint8_t * inDomainName
,
2210 const uint8_t * inEnd
,
2211 char inBuf
[ kDNSServiceMaxDomainName
],
2212 const uint8_t ** outNextPtr
);
2216 const uint8_t * inMsgPtr
,
2222 #define kDNSQueryMessageMaxLen ( kDNSHeaderLength + kDomainNameLengthMax + 4 )
2225 WriteDNSQueryMessage(
2226 uint8_t inMsg
[ kDNSQueryMessageMaxLen
],
2229 const char * inQName
,
2232 size_t * outMsgLen
);
2236 typedef void ( *DispatchHandler
)( void *inContext
);
2239 DispatchSignalSourceCreate(
2241 DispatchHandler inEventHandler
,
2243 dispatch_source_t
* outSource
);
2245 DispatchSocketSourceCreate(
2247 dispatch_source_type_t inType
,
2248 dispatch_queue_t inQueue
,
2249 DispatchHandler inEventHandler
,
2250 DispatchHandler inCancelHandler
,
2252 dispatch_source_t
* outSource
);
2254 #define DispatchReadSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2255 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_READ, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2257 #define DispatchWriteSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2258 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_WRITE, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2261 DispatchTimerCreate(
2262 dispatch_time_t inStart
,
2263 uint64_t inIntervalNs
,
2264 uint64_t inLeewayNs
,
2265 dispatch_queue_t inQueue
,
2266 DispatchHandler inEventHandler
,
2267 DispatchHandler inCancelHandler
,
2269 dispatch_source_t
* outTimer
);
2271 #define DispatchTimerOneShotCreate( IN_START, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, IN_CONTEXT, OUT_TIMER ) \
2272 DispatchTimerCreate( IN_START, DISPATCH_TIME_FOREVER, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, NULL, IN_CONTEXT, OUT_TIMER )
2275 DispatchProcessMonitorCreate(
2277 unsigned long inFlags
,
2278 dispatch_queue_t inQueue
,
2279 DispatchHandler inEventHandler
,
2280 DispatchHandler inCancelHandler
,
2282 dispatch_source_t
* outMonitor
);
2284 static const char * ServiceTypeDescription( const char *inName
);
2288 SocketRef sock
; // Socket.
2289 void * userContext
; // User context.
2290 int32_t refCount
; // Reference count.
2294 static OSStatus
SocketContextCreate( SocketRef inSock
, void * inUserContext
, SocketContext
**outContext
);
2295 static SocketContext
* SocketContextRetain( SocketContext
*inContext
);
2296 static void SocketContextRelease( SocketContext
*inContext
);
2297 static void SocketContextCancelHandler( void *inContext
);
2299 #define ForgetSocketContext( X ) ForgetCustom( X, SocketContextRelease )
2301 static OSStatus
StringToInt32( const char *inString
, int32_t *outValue
);
2302 static OSStatus
StringToUInt32( const char *inString
, uint32_t *outValue
);
2303 #if( TARGET_OS_DARWIN )
2304 static OSStatus
StringToPID( const char *inString
, pid_t
*outPID
);
2306 static OSStatus
StringToARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2307 static OSStatus
StringToAAAARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2308 static OSStatus
StringToDomainName( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2309 #if( TARGET_OS_DARWIN )
2310 static OSStatus
GetDefaultDNSServer( sockaddr_ip
*outAddr
);
2313 _ServerSocketOpenEx2(
2317 const void * inAddr
,
2321 Boolean inNoPortReuse
,
2322 SocketRef
* outSock
);
2324 static const struct sockaddr
* GetMDNSMulticastAddrV4( void );
2325 static const struct sockaddr
* GetMDNSMulticastAddrV6( void );
2326 static OSStatus
GetAnyMDNSInterface( char inNameBuf
[ IF_NAMESIZE
+ 1 ], uint32_t *outIndex
);
2329 CreateMulticastSocket(
2330 const struct sockaddr
* inAddr
,
2332 const char * inIfName
,
2336 SocketRef
* outSock
);
2338 static OSStatus
DecimalTextToUInt32( const char *inSrc
, const char *inEnd
, uint32_t *outValue
, const char **outPtr
);
2339 static OSStatus
CheckIntegerArgument( int inArgValue
, const char *inArgName
, int inMin
, int inMax
);
2340 static OSStatus
CheckDoubleArgument( double inArgValue
, const char *inArgName
, double inMin
, double inMax
);
2341 static OSStatus
CheckRootUser( void );
2342 static OSStatus
SpawnCommand( pid_t
*outPID
, const char *inFormat
, ... );
2345 CFPropertyListRef inPList
,
2346 OutputFormatType inType
,
2347 Boolean inAppendNewline
,
2348 const char * inOutputFilePath
);
2350 DNSRecordFixedFieldsSet(
2351 DNSRecordFixedFields
* inFields
,
2355 uint16_t inRDLength
);
2357 SRVRecordDataFixedFieldsGet(
2358 const SRVRecordDataFixedFields
* inFields
,
2359 unsigned int * outPriority
,
2360 unsigned int * outWeight
,
2361 unsigned int * outPort
);
2363 SRVRecordDataFixedFieldsSet(
2364 SRVRecordDataFixedFields
* inFields
,
2365 uint16_t inPriority
,
2369 SOARecordDataFixedFieldsGet(
2370 const SOARecordDataFixedFields
* inFields
,
2371 uint32_t * outSerial
,
2372 uint32_t * outRefresh
,
2373 uint32_t * outRetry
,
2374 uint32_t * outExpire
,
2375 uint32_t * outMinimum
);
2377 SOARecordDataFixedFieldsSet(
2378 SOARecordDataFixedFields
* inFields
,
2383 uint32_t inMinimum
);
2384 static OSStatus
CreateSRVRecordDataFromString( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
2385 static OSStatus
CreateTXTRecordDataFromString( const char *inString
, int inDelimiter
, uint8_t **outPtr
, size_t *outLen
);
2387 CreateNSECRecordData(
2388 const uint8_t * inNextDomainName
,
2391 unsigned int inTypeCount
,
2396 const uint8_t * inNamePtr
,
2401 const uint8_t * inMName
,
2402 const uint8_t * inRName
,
2407 uint32_t inMinimumTTL
,
2410 CreateSOARecordData(
2411 const uint8_t * inMName
,
2412 const uint8_t * inRName
,
2417 uint32_t inMinimumTTL
,
2421 _DataBuffer_AppendDNSQuestion(
2423 const uint8_t * inNamePtr
,
2428 _DataBuffer_AppendDNSRecord(
2430 const uint8_t * inNamePtr
,
2435 const uint8_t * inRDataPtr
,
2436 size_t inRDataLen
);
2437 static char * _NanoTime64ToDateString( NanoTime64 inTime
, char *inBuf
, size_t inMaxLen
);
2439 #define Unused( X ) (void)(X)
2441 //===========================================================================================================================
2443 //===========================================================================================================================
2445 typedef struct MDNSColliderPrivate
* MDNSColliderRef
;
2447 typedef uint32_t MDNSColliderProtocols
;
2448 #define kMDNSColliderProtocol_None 0
2449 #define kMDNSColliderProtocol_IPv4 ( 1 << 0 )
2450 #define kMDNSColliderProtocol_IPv6 ( 1 << 1 )
2452 typedef void ( *MDNSColliderStopHandler_f
)( void *inContext
, OSStatus inError
);
2454 static OSStatus
MDNSColliderCreate( dispatch_queue_t inQueue
, MDNSColliderRef
*outCollider
);
2455 static OSStatus
MDNSColliderStart( MDNSColliderRef inCollider
);
2456 static void MDNSColliderStop( MDNSColliderRef inCollider
);
2457 static void MDNSColliderSetProtocols( MDNSColliderRef inCollider
, MDNSColliderProtocols inProtocols
);
2458 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef inCollider
, uint32_t inInterfaceIndex
);
2459 static OSStatus
MDNSColliderSetProgram( MDNSColliderRef inCollider
, const char *inProgramStr
);
2461 MDNSColliderSetStopHandler(
2462 MDNSColliderRef inCollider
,
2463 MDNSColliderStopHandler_f inStopHandler
,
2464 void * inStopContext
);
2466 MDNSColliderSetRecord(
2467 MDNSColliderRef inCollider
,
2468 const uint8_t * inName
,
2470 const void * inRDataPtr
,
2471 size_t inRDataLen
);
2472 static CFTypeID
MDNSColliderGetTypeID( void );
2474 //===========================================================================================================================
2476 //===========================================================================================================================
2478 typedef struct ServiceBrowserPrivate
* ServiceBrowserRef
;
2479 typedef struct ServiceBrowserResults ServiceBrowserResults
;
2480 typedef struct SBRDomain SBRDomain
;
2481 typedef struct SBRServiceType SBRServiceType
;
2482 typedef struct SBRServiceInstance SBRServiceInstance
;
2483 typedef struct SBRIPAddress SBRIPAddress
;
2485 typedef void ( *ServiceBrowserCallback_f
)( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
);
2487 struct ServiceBrowserResults
2489 SBRDomain
* domainList
; // List of domains in which services were found.
2494 SBRDomain
* next
; // Next domain in list.
2495 char * name
; // Name of domain represented by this object.
2496 SBRServiceType
* typeList
; // List of service types in this domain.
2499 struct SBRServiceType
2501 SBRServiceType
* next
; // Next service type in list.
2502 char * name
; // Name of service type represented by this object.
2503 SBRServiceInstance
* instanceList
; // List of service instances of this service type.
2506 struct SBRServiceInstance
2508 SBRServiceInstance
* next
; // Next service instance in list.
2509 char * name
; // Name of service instance represented by this object.
2510 char * hostname
; // Target from service instance's SRV record.
2511 uint32_t ifIndex
; // Index of interface over which this service instance was discovered.
2512 uint16_t port
; // Port from service instance's SRV record.
2513 uint8_t * txtPtr
; // Service instance's TXT record data.
2514 size_t txtLen
; // Service instance's TXT record data length.
2515 SBRIPAddress
* ipaddrList
; // List of IP addresses that the hostname resolved to.
2516 uint64_t discoverTimeUs
; // Time it took to discover this service instance in microseconds.
2517 uint64_t resolveTimeUs
; // Time it took to resolve this service instance in microseconds.
2522 SBRIPAddress
* next
; // Next IP address in list.
2523 sockaddr_ip sip
; // IPv4 or IPv6 address.
2524 uint64_t resolveTimeUs
; // Time it took to resolve this IP address in microseconds.
2527 static CFTypeID
ServiceBrowserGetTypeID( void );
2529 ServiceBrowserCreate(
2530 dispatch_queue_t inQueue
,
2531 uint32_t inInterfaceIndex
,
2532 const char * inDomain
,
2533 unsigned int inBrowseTimeSecs
,
2534 Boolean inIncludeAWDL
,
2535 ServiceBrowserRef
* outBrowser
);
2536 static void ServiceBrowserStart( ServiceBrowserRef inBrowser
);
2537 static OSStatus
ServiceBrowserAddServiceType( ServiceBrowserRef inBrowser
, const char *inServiceType
);
2539 ServiceBrowserSetCallback(
2540 ServiceBrowserRef inBrowser
,
2541 ServiceBrowserCallback_f inCallback
,
2543 static void ServiceBrowserResultsRetain( ServiceBrowserResults
*inResults
);
2544 static void ServiceBrowserResultsRelease( ServiceBrowserResults
*inResults
);
2546 #define ForgetServiceBrowserResults( X ) ForgetCustom( X, ServiceBrowserResultsRelease )
2548 //===========================================================================================================================
2550 //===========================================================================================================================
2552 int main( int argc
, const char **argv
)
2556 // Route DebugServices logging output to stderr.
2558 dlog_control( "DebugServices:output=file;stderr" );
2560 PrintFRegisterExtension( "du:time", PrintFTimestampHandler
, NULL
);
2561 PrintFRegisterExtension( "du:dnsmsg", PrintFDNSMessageHandler
, NULL
);
2562 PrintFRegisterExtension( "du:arflags", PrintFAddRmvFlagsHandler
, NULL
);
2563 CLIInit( argc
, argv
);
2564 err
= CLIParse( kGlobalOpts
, kCLIFlags_None
);
2565 if( err
) exit( 1 );
2567 return( gExitCode
);
2570 //===========================================================================================================================
2571 // VersionOptionCallback
2572 //===========================================================================================================================
2574 static OSStatus
VersionOptionCallback( CLIOption
*inOption
, const char *inArg
, int inUnset
)
2576 const char * srcVers
;
2577 #if( MDNSRESPONDER_PROJECT )
2585 #if( MDNSRESPONDER_PROJECT )
2586 srcVers
= SourceVersionToCString( _DNS_SD_H
, srcStr
);
2588 srcVers
= DNSSDUTIL_SOURCE_VERSION
;
2590 FPrintF( stdout
, "%s version %v (%s)\n", gProgramName
, kDNSSDUtilNumVersion
, srcVers
);
2592 return( kEndingErr
);
2595 //===========================================================================================================================
2597 //===========================================================================================================================
2599 typedef struct BrowseResolveOp BrowseResolveOp
;
2601 struct BrowseResolveOp
2603 BrowseResolveOp
* next
; // Next resolve operation in list.
2604 DNSServiceRef sdRef
; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
2605 char * fullName
; // Full name of the service to resolve.
2606 uint32_t interfaceIndex
; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
2611 DNSServiceRef mainRef
; // Main sdRef for shared connection.
2612 DNSServiceRef
* opRefs
; // Array of sdRefs for individual Browse operarions.
2613 size_t opRefsCount
; // Count of array of sdRefs for non-shared connections.
2614 const char * domain
; // Domain for DNSServiceBrowse operation(s).
2615 DNSServiceFlags flags
; // Flags for DNSServiceBrowse operation(s).
2616 char ** serviceTypes
; // Array of service types to browse for.
2617 size_t serviceTypesCount
; // Count of array of service types to browse for.
2618 int timeLimitSecs
; // Time limit of DNSServiceBrowse operation in seconds.
2619 BrowseResolveOp
* resolveList
; // List of resolve and/or TXT record query operations.
2620 uint32_t ifIndex
; // Interface index of DNSServiceBrowse operation(s).
2621 Boolean printedHeader
; // True if results header has been printed.
2622 Boolean doResolve
; // True if service instances are to be resolved.
2623 Boolean doResolveTXTOnly
; // True if TXT records of service instances are to be queried.
2627 static void BrowsePrintPrologue( const BrowseContext
*inContext
);
2628 static void BrowseContextFree( BrowseContext
*inContext
);
2629 static OSStatus
BrowseResolveOpCreate( const char *inFullName
, uint32_t inInterfaceIndex
, BrowseResolveOp
**outOp
);
2630 static void BrowseResolveOpFree( BrowseResolveOp
*inOp
);
2631 static void DNSSD_API
2633 DNSServiceRef inSDRef
,
2634 DNSServiceFlags inFlags
,
2635 uint32_t inInterfaceIndex
,
2636 DNSServiceErrorType inError
,
2637 const char * inName
,
2638 const char * inRegType
,
2639 const char * inDomain
,
2641 static void DNSSD_API
2642 BrowseResolveCallback(
2643 DNSServiceRef inSDRef
,
2644 DNSServiceFlags inFlags
,
2645 uint32_t inInterfaceIndex
,
2646 DNSServiceErrorType inError
,
2647 const char * inFullName
,
2648 const char * inHostname
,
2651 const unsigned char * inTXTPtr
,
2653 static void DNSSD_API
2654 BrowseQueryRecordCallback(
2655 DNSServiceRef inSDRef
,
2656 DNSServiceFlags inFlags
,
2657 uint32_t inInterfaceIndex
,
2658 DNSServiceErrorType inError
,
2659 const char * inFullName
,
2662 uint16_t inRDataLen
,
2663 const void * inRDataPtr
,
2667 static void BrowseCmd( void )
2671 BrowseContext
* context
= NULL
;
2672 dispatch_source_t signalSource
= NULL
;
2673 int useMainConnection
;
2675 // Set up SIGINT handler.
2677 signal( SIGINT
, SIG_IGN
);
2678 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
2679 require_noerr( err
, exit
);
2680 dispatch_resume( signalSource
);
2684 context
= (BrowseContext
*) calloc( 1, sizeof( *context
) );
2685 require_action( context
, exit
, err
= kNoMemoryErr
);
2687 context
->opRefs
= (DNSServiceRef
*) calloc( gBrowse_ServiceTypesCount
, sizeof( DNSServiceRef
) );
2688 require_action( context
->opRefs
, exit
, err
= kNoMemoryErr
);
2689 context
->opRefsCount
= gBrowse_ServiceTypesCount
;
2691 // Check command parameters.
2693 if( gBrowse_TimeLimitSecs
< 0 )
2695 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs
);
2700 // Create main connection.
2702 if( gConnectionOpt
)
2704 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
2705 require_noerr_quiet( err
, exit
);
2706 useMainConnection
= true;
2710 useMainConnection
= false;
2715 context
->flags
= GetDNSSDFlagsFromOpts();
2716 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
2720 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
2721 require_noerr_quiet( err
, exit
);
2723 // Set remaining parameters.
2725 context
->serviceTypes
= gBrowse_ServiceTypes
;
2726 context
->serviceTypesCount
= gBrowse_ServiceTypesCount
;
2727 context
->domain
= gBrowse_Domain
;
2728 context
->doResolve
= gBrowse_DoResolve
? true : false;
2729 context
->timeLimitSecs
= gBrowse_TimeLimitSecs
;
2730 context
->doResolveTXTOnly
= gBrowse_QueryTXT
? true : false;
2734 BrowsePrintPrologue( context
);
2736 // Start operation(s).
2738 for( i
= 0; i
< context
->serviceTypesCount
; ++i
)
2740 DNSServiceRef sdRef
;
2742 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
2743 err
= DNSServiceBrowse( &sdRef
, context
->flags
, context
->ifIndex
, context
->serviceTypes
[ i
], context
->domain
,
2744 BrowseCallback
, context
);
2745 require_noerr( err
, exit
);
2747 context
->opRefs
[ i
] = sdRef
;
2748 if( !useMainConnection
)
2750 err
= DNSServiceSetDispatchQueue( context
->opRefs
[ i
], dispatch_get_main_queue() );
2751 require_noerr( err
, exit
);
2757 if( context
->timeLimitSecs
> 0 )
2759 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
2760 kExitReason_TimeLimit
, Exit
);
2765 dispatch_source_forget( &signalSource
);
2766 if( context
) BrowseContextFree( context
);
2767 if( err
) exit( 1 );
2770 //===========================================================================================================================
2771 // BrowsePrintPrologue
2772 //===========================================================================================================================
2774 static void BrowsePrintPrologue( const BrowseContext
*inContext
)
2776 const int timeLimitSecs
= inContext
->timeLimitSecs
;
2777 const char * const * ptr
= (const char **) inContext
->serviceTypes
;
2778 const char * const * const end
= (const char **) inContext
->serviceTypes
+ inContext
->serviceTypesCount
;
2779 char ifName
[ kInterfaceNameBufLen
];
2781 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
2783 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
2784 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
2785 FPrintF( stdout
, "Service types: %s", *ptr
++ );
2786 while( ptr
< end
) FPrintF( stdout
, ", %s", *ptr
++ );
2787 FPrintF( stdout
, "\n" );
2788 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "<NULL> (default domains)" );
2789 FPrintF( stdout
, "Time limit: " );
2790 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
2791 else FPrintF( stdout
, "∞\n" );
2792 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
2793 FPrintF( stdout
, "---\n" );
2796 //===========================================================================================================================
2797 // BrowseContextFree
2798 //===========================================================================================================================
2800 static void BrowseContextFree( BrowseContext
*inContext
)
2804 for( i
= 0; i
< inContext
->opRefsCount
; ++i
)
2806 DNSServiceForget( &inContext
->opRefs
[ i
] );
2808 if( inContext
->serviceTypes
)
2810 StringArray_Free( inContext
->serviceTypes
, inContext
->serviceTypesCount
);
2811 inContext
->serviceTypes
= NULL
;
2812 inContext
->serviceTypesCount
= 0;
2814 DNSServiceForget( &inContext
->mainRef
);
2818 //===========================================================================================================================
2819 // BrowseResolveOpCreate
2820 //===========================================================================================================================
2822 static OSStatus
BrowseResolveOpCreate( const char *inFullName
, uint32_t inInterfaceIndex
, BrowseResolveOp
**outOp
)
2825 BrowseResolveOp
* resolveOp
;
2827 resolveOp
= (BrowseResolveOp
*) calloc( 1, sizeof( *resolveOp
) );
2828 require_action( resolveOp
, exit
, err
= kNoMemoryErr
);
2830 resolveOp
->fullName
= strdup( inFullName
);
2831 require_action( resolveOp
->fullName
, exit
, err
= kNoMemoryErr
);
2833 resolveOp
->interfaceIndex
= inInterfaceIndex
;
2840 if( resolveOp
) BrowseResolveOpFree( resolveOp
);
2844 //===========================================================================================================================
2845 // BrowseResolveOpFree
2846 //===========================================================================================================================
2848 static void BrowseResolveOpFree( BrowseResolveOp
*inOp
)
2850 DNSServiceForget( &inOp
->sdRef
);
2851 ForgetMem( &inOp
->fullName
);
2855 //===========================================================================================================================
2857 //===========================================================================================================================
2859 static void DNSSD_API
2861 DNSServiceRef inSDRef
,
2862 DNSServiceFlags inFlags
,
2863 uint32_t inInterfaceIndex
,
2864 DNSServiceErrorType inError
,
2865 const char * inName
,
2866 const char * inRegType
,
2867 const char * inDomain
,
2870 BrowseContext
* const context
= (BrowseContext
*) inContext
;
2872 BrowseResolveOp
* newOp
= NULL
;
2873 BrowseResolveOp
** p
;
2874 char fullName
[ kDNSServiceMaxDomainName
];
2879 gettimeofday( &now
, NULL
);
2882 require_noerr( err
, exit
);
2884 if( !context
->printedHeader
)
2886 FPrintF( stdout
, "%-26s %-14s IF %-20s %-20s Instance Name\n", "Timestamp", "Flags", "Domain", "Service Type" );
2887 context
->printedHeader
= true;
2889 FPrintF( stdout
, "%{du:time} %{du:arflags} %2d %-20s %-20s %s\n",
2890 &now
, inFlags
, (int32_t) inInterfaceIndex
, inDomain
, inRegType
, inName
);
2892 if( !context
->doResolve
&& !context
->doResolveTXTOnly
) goto exit
;
2894 err
= DNSServiceConstructFullName( fullName
, inName
, inRegType
, inDomain
);
2895 require_noerr( err
, exit
);
2897 if( inFlags
& kDNSServiceFlagsAdd
)
2899 DNSServiceRef sdRef
;
2900 DNSServiceFlags flags
;
2902 err
= BrowseResolveOpCreate( fullName
, inInterfaceIndex
, &newOp
);
2903 require_noerr( err
, exit
);
2905 if( context
->mainRef
)
2907 sdRef
= context
->mainRef
;
2908 flags
= kDNSServiceFlagsShareConnection
;
2914 if( context
->doResolve
)
2916 err
= DNSServiceResolve( &sdRef
, flags
, inInterfaceIndex
, inName
, inRegType
, inDomain
, BrowseResolveCallback
,
2918 require_noerr( err
, exit
);
2922 err
= DNSServiceQueryRecord( &sdRef
, flags
, inInterfaceIndex
, fullName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
,
2923 BrowseQueryRecordCallback
, NULL
);
2924 require_noerr( err
, exit
);
2927 newOp
->sdRef
= sdRef
;
2928 if( !context
->mainRef
)
2930 err
= DNSServiceSetDispatchQueue( newOp
->sdRef
, dispatch_get_main_queue() );
2931 require_noerr( err
, exit
);
2933 for( p
= &context
->resolveList
; *p
; p
= &( *p
)->next
) {}
2939 BrowseResolveOp
* resolveOp
;
2941 for( p
= &context
->resolveList
; ( resolveOp
= *p
) != NULL
; p
= &resolveOp
->next
)
2943 if( ( resolveOp
->interfaceIndex
== inInterfaceIndex
) && ( strcasecmp( resolveOp
->fullName
, fullName
) == 0 ) )
2950 *p
= resolveOp
->next
;
2951 BrowseResolveOpFree( resolveOp
);
2956 if( newOp
) BrowseResolveOpFree( newOp
);
2957 if( err
) exit( 1 );
2960 //===========================================================================================================================
2961 // BrowseQueryRecordCallback
2962 //===========================================================================================================================
2964 static void DNSSD_API
2965 BrowseQueryRecordCallback(
2966 DNSServiceRef inSDRef
,
2967 DNSServiceFlags inFlags
,
2968 uint32_t inInterfaceIndex
,
2969 DNSServiceErrorType inError
,
2970 const char * inFullName
,
2973 uint16_t inRDataLen
,
2974 const void * inRDataPtr
,
2984 Unused( inContext
);
2986 gettimeofday( &now
, NULL
);
2989 require_noerr( err
, exit
);
2990 require_action( inType
== kDNSServiceType_TXT
, exit
, err
= kTypeErr
);
2992 FPrintF( stdout
, "%{du:time} %s %s TXT on interface %d\n TXT: %#{txt}\n",
2993 &now
, ( inFlags
& kDNSServiceFlagsAdd
) ? "Add" : "Rmv", inFullName
, (int32_t) inInterfaceIndex
,
2994 inRDataPtr
, (size_t) inRDataLen
);
2997 if( err
) exit( 1 );
3000 //===========================================================================================================================
3001 // BrowseResolveCallback
3002 //===========================================================================================================================
3004 static void DNSSD_API
3005 BrowseResolveCallback(
3006 DNSServiceRef inSDRef
,
3007 DNSServiceFlags inFlags
,
3008 uint32_t inInterfaceIndex
,
3009 DNSServiceErrorType inError
,
3010 const char * inFullName
,
3011 const char * inHostname
,
3014 const unsigned char * inTXTPtr
,
3018 char errorStr
[ 64 ];
3022 Unused( inContext
);
3024 gettimeofday( &now
, NULL
);
3026 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " error %#m", inError
);
3028 FPrintF( stdout
, "%{du:time} %s can be reached at %s:%u (interface %d)%?s\n",
3029 &now
, inFullName
, inHostname
, ntohs( inPort
), (int32_t) inInterfaceIndex
, inError
, errorStr
);
3032 FPrintF( stdout
, " TXT record: %#H\n", inTXTPtr
, (int) inTXTLen
, INT_MAX
);
3036 FPrintF( stdout
, " TXT record: %#{txt}\n", inTXTPtr
, (size_t) inTXTLen
);
3040 //===========================================================================================================================
3042 //===========================================================================================================================
3046 DNSServiceRef mainRef
; // Main sdRef for shared connection.
3047 DNSServiceRef opRef
; // sdRef for the DNSServiceGetAddrInfo operation.
3048 const char * name
; // Hostname to resolve.
3049 DNSServiceFlags flags
; // Flags argument for DNSServiceGetAddrInfo().
3050 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceGetAddrInfo().
3051 uint32_t ifIndex
; // Interface index argument for DNSServiceGetAddrInfo().
3052 int timeLimitSecs
; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
3053 Boolean printedHeader
; // True if the results header has been printed.
3054 Boolean oneShotMode
; // True if command is done after the first set of results (one-shot mode).
3055 Boolean needIPv4
; // True if in one-shot mode and an IPv4 result is needed.
3056 Boolean needIPv6
; // True if in one-shot mode and an IPv6 result is needed.
3058 } GetAddrInfoContext
;
3060 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext
*inContext
);
3061 static void GetAddrInfoContextFree( GetAddrInfoContext
*inContext
);
3062 static void DNSSD_API
3063 GetAddrInfoCallback(
3064 DNSServiceRef inSDRef
,
3065 DNSServiceFlags inFlags
,
3066 uint32_t inInterfaceIndex
,
3067 DNSServiceErrorType inError
,
3068 const char * inHostname
,
3069 const struct sockaddr
* inSockAddr
,
3073 static void GetAddrInfoCmd( void )
3076 DNSServiceRef sdRef
;
3077 GetAddrInfoContext
* context
= NULL
;
3078 dispatch_source_t signalSource
= NULL
;
3079 int useMainConnection
;
3081 // Set up SIGINT handler.
3083 signal( SIGINT
, SIG_IGN
);
3084 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3085 require_noerr( err
, exit
);
3086 dispatch_resume( signalSource
);
3088 // Check command parameters.
3090 if( gGetAddrInfo_TimeLimitSecs
< 0 )
3092 FPrintF( stderr
, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs
);
3099 context
= (GetAddrInfoContext
*) calloc( 1, sizeof( *context
) );
3100 require_action( context
, exit
, err
= kNoMemoryErr
);
3102 // Create main connection.
3104 if( gConnectionOpt
)
3106 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
3107 require_noerr_quiet( err
, exit
);
3108 useMainConnection
= true;
3112 useMainConnection
= false;
3117 context
->flags
= GetDNSSDFlagsFromOpts();
3118 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
3122 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3123 require_noerr_quiet( err
, exit
);
3125 // Set remaining parameters.
3127 context
->name
= gGetAddrInfo_Name
;
3128 context
->timeLimitSecs
= gGetAddrInfo_TimeLimitSecs
;
3129 if( gGetAddrInfo_ProtocolIPv4
) context
->protocols
|= kDNSServiceProtocol_IPv4
;
3130 if( gGetAddrInfo_ProtocolIPv6
) context
->protocols
|= kDNSServiceProtocol_IPv6
;
3131 if( gGetAddrInfo_OneShot
)
3133 context
->oneShotMode
= true;
3134 context
->needIPv4
= ( gGetAddrInfo_ProtocolIPv4
|| !gGetAddrInfo_ProtocolIPv6
) ? true : false;
3135 context
->needIPv6
= ( gGetAddrInfo_ProtocolIPv6
|| !gGetAddrInfo_ProtocolIPv4
) ? true : false;
3140 GetAddrInfoPrintPrologue( context
);
3144 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
3145 err
= DNSServiceGetAddrInfo( &sdRef
, context
->flags
, context
->ifIndex
, context
->protocols
, context
->name
,
3146 GetAddrInfoCallback
, context
);
3147 require_noerr( err
, exit
);
3149 context
->opRef
= sdRef
;
3150 if( !useMainConnection
)
3152 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3153 require_noerr( err
, exit
);
3158 if( context
->timeLimitSecs
> 0 )
3160 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
3161 kExitReason_TimeLimit
, Exit
);
3166 dispatch_source_forget( &signalSource
);
3167 if( context
) GetAddrInfoContextFree( context
);
3168 if( err
) exit( 1 );
3171 //===========================================================================================================================
3172 // GetAddrInfoPrintPrologue
3173 //===========================================================================================================================
3175 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext
*inContext
)
3177 const int timeLimitSecs
= inContext
->timeLimitSecs
;
3178 char ifName
[ kInterfaceNameBufLen
];
3180 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3182 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3183 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3184 FPrintF( stdout
, "Protocols: %#{flags}\n", inContext
->protocols
, kDNSServiceProtocolDescriptors
);
3185 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
3186 FPrintF( stdout
, "Mode: %s\n", inContext
->oneShotMode
? "one-shot" : "continuous" );
3187 FPrintF( stdout
, "Time limit: " );
3188 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
3189 else FPrintF( stdout
, "∞\n" );
3190 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3191 FPrintF( stdout
, "---\n" );
3194 //===========================================================================================================================
3195 // GetAddrInfoContextFree
3196 //===========================================================================================================================
3198 static void GetAddrInfoContextFree( GetAddrInfoContext
*inContext
)
3200 DNSServiceForget( &inContext
->opRef
);
3201 DNSServiceForget( &inContext
->mainRef
);
3205 //===========================================================================================================================
3206 // GetAddrInfoCallback
3207 //===========================================================================================================================
3209 static void DNSSD_API
3210 GetAddrInfoCallback(
3211 DNSServiceRef inSDRef
,
3212 DNSServiceFlags inFlags
,
3213 uint32_t inInterfaceIndex
,
3214 DNSServiceErrorType inError
,
3215 const char * inHostname
,
3216 const struct sockaddr
* inSockAddr
,
3220 GetAddrInfoContext
* const context
= (GetAddrInfoContext
*) inContext
;
3223 const char * addrStr
;
3224 char addrStrBuf
[ kSockAddrStringMaxSize
];
3228 gettimeofday( &now
, NULL
);
3232 case kDNSServiceErr_NoError
:
3233 case kDNSServiceErr_NoSuchRecord
:
3237 case kDNSServiceErr_Timeout
:
3238 Exit( kExitReason_Timeout
);
3245 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
3247 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
3254 err
= SockAddrToString( inSockAddr
, kSockAddrStringFlagsNone
, addrStrBuf
);
3255 require_noerr( err
, exit
);
3256 addrStr
= addrStrBuf
;
3260 addrStr
= ( inSockAddr
->sa_family
== AF_INET
) ? kNoSuchRecordAStr
: kNoSuchRecordAAAAStr
;
3263 if( !context
->printedHeader
)
3265 FPrintF( stdout
, "%-26s %-14s IF %-32s %-38s %6s\n", "Timestamp", "Flags", "Hostname", "Address", "TTL" );
3266 context
->printedHeader
= true;
3268 FPrintF( stdout
, "%{du:time} %{du:arflags} %2d %-32s %-38s %6u\n",
3269 &now
, inFlags
, (int32_t) inInterfaceIndex
, inHostname
, addrStr
, inTTL
);
3271 if( context
->oneShotMode
)
3273 if( inFlags
& kDNSServiceFlagsAdd
)
3275 if( inSockAddr
->sa_family
== AF_INET
) context
->needIPv4
= false;
3276 else context
->needIPv6
= false;
3278 if( !( inFlags
& kDNSServiceFlagsMoreComing
) && !context
->needIPv4
&& !context
->needIPv6
)
3280 Exit( kExitReason_OneShotDone
);
3285 if( err
) exit( 1 );
3288 //===========================================================================================================================
3290 //===========================================================================================================================
3294 DNSServiceRef mainRef
; // Main sdRef for shared connection.
3295 DNSServiceRef opRef
; // sdRef for the DNSServiceQueryRecord operation.
3296 const char * recordName
; // Resource record name argument for DNSServiceQueryRecord().
3297 DNSServiceFlags flags
; // Flags argument for DNSServiceQueryRecord().
3298 uint32_t ifIndex
; // Interface index argument for DNSServiceQueryRecord().
3299 int timeLimitSecs
; // Time limit for the DNSServiceQueryRecord() operation in seconds.
3300 uint16_t recordType
; // Resource record type argument for DNSServiceQueryRecord().
3301 Boolean printedHeader
; // True if the results header was printed.
3302 Boolean oneShotMode
; // True if command is done after the first set of results (one-shot mode).
3303 Boolean gotRecord
; // True if in one-shot mode and received at least one record of the desired type.
3304 Boolean printRawRData
; // True if RDATA results are not to be formatted when printed.
3306 } QueryRecordContext
;
3308 static void QueryRecordPrintPrologue( const QueryRecordContext
*inContext
);
3309 static void QueryRecordContextFree( QueryRecordContext
*inContext
);
3310 static void DNSSD_API
3311 QueryRecordCallback(
3312 DNSServiceRef inSDRef
,
3313 DNSServiceFlags inFlags
,
3314 uint32_t inInterfaceIndex
,
3315 DNSServiceErrorType inError
,
3316 const char * inFullName
,
3319 uint16_t inRDataLen
,
3320 const void * inRDataPtr
,
3324 static void QueryRecordCmd( void )
3327 DNSServiceRef sdRef
;
3328 QueryRecordContext
* context
= NULL
;
3329 dispatch_source_t signalSource
= NULL
;
3330 int useMainConnection
;
3332 // Set up SIGINT handler.
3334 signal( SIGINT
, SIG_IGN
);
3335 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3336 require_noerr( err
, exit
);
3337 dispatch_resume( signalSource
);
3341 context
= (QueryRecordContext
*) calloc( 1, sizeof( *context
) );
3342 require_action( context
, exit
, err
= kNoMemoryErr
);
3344 // Check command parameters.
3346 if( gQueryRecord_TimeLimitSecs
< 0 )
3348 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs
);
3353 // Create main connection.
3355 if( gConnectionOpt
)
3357 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
3358 require_noerr_quiet( err
, exit
);
3359 useMainConnection
= true;
3363 useMainConnection
= false;
3368 context
->flags
= GetDNSSDFlagsFromOpts();
3369 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
3373 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3374 require_noerr_quiet( err
, exit
);
3378 err
= RecordTypeFromArgString( gQueryRecord_Type
, &context
->recordType
);
3379 require_noerr( err
, exit
);
3381 // Set remaining parameters.
3383 context
->recordName
= gQueryRecord_Name
;
3384 context
->timeLimitSecs
= gQueryRecord_TimeLimitSecs
;
3385 context
->oneShotMode
= gQueryRecord_OneShot
? true : false;
3386 context
->printRawRData
= gQueryRecord_RawRData
? true : false;
3390 QueryRecordPrintPrologue( context
);
3394 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
3395 err
= DNSServiceQueryRecord( &sdRef
, context
->flags
, context
->ifIndex
, context
->recordName
, context
->recordType
,
3396 kDNSServiceClass_IN
, QueryRecordCallback
, context
);
3397 require_noerr( err
, exit
);
3399 context
->opRef
= sdRef
;
3400 if( !useMainConnection
)
3402 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3403 require_noerr( err
, exit
);
3408 if( context
->timeLimitSecs
> 0 )
3410 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_TimeLimit
,
3416 dispatch_source_forget( &signalSource
);
3417 if( context
) QueryRecordContextFree( context
);
3418 if( err
) exit( 1 );
3421 //===========================================================================================================================
3422 // QueryRecordContextFree
3423 //===========================================================================================================================
3425 static void QueryRecordContextFree( QueryRecordContext
*inContext
)
3427 DNSServiceForget( &inContext
->opRef
);
3428 DNSServiceForget( &inContext
->mainRef
);
3432 //===========================================================================================================================
3433 // QueryRecordPrintPrologue
3434 //===========================================================================================================================
3436 static void QueryRecordPrintPrologue( const QueryRecordContext
*inContext
)
3438 const int timeLimitSecs
= inContext
->timeLimitSecs
;
3439 char ifName
[ kInterfaceNameBufLen
];
3441 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3443 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3444 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3445 FPrintF( stdout
, "Name: %s\n", inContext
->recordName
);
3446 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->recordType
), inContext
->recordType
);
3447 FPrintF( stdout
, "Mode: %s\n", inContext
->oneShotMode
? "one-shot" : "continuous" );
3448 FPrintF( stdout
, "Time limit: " );
3449 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
3450 else FPrintF( stdout
, "∞\n" );
3451 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3452 FPrintF( stdout
, "---\n" );
3456 //===========================================================================================================================
3457 // QueryRecordCallback
3458 //===========================================================================================================================
3460 static void DNSSD_API
3461 QueryRecordCallback(
3462 DNSServiceRef inSDRef
,
3463 DNSServiceFlags inFlags
,
3464 uint32_t inInterfaceIndex
,
3465 DNSServiceErrorType inError
,
3466 const char * inFullName
,
3469 uint16_t inRDataLen
,
3470 const void * inRDataPtr
,
3474 QueryRecordContext
* const context
= (QueryRecordContext
*) inContext
;
3477 char * rdataStr
= NULL
;
3481 gettimeofday( &now
, NULL
);
3485 case kDNSServiceErr_NoError
:
3486 case kDNSServiceErr_NoSuchRecord
:
3490 case kDNSServiceErr_Timeout
:
3491 Exit( kExitReason_Timeout
);
3498 if( inError
!= kDNSServiceErr_NoSuchRecord
)
3500 if( !context
->printRawRData
) DNSRecordDataToString( inRDataPtr
, inRDataLen
, inType
, NULL
, 0, &rdataStr
);
3503 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, INT_MAX
);
3504 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
3508 if( !context
->printedHeader
)
3510 FPrintF( stdout
, "%-26s %-14s IF %-32s %-5s %-5s %6s RData\n",
3511 "Timestamp", "Flags", "Name", "Type", "Class", "TTL" );
3512 context
->printedHeader
= true;
3514 FPrintF( stdout
, "%{du:time} %{du:arflags} %2d %-32s %-5s %?-5s%?5u %6u %s\n",
3515 &now
, inFlags
, (int32_t) inInterfaceIndex
, inFullName
, RecordTypeToString( inType
),
3516 ( inClass
== kDNSServiceClass_IN
), "IN", ( inClass
!= kDNSServiceClass_IN
), inClass
, inTTL
,
3517 rdataStr
? rdataStr
: kNoSuchRecordStr
);
3519 if( context
->oneShotMode
)
3521 if( ( inFlags
& kDNSServiceFlagsAdd
) &&
3522 ( ( context
->recordType
== kDNSServiceType_ANY
) || ( context
->recordType
== inType
) ) )
3524 context
->gotRecord
= true;
3526 if( !( inFlags
& kDNSServiceFlagsMoreComing
) && context
->gotRecord
) Exit( kExitReason_OneShotDone
);
3530 FreeNullSafe( rdataStr
);
3531 if( err
) exit( 1 );
3534 //===========================================================================================================================
3536 //===========================================================================================================================
3540 DNSRecordRef recordRef
; // Reference returned by DNSServiceAddRecord().
3541 uint8_t * dataPtr
; // Record data.
3542 size_t dataLen
; // Record data length.
3543 uint32_t ttl
; // Record TTL value.
3544 uint16_t type
; // Record type.
3550 DNSServiceRef opRef
; // sdRef for DNSServiceRegister operation.
3551 const char * name
; // Service name argument for DNSServiceRegister().
3552 const char * type
; // Service type argument for DNSServiceRegister().
3553 const char * domain
; // Domain in which advertise the service.
3554 uint8_t * txtPtr
; // Service TXT record data. (malloc'd)
3555 size_t txtLen
; // Service TXT record data len.
3556 ExtraRecord
* extraRecords
; // Array of extra records to add to registered service.
3557 size_t extraRecordsCount
; // Number of extra records.
3558 uint8_t * updateTXTPtr
; // Pointer to record data for TXT record update. (malloc'd)
3559 size_t updateTXTLen
; // Length of record data for TXT record update.
3560 uint32_t updateTTL
; // TTL of updated TXT record.
3561 int updateDelayMs
; // Post-registration TXT record update delay in milliseconds.
3562 DNSServiceFlags flags
; // Flags argument for DNSServiceRegister().
3563 uint32_t ifIndex
; // Interface index argument for DNSServiceRegister().
3564 int lifetimeMs
; // Lifetime of the record registration in milliseconds.
3565 uint16_t port
; // Service instance's port number.
3566 Boolean printedHeader
; // True if results header was printed.
3567 Boolean didRegister
; // True if service was registered.
3571 static void RegisterPrintPrologue( const RegisterContext
*inContext
);
3572 static void RegisterContextFree( RegisterContext
*inContext
);
3573 static void DNSSD_API
3575 DNSServiceRef inSDRef
,
3576 DNSServiceFlags inFlags
,
3577 DNSServiceErrorType inError
,
3578 const char * inName
,
3579 const char * inType
,
3580 const char * inDomain
,
3582 static void RegisterUpdate( void *inContext
);
3584 static void RegisterCmd( void )
3587 RegisterContext
* context
= NULL
;
3588 dispatch_source_t signalSource
= NULL
;
3590 // Set up SIGINT handler.
3592 signal( SIGINT
, SIG_IGN
);
3593 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3594 require_noerr( err
, exit
);
3595 dispatch_resume( signalSource
);
3599 context
= (RegisterContext
*) calloc( 1, sizeof( *context
) );
3600 require_action( context
, exit
, err
= kNoMemoryErr
);
3602 // Check command parameters.
3604 if( ( gRegister_Port
< 0 ) || ( gRegister_Port
> UINT16_MAX
) )
3606 FPrintF( stderr
, "Port number %d is out-of-range.\n", gRegister_Port
);
3611 if( ( gAddRecord_DataCount
!= gAddRecord_TypesCount
) || ( gAddRecord_TTLsCount
!= gAddRecord_TypesCount
) )
3613 FPrintF( stderr
, "There are missing additional record parameters.\n" );
3620 context
->flags
= GetDNSSDFlagsFromOpts();
3622 // Get interface index.
3624 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3625 require_noerr_quiet( err
, exit
);
3627 // Get TXT record data.
3631 err
= RecordDataFromArgString( gRegister_TXT
, &context
->txtPtr
, &context
->txtLen
);
3632 require_noerr_quiet( err
, exit
);
3635 // Set remaining parameters.
3637 context
->name
= gRegister_Name
;
3638 context
->type
= gRegister_Type
;
3639 context
->domain
= gRegister_Domain
;
3640 context
->port
= (uint16_t) gRegister_Port
;
3641 context
->lifetimeMs
= gRegister_LifetimeMs
;
3643 if( gAddRecord_TypesCount
> 0 )
3647 context
->extraRecords
= (ExtraRecord
*) calloc( gAddRecord_TypesCount
, sizeof( ExtraRecord
) );
3648 require_action( context
, exit
, err
= kNoMemoryErr
);
3649 context
->extraRecordsCount
= gAddRecord_TypesCount
;
3651 for( i
= 0; i
< gAddRecord_TypesCount
; ++i
)
3653 ExtraRecord
* const extraRecord
= &context
->extraRecords
[ i
];
3655 err
= RecordTypeFromArgString( gAddRecord_Types
[ i
], &extraRecord
->type
);
3656 require_noerr( err
, exit
);
3658 err
= StringToUInt32( gAddRecord_TTLs
[ i
], &extraRecord
->ttl
);
3661 FPrintF( stderr
, "Invalid TTL value: %s\n", gAddRecord_TTLs
[ i
] );
3666 err
= RecordDataFromArgString( gAddRecord_Data
[ i
], &extraRecord
->dataPtr
, &extraRecord
->dataLen
);
3667 require_noerr_quiet( err
, exit
);
3671 if( gUpdateRecord_Data
)
3673 err
= RecordDataFromArgString( gUpdateRecord_Data
, &context
->updateTXTPtr
, &context
->updateTXTLen
);
3674 require_noerr_quiet( err
, exit
);
3676 context
->updateTTL
= (uint32_t) gUpdateRecord_TTL
;
3677 context
->updateDelayMs
= gUpdateRecord_DelayMs
;
3682 RegisterPrintPrologue( context
);
3686 err
= DNSServiceRegister( &context
->opRef
, context
->flags
, context
->ifIndex
, context
->name
, context
->type
,
3687 context
->domain
, NULL
, htons( context
->port
), (uint16_t) context
->txtLen
, context
->txtPtr
,
3688 RegisterCallback
, context
);
3689 ForgetMem( &context
->txtPtr
);
3690 require_noerr( err
, exit
);
3692 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3693 require_noerr( err
, exit
);
3698 dispatch_source_forget( &signalSource
);
3699 if( context
) RegisterContextFree( context
);
3700 if( err
) exit( 1 );
3703 //===========================================================================================================================
3704 // RegisterPrintPrologue
3705 //===========================================================================================================================
3707 static void RegisterPrintPrologue( const RegisterContext
*inContext
)
3711 char ifName
[ kInterfaceNameBufLen
];
3713 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3715 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3716 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3717 FPrintF( stdout
, "Name: %s\n", inContext
->name
? inContext
->name
: "<NULL>" );
3718 FPrintF( stdout
, "Type: %s\n", inContext
->type
);
3719 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "<NULL> (default domains)" );
3720 FPrintF( stdout
, "Port: %u\n", inContext
->port
);
3721 FPrintF( stdout
, "TXT data: %#{txt}\n", inContext
->txtPtr
, inContext
->txtLen
);
3722 infinite
= ( inContext
->lifetimeMs
< 0 ) ? true : false;
3723 FPrintF( stdout
, "Lifetime: %?s%?d ms\n", infinite
, "∞", !infinite
, inContext
->lifetimeMs
);
3724 if( inContext
->updateTXTPtr
)
3726 FPrintF( stdout
, "\nUpdate record:\n" );
3727 FPrintF( stdout
, " Delay: %d ms\n", ( inContext
->updateDelayMs
> 0 ) ? inContext
->updateDelayMs
: 0 );
3728 FPrintF( stdout
, " TTL: %u%?s\n",
3729 inContext
->updateTTL
, inContext
->updateTTL
== 0, " (system will use a default value.)" );
3730 FPrintF( stdout
, " TXT data: %#{txt}\n", inContext
->updateTXTPtr
, inContext
->updateTXTLen
);
3732 if( inContext
->extraRecordsCount
> 0 ) FPrintF( stdout
, "\n" );
3733 for( i
= 0; i
< inContext
->extraRecordsCount
; ++i
)
3735 const ExtraRecord
* record
= &inContext
->extraRecords
[ i
];
3737 FPrintF( stdout
, "Extra record %zu:\n", i
+ 1 );
3738 FPrintF( stdout
, " Type: %s (%u)\n", RecordTypeToString( record
->type
), record
->type
);
3739 FPrintF( stdout
, " TTL: %u%?s\n", record
->ttl
, record
->ttl
== 0, " (system will use a default value.)" );
3740 FPrintF( stdout
, " RData: %#H\n\n", record
->dataPtr
, (int) record
->dataLen
, INT_MAX
);
3742 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3743 FPrintF( stdout
, "---\n" );
3746 //===========================================================================================================================
3747 // RegisterContextFree
3748 //===========================================================================================================================
3750 static void RegisterContextFree( RegisterContext
*inContext
)
3752 ExtraRecord
* record
;
3753 const ExtraRecord
* const end
= inContext
->extraRecords
+ inContext
->extraRecordsCount
;
3755 DNSServiceForget( &inContext
->opRef
);
3756 ForgetMem( &inContext
->txtPtr
);
3757 for( record
= inContext
->extraRecords
; record
< end
; ++record
)
3759 check( !record
->recordRef
);
3760 ForgetMem( &record
->dataPtr
);
3762 ForgetMem( &inContext
->extraRecords
);
3763 ForgetMem( &inContext
->updateTXTPtr
);
3767 //===========================================================================================================================
3769 //===========================================================================================================================
3771 static void DNSSD_API
3773 DNSServiceRef inSDRef
,
3774 DNSServiceFlags inFlags
,
3775 DNSServiceErrorType inError
,
3776 const char * inName
,
3777 const char * inType
,
3778 const char * inDomain
,
3781 RegisterContext
* const context
= (RegisterContext
*) inContext
;
3787 gettimeofday( &now
, NULL
);
3789 if( !context
->printedHeader
)
3791 FPrintF( stdout
, "%-26s %-14s Service\n", "Timestamp", "Flags" );
3792 context
->printedHeader
= true;
3794 FPrintF( stdout
, "%{du:time} %{du:arflags} %s.%s%s %?#m\n", &now
, inFlags
, inName
, inType
, inDomain
, inError
, inError
);
3796 require_noerr_action_quiet( inError
, exit
, err
= inError
);
3798 if( !context
->didRegister
&& ( inFlags
& kDNSServiceFlagsAdd
) )
3800 context
->didRegister
= true;
3801 if( context
->updateTXTPtr
)
3803 if( context
->updateDelayMs
> 0 )
3805 dispatch_after_f( dispatch_time_milliseconds( context
->updateDelayMs
), dispatch_get_main_queue(),
3806 context
, RegisterUpdate
);
3810 RegisterUpdate( context
);
3813 if( context
->extraRecordsCount
> 0 )
3815 ExtraRecord
* record
;
3816 const ExtraRecord
* const end
= context
->extraRecords
+ context
->extraRecordsCount
;
3818 for( record
= context
->extraRecords
; record
< end
; ++record
)
3820 err
= DNSServiceAddRecord( context
->opRef
, &record
->recordRef
, 0, record
->type
,
3821 (uint16_t) record
->dataLen
, record
->dataPtr
, record
->ttl
);
3822 require_noerr( err
, exit
);
3825 if( context
->lifetimeMs
== 0 )
3827 Exit( kExitReason_TimeLimit
);
3829 else if( context
->lifetimeMs
> 0 )
3831 dispatch_after_f( dispatch_time_milliseconds( context
->lifetimeMs
), dispatch_get_main_queue(),
3832 kExitReason_TimeLimit
, Exit
);
3838 if( err
) exit( 1 );
3841 //===========================================================================================================================
3843 //===========================================================================================================================
3845 static void RegisterUpdate( void *inContext
)
3848 RegisterContext
* const context
= (RegisterContext
*) inContext
;
3850 err
= DNSServiceUpdateRecord( context
->opRef
, NULL
, 0, (uint16_t) context
->updateTXTLen
, context
->updateTXTPtr
,
3851 context
->updateTTL
);
3852 require_noerr( err
, exit
);
3855 if( err
) exit( 1 );
3858 //===========================================================================================================================
3859 // RegisterRecordCmd
3860 //===========================================================================================================================
3864 DNSServiceRef conRef
; // sdRef to be initialized by DNSServiceCreateConnection().
3865 DNSRecordRef recordRef
; // Registered record reference.
3866 const char * recordName
; // Name of resource record.
3867 uint8_t * dataPtr
; // Pointer to resource record data.
3868 size_t dataLen
; // Length of resource record data.
3869 uint32_t ttl
; // TTL value of resource record in seconds.
3870 uint32_t ifIndex
; // Interface index argument for DNSServiceRegisterRecord().
3871 DNSServiceFlags flags
; // Flags argument for DNSServiceRegisterRecord().
3872 int lifetimeMs
; // Lifetime of the record registration in milliseconds.
3873 uint16_t recordType
; // Resource record type.
3874 uint8_t * updateDataPtr
; // Pointer to data for record update. (malloc'd)
3875 size_t updateDataLen
; // Length of data for record update.
3876 uint32_t updateTTL
; // TTL for updated record.
3877 int updateDelayMs
; // Post-registration record update delay in milliseconds.
3878 Boolean didRegister
; // True if the record was registered.
3880 } RegisterRecordContext
;
3882 static void RegisterRecordPrintPrologue( const RegisterRecordContext
*inContext
);
3883 static void RegisterRecordContextFree( RegisterRecordContext
*inContext
);
3884 static void DNSSD_API
3885 RegisterRecordCallback(
3886 DNSServiceRef inSDRef
,
3887 DNSRecordRef inRecordRef
,
3888 DNSServiceFlags inFlags
,
3889 DNSServiceErrorType inError
,
3891 static void RegisterRecordUpdate( void *inContext
);
3893 static void RegisterRecordCmd( void )
3896 RegisterRecordContext
* context
= NULL
;
3897 dispatch_source_t signalSource
= NULL
;
3899 // Set up SIGINT handler.
3901 signal( SIGINT
, SIG_IGN
);
3902 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3903 require_noerr( err
, exit
);
3904 dispatch_resume( signalSource
);
3908 context
= (RegisterRecordContext
*) calloc( 1, sizeof( *context
) );
3909 require_action( context
, exit
, err
= kNoMemoryErr
);
3911 // Create connection.
3913 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->conRef
, NULL
);
3914 require_noerr_quiet( err
, exit
);
3918 context
->flags
= GetDNSSDFlagsFromOpts();
3922 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3923 require_noerr_quiet( err
, exit
);
3927 err
= RecordTypeFromArgString( gRegisterRecord_Type
, &context
->recordType
);
3928 require_noerr( err
, exit
);
3932 if( gRegisterRecord_Data
)
3934 err
= RecordDataFromArgString( gRegisterRecord_Data
, &context
->dataPtr
, &context
->dataLen
);
3935 require_noerr_quiet( err
, exit
);
3938 // Set remaining parameters.
3940 context
->recordName
= gRegisterRecord_Name
;
3941 context
->ttl
= (uint32_t) gRegisterRecord_TTL
;
3942 context
->lifetimeMs
= gRegisterRecord_LifetimeMs
;
3946 if( gRegisterRecord_UpdateData
)
3948 err
= RecordDataFromArgString( gRegisterRecord_UpdateData
, &context
->updateDataPtr
, &context
->updateDataLen
);
3949 require_noerr_quiet( err
, exit
);
3951 context
->updateTTL
= (uint32_t) gRegisterRecord_UpdateTTL
;
3952 context
->updateDelayMs
= gRegisterRecord_UpdateDelayMs
;
3957 RegisterRecordPrintPrologue( context
);
3961 err
= DNSServiceRegisterRecord( context
->conRef
, &context
->recordRef
, context
->flags
, context
->ifIndex
,
3962 context
->recordName
, context
->recordType
, kDNSServiceClass_IN
, (uint16_t) context
->dataLen
, context
->dataPtr
,
3963 context
->ttl
, RegisterRecordCallback
, context
);
3966 FPrintF( stderr
, "DNSServiceRegisterRecord() returned %#m\n", err
);
3973 dispatch_source_forget( &signalSource
);
3974 if( context
) RegisterRecordContextFree( context
);
3975 if( err
) exit( 1 );
3978 //===========================================================================================================================
3979 // RegisterRecordPrintPrologue
3980 //===========================================================================================================================
3982 static void RegisterRecordPrintPrologue( const RegisterRecordContext
*inContext
)
3985 char ifName
[ kInterfaceNameBufLen
];
3987 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3989 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3990 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3991 FPrintF( stdout
, "Name: %s\n", inContext
->recordName
);
3992 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->recordType
), inContext
->recordType
);
3993 FPrintF( stdout
, "TTL: %u\n", inContext
->ttl
);
3994 FPrintF( stdout
, "Data: %#H\n", inContext
->dataPtr
, (int) inContext
->dataLen
, INT_MAX
);
3995 infinite
= ( inContext
->lifetimeMs
< 0 ) ? true : false;
3996 FPrintF( stdout
, "Lifetime: %?s%?d ms\n", infinite
, "∞", !infinite
, inContext
->lifetimeMs
);
3997 if( inContext
->updateDataPtr
)
3999 FPrintF( stdout
, "\nUpdate record:\n" );
4000 FPrintF( stdout
, " Delay: %d ms\n", ( inContext
->updateDelayMs
>= 0 ) ? inContext
->updateDelayMs
: 0 );
4001 FPrintF( stdout
, " TTL: %u%?s\n",
4002 inContext
->updateTTL
, inContext
->updateTTL
== 0, " (system will use a default value.)" );
4003 FPrintF( stdout
, " RData: %#H\n", inContext
->updateDataPtr
, (int) inContext
->updateDataLen
, INT_MAX
);
4005 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4006 FPrintF( stdout
, "---\n" );
4009 //===========================================================================================================================
4010 // RegisterRecordContextFree
4011 //===========================================================================================================================
4013 static void RegisterRecordContextFree( RegisterRecordContext
*inContext
)
4015 DNSServiceForget( &inContext
->conRef
);
4016 ForgetMem( &inContext
->dataPtr
);
4017 ForgetMem( &inContext
->updateDataPtr
);
4021 //===========================================================================================================================
4022 // RegisterRecordCallback
4023 //===========================================================================================================================
4026 RegisterRecordCallback(
4027 DNSServiceRef inSDRef
,
4028 DNSRecordRef inRecordRef
,
4029 DNSServiceFlags inFlags
,
4030 DNSServiceErrorType inError
,
4033 RegisterRecordContext
* context
= (RegisterRecordContext
*) inContext
;
4037 Unused( inRecordRef
);
4041 gettimeofday( &now
, NULL
);
4042 FPrintF( stdout
, "%{du:time} Record registration result (error %#m)\n", &now
, inError
);
4044 if( !context
->didRegister
&& !inError
)
4046 context
->didRegister
= true;
4047 if( context
->updateDataPtr
)
4049 if( context
->updateDelayMs
> 0 )
4051 dispatch_after_f( dispatch_time_milliseconds( context
->updateDelayMs
), dispatch_get_main_queue(),
4052 context
, RegisterRecordUpdate
);
4056 RegisterRecordUpdate( context
);
4059 if( context
->lifetimeMs
== 0 )
4061 Exit( kExitReason_TimeLimit
);
4063 else if( context
->lifetimeMs
> 0 )
4065 dispatch_after_f( dispatch_time_milliseconds( context
->lifetimeMs
), dispatch_get_main_queue(),
4066 kExitReason_TimeLimit
, Exit
);
4071 //===========================================================================================================================
4072 // RegisterRecordUpdate
4073 //===========================================================================================================================
4075 static void RegisterRecordUpdate( void *inContext
)
4078 RegisterRecordContext
* const context
= (RegisterRecordContext
*) inContext
;
4080 err
= DNSServiceUpdateRecord( context
->conRef
, context
->recordRef
, 0, (uint16_t) context
->updateDataLen
,
4081 context
->updateDataPtr
, context
->updateTTL
);
4082 require_noerr( err
, exit
);
4085 if( err
) exit( 1 );
4088 //===========================================================================================================================
4090 //===========================================================================================================================
4094 DNSServiceRef mainRef
; // Main sdRef for shared connections.
4095 DNSServiceRef opRef
; // sdRef for the DNSServiceResolve operation.
4096 DNSServiceFlags flags
; // Flags argument for DNSServiceResolve().
4097 const char * name
; // Service name argument for DNSServiceResolve().
4098 const char * type
; // Service type argument for DNSServiceResolve().
4099 const char * domain
; // Domain argument for DNSServiceResolve().
4100 uint32_t ifIndex
; // Interface index argument for DNSServiceResolve().
4101 int timeLimitSecs
; // Time limit for the DNSServiceResolve operation in seconds.
4105 static void ResolvePrintPrologue( const ResolveContext
*inContext
);
4106 static void ResolveContextFree( ResolveContext
*inContext
);
4107 static void DNSSD_API
4109 DNSServiceRef inSDRef
,
4110 DNSServiceFlags inFlags
,
4111 uint32_t inInterfaceIndex
,
4112 DNSServiceErrorType inError
,
4113 const char * inFullName
,
4114 const char * inHostname
,
4117 const unsigned char * inTXTPtr
,
4120 static void ResolveCmd( void )
4123 DNSServiceRef sdRef
;
4124 ResolveContext
* context
= NULL
;
4125 dispatch_source_t signalSource
= NULL
;
4126 int useMainConnection
;
4128 // Set up SIGINT handler.
4130 signal( SIGINT
, SIG_IGN
);
4131 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
4132 require_noerr( err
, exit
);
4133 dispatch_resume( signalSource
);
4137 context
= (ResolveContext
*) calloc( 1, sizeof( *context
) );
4138 require_action( context
, exit
, err
= kNoMemoryErr
);
4140 // Check command parameters.
4142 if( gResolve_TimeLimitSecs
< 0 )
4144 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs
);
4149 // Create main connection.
4151 if( gConnectionOpt
)
4153 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
4154 require_noerr_quiet( err
, exit
);
4155 useMainConnection
= true;
4159 useMainConnection
= false;
4164 context
->flags
= GetDNSSDFlagsFromOpts();
4165 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
4167 // Get interface index.
4169 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
4170 require_noerr_quiet( err
, exit
);
4172 // Set remaining parameters.
4174 context
->name
= gResolve_Name
;
4175 context
->type
= gResolve_Type
;
4176 context
->domain
= gResolve_Domain
;
4177 context
->timeLimitSecs
= gResolve_TimeLimitSecs
;
4181 ResolvePrintPrologue( context
);
4185 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
4186 err
= DNSServiceResolve( &sdRef
, context
->flags
, context
->ifIndex
, context
->name
, context
->type
, context
->domain
,
4187 ResolveCallback
, NULL
);
4188 require_noerr( err
, exit
);
4190 context
->opRef
= sdRef
;
4191 if( !useMainConnection
)
4193 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
4194 require_noerr( err
, exit
);
4199 if( context
->timeLimitSecs
> 0 )
4201 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
4202 kExitReason_TimeLimit
, Exit
);
4207 dispatch_source_forget( &signalSource
);
4208 if( context
) ResolveContextFree( context
);
4209 if( err
) exit( 1 );
4212 //===========================================================================================================================
4214 //===========================================================================================================================
4216 static void ReconfirmCmd( void )
4219 uint8_t * rdataPtr
= NULL
;
4220 size_t rdataLen
= 0;
4221 DNSServiceFlags flags
;
4223 uint16_t type
, class;
4224 char ifName
[ kInterfaceNameBufLen
];
4228 flags
= GetDNSSDFlagsFromOpts();
4230 // Get interface index.
4232 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
4233 require_noerr_quiet( err
, exit
);
4237 err
= RecordTypeFromArgString( gReconfirmRecord_Type
, &type
);
4238 require_noerr( err
, exit
);
4242 if( gReconfirmRecord_Data
)
4244 err
= RecordDataFromArgString( gReconfirmRecord_Data
, &rdataPtr
, &rdataLen
);
4245 require_noerr_quiet( err
, exit
);
4248 // Get record class.
4250 if( gReconfirmRecord_Class
)
4252 err
= RecordClassFromArgString( gReconfirmRecord_Class
, &class );
4253 require_noerr( err
, exit
);
4257 class = kDNSServiceClass_IN
;
4262 FPrintF( stdout
, "Flags: %#{flags}\n", flags
, kDNSServiceFlagsDescriptors
);
4263 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
4264 FPrintF( stdout
, "Name: %s\n", gReconfirmRecord_Name
);
4265 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
4266 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
4267 FPrintF( stdout
, "Data: %#H\n", rdataPtr
, (int) rdataLen
, INT_MAX
);
4268 FPrintF( stdout
, "---\n" );
4270 err
= DNSServiceReconfirmRecord( flags
, ifIndex
, gReconfirmRecord_Name
, type
, class, (uint16_t) rdataLen
, rdataPtr
);
4271 FPrintF( stdout
, "Error: %#m\n", err
);
4274 FreeNullSafe( rdataPtr
);
4275 if( err
) exit( 1 );
4278 //===========================================================================================================================
4279 // ResolvePrintPrologue
4280 //===========================================================================================================================
4282 static void ResolvePrintPrologue( const ResolveContext
*inContext
)
4284 const int timeLimitSecs
= inContext
->timeLimitSecs
;
4285 char ifName
[ kInterfaceNameBufLen
];
4287 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
4289 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
4290 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
4291 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
4292 FPrintF( stdout
, "Type: %s\n", inContext
->type
);
4293 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
);
4294 FPrintF( stdout
, "Time limit: " );
4295 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
4296 else FPrintF( stdout
, "∞\n" );
4297 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4298 FPrintF( stdout
, "---\n" );
4301 //===========================================================================================================================
4302 // ResolveContextFree
4303 //===========================================================================================================================
4305 static void ResolveContextFree( ResolveContext
*inContext
)
4307 DNSServiceForget( &inContext
->opRef
);
4308 DNSServiceForget( &inContext
->mainRef
);
4312 //===========================================================================================================================
4314 //===========================================================================================================================
4316 static void DNSSD_API
4318 DNSServiceRef inSDRef
,
4319 DNSServiceFlags inFlags
,
4320 uint32_t inInterfaceIndex
,
4321 DNSServiceErrorType inError
,
4322 const char * inFullName
,
4323 const char * inHostname
,
4326 const unsigned char * inTXTPtr
,
4330 char errorStr
[ 64 ];
4334 Unused( inContext
);
4336 gettimeofday( &now
, NULL
);
4338 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " error %#m", inError
);
4340 FPrintF( stdout
, "%{du:time}: %s can be reached at %s:%u (interface %d)%?s\n",
4341 &now
, inFullName
, inHostname
, ntohs( inPort
), (int32_t) inInterfaceIndex
, inError
, errorStr
);
4344 FPrintF( stdout
, " TXT record: %#H\n", inTXTPtr
, (int) inTXTLen
, INT_MAX
);
4348 FPrintF( stdout
, " TXT record: %#{txt}\n", inTXTPtr
, (size_t) inTXTLen
);
4352 //===========================================================================================================================
4353 // GetAddrInfoPOSIXCmd
4354 //===========================================================================================================================
4356 #define AddressFamilyStr( X ) ( \
4357 ( (X) == AF_INET ) ? "inet" : \
4358 ( (X) == AF_INET6 ) ? "inet6" : \
4359 ( (X) == AF_UNSPEC ) ? "unspec" : \
4369 #define CaseFlagStringify( X ) { (X), # X }
4371 const FlagStringPair kGAIPOSIXFlagStringPairs
[] =
4373 #if( defined( AI_UNUSABLE ) )
4374 CaseFlagStringify( AI_UNUSABLE
),
4376 CaseFlagStringify( AI_NUMERICSERV
),
4377 CaseFlagStringify( AI_V4MAPPED
),
4378 CaseFlagStringify( AI_ADDRCONFIG
),
4379 #if( defined( AI_V4MAPPED_CFG ) )
4380 CaseFlagStringify( AI_V4MAPPED_CFG
),
4382 CaseFlagStringify( AI_ALL
),
4383 CaseFlagStringify( AI_NUMERICHOST
),
4384 CaseFlagStringify( AI_CANONNAME
),
4385 CaseFlagStringify( AI_PASSIVE
),
4389 static void GetAddrInfoPOSIXCmd( void )
4392 struct addrinfo hints
;
4394 const struct addrinfo
* addrInfo
;
4395 struct addrinfo
* addrInfoList
= NULL
;
4396 const FlagStringPair
* pair
;
4398 memset( &hints
, 0, sizeof( hints
) );
4399 hints
.ai_socktype
= SOCK_STREAM
;
4401 // Set hints address family.
4403 if( !gGAIPOSIX_Family
) hints
.ai_family
= AF_UNSPEC
;
4404 else if( strcasecmp( gGAIPOSIX_Family
, "inet" ) == 0 ) hints
.ai_family
= AF_INET
;
4405 else if( strcasecmp( gGAIPOSIX_Family
, "inet6" ) == 0 ) hints
.ai_family
= AF_INET6
;
4406 else if( strcasecmp( gGAIPOSIX_Family
, "unspec" ) == 0 ) hints
.ai_family
= AF_UNSPEC
;
4409 FPrintF( stderr
, "Invalid address family: %s.\n", gGAIPOSIX_Family
);
4416 if( gGAIPOSIXFlag_AddrConfig
) hints
.ai_flags
|= AI_ADDRCONFIG
;
4417 if( gGAIPOSIXFlag_All
) hints
.ai_flags
|= AI_ALL
;
4418 if( gGAIPOSIXFlag_CanonName
) hints
.ai_flags
|= AI_CANONNAME
;
4419 if( gGAIPOSIXFlag_NumericHost
) hints
.ai_flags
|= AI_NUMERICHOST
;
4420 if( gGAIPOSIXFlag_NumericServ
) hints
.ai_flags
|= AI_NUMERICSERV
;
4421 if( gGAIPOSIXFlag_Passive
) hints
.ai_flags
|= AI_PASSIVE
;
4422 if( gGAIPOSIXFlag_V4Mapped
) hints
.ai_flags
|= AI_V4MAPPED
;
4423 #if( defined( AI_V4MAPPED_CFG ) )
4424 if( gGAIPOSIXFlag_V4MappedCFG
) hints
.ai_flags
|= AI_V4MAPPED_CFG
;
4426 #if( defined( AI_DEFAULT ) )
4427 if( gGAIPOSIXFlag_Default
) hints
.ai_flags
|= AI_DEFAULT
;
4429 #if( defined( AI_UNUSABLE ) )
4430 if( gGAIPOSIXFlag_Unusable
) hints
.ai_flags
|= AI_UNUSABLE
;
4435 FPrintF( stdout
, "Hostname: %s\n", gGAIPOSIX_HostName
);
4436 FPrintF( stdout
, "Servname: %s\n", gGAIPOSIX_ServName
);
4437 FPrintF( stdout
, "Address family: %s\n", AddressFamilyStr( hints
.ai_family
) );
4438 FPrintF( stdout
, "Flags: 0x%X < ", hints
.ai_flags
);
4439 for( pair
= kGAIPOSIXFlagStringPairs
; pair
->str
!= NULL
; ++pair
)
4441 if( ( (unsigned int) hints
.ai_flags
) & pair
->flag
) FPrintF( stdout
, "%s ", pair
->str
);
4443 FPrintF( stdout
, ">\n" );
4444 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4445 FPrintF( stdout
, "---\n" );
4447 // Call getaddrinfo().
4449 err
= getaddrinfo( gGAIPOSIX_HostName
, gGAIPOSIX_ServName
, &hints
, &addrInfoList
);
4450 gettimeofday( &now
, NULL
);
4453 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
4459 for( addrInfo
= addrInfoList
; addrInfo
; addrInfo
= addrInfo
->ai_next
) { ++addrCount
; }
4461 FPrintF( stdout
, "Addresses (%d total):\n", addrCount
);
4462 for( addrInfo
= addrInfoList
; addrInfo
; addrInfo
= addrInfo
->ai_next
)
4464 FPrintF( stdout
, "%##a\n", addrInfo
->ai_addr
);
4467 FPrintF( stdout
, "---\n" );
4468 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
4471 if( addrInfoList
) freeaddrinfo( addrInfoList
);
4472 if( err
) exit( 1 );
4475 //===========================================================================================================================
4477 //===========================================================================================================================
4479 #define kIP6ARPADomainStr "ip6.arpa."
4481 static void ReverseLookupCmd( void )
4484 QueryRecordContext
* context
= NULL
;
4485 DNSServiceRef sdRef
;
4486 dispatch_source_t signalSource
= NULL
;
4488 uint8_t ipv6Addr
[ 16 ];
4489 char recordName
[ ( 16 * 4 ) + sizeof( kIP6ARPADomainStr
) ];
4490 int useMainConnection
;
4491 const char * endPtr
;
4493 // Set up SIGINT handler.
4495 signal( SIGINT
, SIG_IGN
);
4496 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
4497 require_noerr( err
, exit
);
4498 dispatch_resume( signalSource
);
4502 context
= (QueryRecordContext
*) calloc( 1, sizeof( *context
) );
4503 require_action( context
, exit
, err
= kNoMemoryErr
);
4505 // Check command parameters.
4507 if( gReverseLookup_TimeLimitSecs
< 0 )
4509 FPrintF( stderr
, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs
);
4514 // Create main connection.
4516 if( gConnectionOpt
)
4518 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
4519 require_noerr_quiet( err
, exit
);
4520 useMainConnection
= true;
4524 useMainConnection
= false;
4529 context
->flags
= GetDNSSDFlagsFromOpts();
4530 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
4532 // Get interface index.
4534 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
4535 require_noerr_quiet( err
, exit
);
4537 // Create reverse lookup record name.
4539 err
= StringToIPv4Address( gReverseLookup_IPAddr
, kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
,
4540 &ipv4Addr
, NULL
, NULL
, NULL
, &endPtr
);
4541 if( err
|| ( *endPtr
!= '\0' ) )
4546 err
= StringToIPv6Address( gReverseLookup_IPAddr
,
4547 kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
| kStringToIPAddressFlagsNoScope
,
4548 ipv6Addr
, NULL
, NULL
, NULL
, &endPtr
);
4549 if( err
|| ( *endPtr
!= '\0' ) )
4551 FPrintF( stderr
, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr
);
4556 for( i
= 15; i
>= 0; --i
)
4558 *dst
++ = kHexDigitsLowercase
[ ipv6Addr
[ i
] & 0x0F ];
4560 *dst
++ = kHexDigitsLowercase
[ ipv6Addr
[ i
] >> 4 ];
4563 strcpy_literal( dst
, kIP6ARPADomainStr
);
4564 check( ( strlen( recordName
) + 1 ) <= sizeof( recordName
) );
4568 SNPrintF( recordName
, sizeof( recordName
), "%u.%u.%u.%u.in-addr.arpa.",
4570 ( ipv4Addr
>> 8 ) & 0xFF,
4571 ( ipv4Addr
>> 16 ) & 0xFF,
4572 ( ipv4Addr
>> 24 ) & 0xFF );
4575 // Set remaining parameters.
4577 context
->recordName
= recordName
;
4578 context
->recordType
= kDNSServiceType_PTR
;
4579 context
->timeLimitSecs
= gReverseLookup_TimeLimitSecs
;
4580 context
->oneShotMode
= gReverseLookup_OneShot
? true : false;
4584 QueryRecordPrintPrologue( context
);
4588 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
4589 err
= DNSServiceQueryRecord( &sdRef
, context
->flags
, context
->ifIndex
, context
->recordName
, context
->recordType
,
4590 kDNSServiceClass_IN
, QueryRecordCallback
, context
);
4591 require_noerr( err
, exit
);
4593 context
->opRef
= sdRef
;
4594 if( !useMainConnection
)
4596 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
4597 require_noerr( err
, exit
);
4602 if( context
->timeLimitSecs
> 0 )
4604 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
4605 kExitReason_TimeLimit
, Exit
);
4610 dispatch_source_forget( &signalSource
);
4611 if( context
) QueryRecordContextFree( context
);
4612 if( err
) exit( 1 );
4615 //===========================================================================================================================
4617 //===========================================================================================================================
4621 DNSServiceRef mainRef
; // Main sdRef for shared connection.
4622 DNSServiceRef opRef
; // sdRef for the DNSServiceNATPortMappingCreate operation.
4623 DNSServiceFlags flags
; // Flags for DNSServiceNATPortMappingCreate operation.
4624 uint32_t ifIndex
; // Interface index argument for DNSServiceNATPortMappingCreate operation.
4625 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceNATPortMappingCreate operation.
4626 uint32_t ttl
; // TTL argument for DNSServiceNATPortMappingCreate operation.
4627 uint16_t internalPort
; // Internal port argument for DNSServiceNATPortMappingCreate operation.
4628 uint16_t externalPort
; // External port argument for DNSServiceNATPortMappingCreate operation.
4629 Boolean printedHeader
; // True if results header was printed.
4631 } PortMappingContext
;
4633 static void PortMappingPrintPrologue( const PortMappingContext
*inContext
);
4634 static void PortMappingContextFree( PortMappingContext
*inContext
);
4635 static void DNSSD_API
4636 PortMappingCallback(
4637 DNSServiceRef inSDRef
,
4638 DNSServiceFlags inFlags
,
4639 uint32_t inInterfaceIndex
,
4640 DNSServiceErrorType inError
,
4641 uint32_t inExternalIPv4Address
,
4642 DNSServiceProtocol inProtocol
,
4643 uint16_t inInternalPort
,
4644 uint16_t inExternalPort
,
4648 static void PortMappingCmd( void )
4651 PortMappingContext
* context
= NULL
;
4652 DNSServiceRef sdRef
;
4653 dispatch_source_t signalSource
= NULL
;
4654 int useMainConnection
;
4656 // Set up SIGINT handler.
4658 signal( SIGINT
, SIG_IGN
);
4659 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
4660 require_noerr( err
, exit
);
4661 dispatch_resume( signalSource
);
4665 context
= (PortMappingContext
*) calloc( 1, sizeof( *context
) );
4666 require_action( context
, exit
, err
= kNoMemoryErr
);
4668 // Check command parameters.
4670 if( ( gPortMapping_InternalPort
< 0 ) || ( gPortMapping_InternalPort
> UINT16_MAX
) )
4672 FPrintF( stderr
, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort
);
4677 if( ( gPortMapping_ExternalPort
< 0 ) || ( gPortMapping_ExternalPort
> UINT16_MAX
) )
4679 FPrintF( stderr
, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort
);
4684 // Create main connection.
4686 if( gConnectionOpt
)
4688 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
4689 require_noerr_quiet( err
, exit
);
4690 useMainConnection
= true;
4694 useMainConnection
= false;
4699 context
->flags
= GetDNSSDFlagsFromOpts();
4700 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
4702 // Get interface index.
4704 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
4705 require_noerr_quiet( err
, exit
);
4707 // Set remaining parameters.
4709 if( gPortMapping_ProtocolTCP
) context
->protocols
|= kDNSServiceProtocol_TCP
;
4710 if( gPortMapping_ProtocolUDP
) context
->protocols
|= kDNSServiceProtocol_UDP
;
4711 context
->ttl
= (uint32_t) gPortMapping_TTL
;
4712 context
->internalPort
= (uint16_t) gPortMapping_InternalPort
;
4713 context
->externalPort
= (uint16_t) gPortMapping_ExternalPort
;
4717 PortMappingPrintPrologue( context
);
4721 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
4722 err
= DNSServiceNATPortMappingCreate( &sdRef
, context
->flags
, context
->ifIndex
, context
->protocols
,
4723 htons( context
->internalPort
), htons( context
->externalPort
), context
->ttl
, PortMappingCallback
, context
);
4724 require_noerr( err
, exit
);
4726 context
->opRef
= sdRef
;
4727 if( !useMainConnection
)
4729 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
4730 require_noerr( err
, exit
);
4736 dispatch_source_forget( &signalSource
);
4737 if( context
) PortMappingContextFree( context
);
4738 if( err
) exit( 1 );
4741 //===========================================================================================================================
4742 // PortMappingPrintPrologue
4743 //===========================================================================================================================
4745 static void PortMappingPrintPrologue( const PortMappingContext
*inContext
)
4747 char ifName
[ kInterfaceNameBufLen
];
4749 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
4751 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
4752 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
4753 FPrintF( stdout
, "Protocols: %#{flags}\n", inContext
->protocols
, kDNSServiceProtocolDescriptors
);
4754 FPrintF( stdout
, "Internal Port: %u\n", inContext
->internalPort
);
4755 FPrintF( stdout
, "External Port: %u\n", inContext
->externalPort
);
4756 FPrintF( stdout
, "TTL: %u%?s\n", inContext
->ttl
, !inContext
->ttl
,
4757 " (system will use a default value.)" );
4758 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4759 FPrintF( stdout
, "---\n" );
4763 //===========================================================================================================================
4764 // PortMappingContextFree
4765 //===========================================================================================================================
4767 static void PortMappingContextFree( PortMappingContext
*inContext
)
4769 DNSServiceForget( &inContext
->opRef
);
4770 DNSServiceForget( &inContext
->mainRef
);
4774 //===========================================================================================================================
4775 // PortMappingCallback
4776 //===========================================================================================================================
4778 static void DNSSD_API
4779 PortMappingCallback(
4780 DNSServiceRef inSDRef
,
4781 DNSServiceFlags inFlags
,
4782 uint32_t inInterfaceIndex
,
4783 DNSServiceErrorType inError
,
4784 uint32_t inExternalIPv4Address
,
4785 DNSServiceProtocol inProtocol
,
4786 uint16_t inInternalPort
,
4787 uint16_t inExternalPort
,
4791 PortMappingContext
* const context
= (PortMappingContext
*) inContext
;
4793 char errorStr
[ 128 ];
4798 gettimeofday( &now
, NULL
);
4800 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " (error: %#m)", inError
);
4801 if( !context
->printedHeader
)
4803 FPrintF( stdout
, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" );
4804 context
->printedHeader
= true;
4806 FPrintF( stdout
, "%{du:time} %2u %7u %15.4a %7u %6u %#{flags}%?s\n",
4807 &now
, inInterfaceIndex
, ntohs( inInternalPort
), &inExternalIPv4Address
, ntohs( inExternalPort
), inTTL
,
4808 inProtocol
, kDNSServiceProtocolDescriptors
, inError
, errorStr
);
4811 //===========================================================================================================================
4813 //===========================================================================================================================
4815 typedef struct BrowseAllConnection BrowseAllConnection
;
4819 ServiceBrowserRef browser
; // Service browser.
4820 ServiceBrowserResults
* results
; // Results from the service browser.
4821 BrowseAllConnection
* connectionList
; // List of connections.
4822 dispatch_source_t connectionTimer
; // Timer for connection timeout.
4823 int connectionPendingCount
; // Number of pending connections.
4824 int connectionTimeoutSecs
; // Timeout value for connections in seconds.
4828 struct BrowseAllConnection
4830 BrowseAllConnection
* next
; // Next connection object in list.
4831 sockaddr_ip sip
; // IPv4 or IPv6 address to connect to.
4832 uint16_t port
; // TCP port to connect to.
4833 AsyncConnectionRef asyncCnx
; // AsyncConnection object to handle the actual connection.
4834 OSStatus status
; // Status of connection. NoErr means connection succeeded.
4835 CFTimeInterval connectTimeSecs
; // Time it took to connect in seconds.
4836 int32_t refCount
; // This object's reference count.
4837 BrowseAllContext
* context
; // Back pointer to parent context.
4840 static void _BrowseAllContextFree( BrowseAllContext
*inContext
);
4841 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
);
4843 _BrowseAllConnectionCreate(
4844 const struct sockaddr
* inSockAddr
,
4846 BrowseAllContext
* inContext
,
4847 BrowseAllConnection
** outConnection
);
4848 static void _BrowseAllConnectionRetain( BrowseAllConnection
*inConnection
);
4849 static void _BrowseAllConnectionRelease( BrowseAllConnection
*inConnection
);
4850 static void _BrowseAllConnectionProgress( int inPhase
, const void *inDetails
, void *inArg
);
4851 static void _BrowseAllConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
);
4852 static void _BrowseAllExit( void *inContext
);
4854 static Boolean
_IsServiceTypeTCP( const char *inServiceType
);
4856 static void BrowseAllCmd( void )
4859 BrowseAllContext
* context
= NULL
;
4862 char ifName
[ kInterfaceNameBufLen
];
4864 // Check parameters.
4866 if( gBrowseAll_BrowseTimeSecs
<= 0 )
4868 FPrintF( stdout
, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs
);
4873 context
= (BrowseAllContext
*) calloc( 1, sizeof( *context
) );
4874 require_action( context
, exit
, err
= kNoMemoryErr
);
4876 context
->connectionTimeoutSecs
= gBrowseAll_ConnectTimeout
;
4877 #if( TARGET_OS_POSIX )
4878 // Increase the open file descriptor limit for connection sockets.
4880 if( context
->connectionTimeoutSecs
> 0 )
4882 struct rlimit fdLimits
;
4884 err
= getrlimit( RLIMIT_NOFILE
, &fdLimits
);
4885 err
= map_global_noerr_errno( err
);
4886 require_noerr( err
, exit
);
4888 if( fdLimits
.rlim_cur
< 4096 )
4890 fdLimits
.rlim_cur
= 4096;
4891 err
= setrlimit( RLIMIT_NOFILE
, &fdLimits
);
4892 err
= map_global_noerr_errno( err
);
4893 require_noerr( err
, exit
);
4898 // Get interface index.
4900 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
4901 require_noerr_quiet( err
, exit
);
4905 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
4906 FPrintF( stdout
, "Service types: ");
4907 if( gBrowseAll_ServiceTypesCount
> 0 )
4909 FPrintF( stdout
, "%s", gBrowseAll_ServiceTypes
[ 0 ] );
4910 for( i
= 1; i
< gBrowseAll_ServiceTypesCount
; ++i
)
4912 FPrintF( stdout
, ", %s", gBrowseAll_ServiceTypes
[ i
] );
4914 FPrintF( stdout
, "\n" );
4918 FPrintF( stdout
, "all services\n" );
4920 FPrintF( stdout
, "Domain: %s\n", gBrowseAll_Domain
? gBrowseAll_Domain
: "default domains" );
4921 FPrintF( stdout
, "Browse time: %d second%?c\n", gBrowseAll_BrowseTimeSecs
, gBrowseAll_BrowseTimeSecs
!= 1, 's' );
4922 FPrintF( stdout
, "Connect timeout: %d second%?c\n",
4923 context
->connectionTimeoutSecs
, context
->connectionTimeoutSecs
!= 1, 's' );
4924 FPrintF( stdout
, "IncludeAWDL: %s\n", gDNSSDFlag_IncludeAWDL
? "yes" : "no" );
4925 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4926 FPrintF( stdout
, "---\n" );
4928 err
= ServiceBrowserCreate( dispatch_get_main_queue(), ifIndex
, gBrowseAll_Domain
,
4929 (unsigned int) gBrowseAll_BrowseTimeSecs
, gDNSSDFlag_IncludeAWDL
? true : false, &context
->browser
);
4930 require_noerr( err
, exit
);
4932 for( i
= 0; i
< gBrowseAll_ServiceTypesCount
; ++i
)
4934 err
= ServiceBrowserAddServiceType( context
->browser
, gBrowseAll_ServiceTypes
[ i
] );
4935 require_noerr( err
, exit
);
4937 ServiceBrowserSetCallback( context
->browser
, _BrowseAllServiceBrowserCallback
, context
);
4938 ServiceBrowserStart( context
->browser
);
4942 if( context
) _BrowseAllContextFree( context
);
4943 if( err
) exit( 1 );
4946 //===========================================================================================================================
4947 // _BrowseAllContextFree
4948 //===========================================================================================================================
4950 static void _BrowseAllContextFree( BrowseAllContext
*inContext
)
4952 check( !inContext
->browser
);
4953 check( !inContext
->connectionTimer
);
4954 check( !inContext
->connectionList
);
4955 ForgetServiceBrowserResults( &inContext
->results
);
4959 //===========================================================================================================================
4960 // _BrowseAllServiceBrowserCallback
4961 //===========================================================================================================================
4963 #define kDiscardProtocolPort 9
4965 static void _BrowseAllServiceBrowserCallback( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
)
4968 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
4970 SBRServiceType
* type
;
4971 SBRServiceInstance
* instance
;
4972 SBRIPAddress
* ipaddr
;
4976 require_action( inResults
, exit
, err
= kUnexpectedErr
);
4978 check( !context
->results
);
4979 context
->results
= inResults
;
4980 ServiceBrowserResultsRetain( context
->results
);
4982 check( context
->connectionPendingCount
== 0 );
4983 if( context
->connectionTimeoutSecs
> 0 )
4985 BrowseAllConnection
* connection
;
4986 BrowseAllConnection
** connectionPtr
= &context
->connectionList
;
4987 char destination
[ kSockAddrStringMaxSize
];
4989 for( domain
= context
->results
->domainList
; domain
; domain
= domain
->next
)
4991 for( type
= domain
->typeList
; type
; type
= type
->next
)
4993 if( !_IsServiceTypeTCP( type
->name
) ) continue;
4994 for( instance
= type
->instanceList
; instance
; instance
= instance
->next
)
4996 if( instance
->port
== kDiscardProtocolPort
) continue;
4997 for( ipaddr
= instance
->ipaddrList
; ipaddr
; ipaddr
= ipaddr
->next
)
4999 err
= _BrowseAllConnectionCreate( &ipaddr
->sip
.sa
, instance
->port
, context
, &connection
);
5000 require_noerr( err
, exit
);
5002 *connectionPtr
= connection
;
5003 connectionPtr
= &connection
->next
;
5005 err
= SockAddrToString( &ipaddr
->sip
, kSockAddrStringFlagsNoPort
, destination
);
5009 err
= AsyncConnection_Connect( &connection
->asyncCnx
, destination
, -instance
->port
,
5010 kAsyncConnectionFlag_P2P
, kAsyncConnectionNoTimeout
,
5011 kSocketBufferSize_DontSet
, kSocketBufferSize_DontSet
,
5012 _BrowseAllConnectionProgress
, connection
, _BrowseAllConnectionHandler
, connection
,
5013 dispatch_get_main_queue() );
5018 _BrowseAllConnectionRetain( connection
);
5019 connection
->status
= kInProgressErr
;
5020 ++context
->connectionPendingCount
;
5024 connection
->status
= err
;
5032 if( context
->connectionPendingCount
> 0 )
5034 check( !context
->connectionTimer
);
5035 err
= DispatchTimerCreate( dispatch_time_seconds( context
->connectionTimeoutSecs
), DISPATCH_TIME_FOREVER
,
5036 100 * kNanosecondsPerMillisecond
, NULL
, _BrowseAllExit
, NULL
, context
, &context
->connectionTimer
);
5037 require_noerr( err
, exit
);
5038 dispatch_resume( context
->connectionTimer
);
5042 dispatch_async_f( dispatch_get_main_queue(), context
, _BrowseAllExit
);
5047 ForgetCF( &context
->browser
);
5048 if( err
) exit( 1 );
5051 //===========================================================================================================================
5052 // _BrowseAllConnectionCreate
5053 //===========================================================================================================================
5056 _BrowseAllConnectionCreate(
5057 const struct sockaddr
* inSockAddr
,
5059 BrowseAllContext
* inContext
,
5060 BrowseAllConnection
** outConnection
)
5063 BrowseAllConnection
* obj
;
5065 obj
= (BrowseAllConnection
*) calloc( 1, sizeof( *obj
) );
5066 require_action( obj
, exit
, err
= kNoMemoryErr
);
5069 SockAddrCopy( inSockAddr
, &obj
->sip
);
5071 obj
->context
= inContext
;
5073 *outConnection
= obj
;
5080 //===========================================================================================================================
5081 // _BrowseAllConnectionRetain
5082 //===========================================================================================================================
5084 static void _BrowseAllConnectionRetain( BrowseAllConnection
*inConnection
)
5086 ++inConnection
->refCount
;
5089 //===========================================================================================================================
5090 // _BrowseAllConnectionRelease
5091 //===========================================================================================================================
5093 static void _BrowseAllConnectionRelease( BrowseAllConnection
*inConnection
)
5095 if( --inConnection
->refCount
== 0 ) free( inConnection
);
5098 //===========================================================================================================================
5099 // _BrowseAllConnectionProgress
5100 //===========================================================================================================================
5102 static void _BrowseAllConnectionProgress( int inPhase
, const void *inDetails
, void *inArg
)
5104 BrowseAllConnection
* const connection
= (BrowseAllConnection
*) inArg
;
5106 if( inPhase
== kAsyncConnectionPhase_Connected
)
5108 const AsyncConnectedInfo
* const info
= (AsyncConnectedInfo
*) inDetails
;
5110 connection
->connectTimeSecs
= info
->connectSecs
;
5114 //===========================================================================================================================
5115 // _BrowseAllConnectionHandler
5116 //===========================================================================================================================
5118 static void _BrowseAllConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
)
5120 BrowseAllConnection
* const connection
= (BrowseAllConnection
*) inArg
;
5121 BrowseAllContext
* const context
= connection
->context
;
5123 connection
->status
= inError
;
5124 ForgetSocket( &inSock
);
5127 check( context
->connectionPendingCount
> 0 );
5128 if( ( --context
->connectionPendingCount
== 0 ) && context
->connectionTimer
)
5130 dispatch_source_forget( &context
->connectionTimer
);
5131 dispatch_async_f( dispatch_get_main_queue(), context
, _BrowseAllExit
);
5134 _BrowseAllConnectionRelease( connection
);
5137 //===========================================================================================================================
5139 //===========================================================================================================================
5141 #define Indent( X ) ( (X) * 4 ), ""
5143 static void _BrowseAllExit( void *inContext
)
5145 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
5147 SBRServiceType
* type
;
5148 SBRServiceInstance
* instance
;
5149 SBRIPAddress
* ipaddr
;
5150 char textBuf
[ 512 ];
5151 #if( TARGET_OS_POSIX )
5152 const Boolean useColor
= isatty( STDOUT_FILENO
) ? true : false;
5155 dispatch_source_forget( &context
->connectionTimer
);
5157 for( domain
= context
->results
->domainList
; domain
; domain
= domain
->next
)
5159 FPrintF( stdout
, "%s\n\n", domain
->name
);
5161 for( type
= domain
->typeList
; type
; type
= type
->next
)
5163 const char * description
;
5164 const Boolean serviceTypeIsTCP
= _IsServiceTypeTCP( type
->name
);
5166 description
= ServiceTypeDescription( type
->name
);
5167 if( description
) FPrintF( stdout
, "%*s" "%s (%s)\n\n", Indent( 1 ), description
, type
->name
);
5168 else FPrintF( stdout
, "%*s" "%s\n\n", Indent( 1 ), type
->name
);
5170 for( instance
= type
->instanceList
; instance
; instance
= instance
->next
)
5172 char * dst
= textBuf
;
5173 char * const lim
= &textBuf
[ countof( textBuf
) ];
5174 char ifname
[ IF_NAMESIZE
+ 1 ];
5176 SNPrintF_Add( &dst
, lim
, "%s via ", instance
->name
);
5177 if( instance
->ifIndex
== 0 )
5179 SNPrintF_Add( &dst
, lim
, "the Internet" );
5181 else if( if_indextoname( instance
->ifIndex
, ifname
) )
5183 NetTransportType netType
;
5185 SocketGetInterfaceInfo( kInvalidSocketRef
, ifname
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &netType
);
5186 SNPrintF_Add( &dst
, lim
, "%s (%s)",
5187 ( netType
== kNetTransportType_Ethernet
) ? "Ethernet" : NetTransportTypeToString( netType
),
5192 SNPrintF_Add( &dst
, lim
, "interface index %u", instance
->ifIndex
);
5194 FPrintF( stdout
, "%*s" "%-55s %4llu.%03llu ms\n\n",
5195 Indent( 2 ), textBuf
, instance
->discoverTimeUs
/ 1000, instance
->discoverTimeUs
% 1000 );
5197 if( instance
->hostname
)
5199 SNPrintF( textBuf
, sizeof( textBuf
), "%s:%u", instance
->hostname
, instance
->port
);
5200 FPrintF( stdout
, "%*s" "%-51s %4llu.%03llu ms\n",
5201 Indent( 3 ), textBuf
, instance
->resolveTimeUs
/ 1000, instance
->resolveTimeUs
% 1000 );
5205 FPrintF( stdout
, "%*s" "%s:%u\n", Indent( 3 ), instance
->hostname
, instance
->port
);
5208 for( ipaddr
= instance
->ipaddrList
; ipaddr
; ipaddr
= ipaddr
->next
)
5210 BrowseAllConnection
* conn
;
5211 BrowseAllConnection
** connPtr
;
5213 FPrintF( stdout
, "%*s" "%-##47a %4llu.%03llu ms",
5214 Indent( 4 ), &ipaddr
->sip
.sa
, ipaddr
->resolveTimeUs
/ 1000, ipaddr
->resolveTimeUs
% 1000 );
5217 if( serviceTypeIsTCP
&& ( instance
->port
!= kDiscardProtocolPort
) )
5219 for( connPtr
= &context
->connectionList
; ( conn
= *connPtr
) != NULL
; connPtr
= &conn
->next
)
5221 if( ( conn
->port
== instance
->port
) &&
5222 ( SockAddrCompareAddr( &conn
->sip
, &ipaddr
->sip
) == 0 ) ) break;
5226 if( conn
->status
== kInProgressErr
) conn
->status
= kTimeoutErr
;
5227 *connPtr
= conn
->next
;
5228 conn
->context
= NULL
;
5229 AsyncConnection_Forget( &conn
->asyncCnx
);
5235 if( conn
->status
== kNoErr
)
5237 FPrintF( stdout
, " (%sconnected%s in %.3f ms)\n",
5238 useColor
? kANSIGreen
: "", useColor
? kANSINormal
: "", conn
->connectTimeSecs
* 1000 );
5242 FPrintF( stdout
, " (%scould not connect%s: %m)\n",
5243 useColor
? kANSIRed
: "", useColor
? kANSINormal
: "", conn
->status
);
5245 _BrowseAllConnectionRelease( conn
);
5249 FPrintF( stdout
, " (no connection attempted)\n" );
5253 FPrintF( stdout
, "\n" );
5254 if( instance
->txtLen
== 0 ) continue;
5256 FPrintF( stdout
, "%*s" "TXT record (%zu byte%?c):\n",
5257 Indent( 3 ), instance
->txtLen
, instance
->txtLen
!= 1, 's' );
5258 if( instance
->txtLen
> 1 )
5260 FPrintF( stdout
, "%3{txt}", instance
->txtPtr
, instance
->txtLen
);
5264 FPrintF( stdout
, "%*s" "%#H\n", Indent( 3 ), instance
->txtPtr
, (int) instance
->txtLen
, INT_MAX
);
5266 FPrintF( stdout
, "\n" );
5268 FPrintF( stdout
, "\n" );
5272 _BrowseAllContextFree( context
);
5276 //===========================================================================================================================
5277 // _IsServiceTypeTCP
5278 //===========================================================================================================================
5280 static Boolean
_IsServiceTypeTCP( const char *inServiceType
)
5283 const uint8_t * secondLabel
;
5284 uint8_t name
[ kDomainNameLengthMax
];
5286 err
= DomainNameFromString( name
, inServiceType
, NULL
);
5289 secondLabel
= NextLabel( name
);
5290 if( secondLabel
&& DomainNameEqual( secondLabel
, (const uint8_t *) "\x04" "_tcp" ) ) return( true );
5295 //===========================================================================================================================
5297 //===========================================================================================================================
5299 const FlagStringPair kGetNameInfoFlagStringPairs
[] =
5301 CaseFlagStringify( NI_NUMERICSCOPE
),
5302 CaseFlagStringify( NI_DGRAM
),
5303 CaseFlagStringify( NI_NUMERICSERV
),
5304 CaseFlagStringify( NI_NAMEREQD
),
5305 CaseFlagStringify( NI_NUMERICHOST
),
5306 CaseFlagStringify( NI_NOFQDN
),
5310 static void GetNameInfoCmd( void )
5316 const FlagStringPair
* pair
;
5318 char host
[ NI_MAXHOST
];
5319 char serv
[ NI_MAXSERV
];
5321 err
= StringToSockAddr( gGetNameInfo_IPAddress
, &sip
, sizeof( sip
), &sockAddrLen
);
5325 FPrintF( stderr
, "Failed to convert \"%s\" to a sockaddr.\n", gGetNameInfo_IPAddress
);
5330 if( gGetNameInfoFlag_DGram
) flags
|= NI_DGRAM
;
5331 if( gGetNameInfoFlag_NameReqd
) flags
|= NI_NAMEREQD
;
5332 if( gGetNameInfoFlag_NoFQDN
) flags
|= NI_NOFQDN
;
5333 if( gGetNameInfoFlag_NumericHost
) flags
|= NI_NUMERICHOST
;
5334 if( gGetNameInfoFlag_NumericScope
) flags
|= NI_NUMERICSCOPE
;
5335 if( gGetNameInfoFlag_NumericServ
) flags
|= NI_NUMERICSERV
;
5339 FPrintF( stdout
, "SockAddr: %##a\n", &sip
.sa
);
5340 FPrintF( stdout
, "Flags: 0x%X < ", flags
);
5341 for( pair
= kGetNameInfoFlagStringPairs
; pair
->str
!= NULL
; ++pair
)
5343 if( flags
& pair
->flag
) FPrintF( stdout
, "%s ", pair
->str
);
5345 FPrintF( stdout
, ">\n" );
5346 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5347 FPrintF( stdout
, "---\n" );
5349 // Call getnameinfo().
5351 err
= getnameinfo( &sip
.sa
, (socklen_t
) sockAddrLen
, host
, (socklen_t
) sizeof( host
), serv
, (socklen_t
) sizeof( serv
),
5353 gettimeofday( &now
, NULL
);
5356 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
5360 FPrintF( stdout
, "host: %s\n", host
);
5361 FPrintF( stdout
, "serv: %s\n", serv
);
5363 FPrintF( stdout
, "---\n" );
5364 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
5367 gExitCode
= err
? 1 : 0;
5370 //===========================================================================================================================
5371 // GetAddrInfoStressCmd
5372 //===========================================================================================================================
5376 DNSServiceRef mainRef
;
5377 DNSServiceRef sdRef
;
5378 DNSServiceFlags flags
;
5379 unsigned int interfaceIndex
;
5380 unsigned int connectionNumber
;
5381 unsigned int requestCount
;
5382 unsigned int requestCountMax
;
5383 unsigned int requestCountLimit
;
5384 unsigned int durationMinMs
;
5385 unsigned int durationMaxMs
;
5389 static void GetAddrInfoStressEvent( void *inContext
);
5390 static void DNSSD_API
5391 GetAddrInfoStressCallback(
5392 DNSServiceRef inSDRef
,
5393 DNSServiceFlags inFlags
,
5394 uint32_t inInterfaceIndex
,
5395 DNSServiceErrorType inError
,
5396 const char * inHostname
,
5397 const struct sockaddr
* inSockAddr
,
5401 static void GetAddrInfoStressCmd( void )
5404 GAIStressContext
* context
= NULL
;
5406 DNSServiceFlags flags
;
5408 char ifName
[ kInterfaceNameBufLen
];
5410 if( gGAIStress_TestDurationSecs
< 0 )
5412 FPrintF( stdout
, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs
);
5416 if( gGAIStress_ConnectionCount
<= 0 )
5418 FPrintF( stdout
, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount
);
5422 if( gGAIStress_DurationMinMs
<= 0 )
5424 FPrintF( stdout
, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs
);
5428 if( gGAIStress_DurationMaxMs
<= 0 )
5430 FPrintF( stdout
, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs
);
5434 if( gGAIStress_DurationMinMs
> gGAIStress_DurationMaxMs
)
5436 FPrintF( stdout
, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
5437 gGAIStress_DurationMinMs
, gGAIStress_DurationMaxMs
);
5441 if( gGAIStress_RequestCountMax
<= 0 )
5443 FPrintF( stdout
, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax
);
5450 flags
= GetDNSSDFlagsFromOpts();
5452 // Set interface index.
5454 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
5455 require_noerr_quiet( err
, exit
);
5457 for( i
= 0; i
< gGAIStress_ConnectionCount
; ++i
)
5459 context
= (GAIStressContext
*) calloc( 1, sizeof( *context
) );
5460 require_action( context
, exit
, err
= kNoMemoryErr
);
5462 context
->flags
= flags
;
5463 context
->interfaceIndex
= ifIndex
;
5464 context
->connectionNumber
= (unsigned int)( i
+ 1 );
5465 context
->requestCountMax
= (unsigned int) gGAIStress_RequestCountMax
;
5466 context
->durationMinMs
= (unsigned int) gGAIStress_DurationMinMs
;
5467 context
->durationMaxMs
= (unsigned int) gGAIStress_DurationMaxMs
;
5469 dispatch_async_f( dispatch_get_main_queue(), context
, GetAddrInfoStressEvent
);
5473 if( gGAIStress_TestDurationSecs
> 0 )
5475 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs
), dispatch_get_main_queue(), NULL
, Exit
);
5478 FPrintF( stdout
, "Flags: %#{flags}\n", flags
, kDNSServiceFlagsDescriptors
);
5479 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
5480 FPrintF( stdout
, "Test duration: " );
5481 if( gGAIStress_TestDurationSecs
== 0 )
5483 FPrintF( stdout
, "∞\n" );
5487 FPrintF( stdout
, "%d s\n", gGAIStress_TestDurationSecs
);
5489 FPrintF( stdout
, "Connection count: %d\n", gGAIStress_ConnectionCount
);
5490 FPrintF( stdout
, "Request duration min: %d ms\n", gGAIStress_DurationMinMs
);
5491 FPrintF( stdout
, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs
);
5492 FPrintF( stdout
, "Request count max: %d\n", gGAIStress_RequestCountMax
);
5493 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5494 FPrintF( stdout
, "---\n" );
5499 FreeNullSafe( context
);
5500 if( err
) exit( 1 );
5503 //===========================================================================================================================
5504 // GetAddrInfoStressEvent
5505 //===========================================================================================================================
5507 #define kStressRandStrLen 5
5509 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
5511 static void GetAddrInfoStressEvent( void *inContext
)
5513 GAIStressContext
* const context
= (GAIStressContext
*) inContext
;
5515 DNSServiceRef sdRef
;
5516 unsigned int nextMs
;
5517 char randomStr
[ kStressRandStrLen
+ 1 ];
5518 char hostname
[ kStressRandStrLen
+ 4 + 1 ];
5519 Boolean isConnectionNew
= false;
5520 static Boolean printedHeader
= false;
5522 if( !context
->mainRef
|| ( context
->requestCount
>= context
->requestCountLimit
) )
5524 DNSServiceForget( &context
->mainRef
);
5525 context
->sdRef
= NULL
;
5526 context
->requestCount
= 0;
5527 context
->requestCountLimit
= RandomRange( 1, context
->requestCountMax
);
5529 err
= DNSServiceCreateConnection( &context
->mainRef
);
5530 require_noerr( err
, exit
);
5532 err
= DNSServiceSetDispatchQueue( context
->mainRef
, dispatch_get_main_queue() );
5533 require_noerr( err
, exit
);
5535 isConnectionNew
= true;
5538 RandomString( kLowercaseAlphaCharSet
, sizeof_string( kLowercaseAlphaCharSet
), 2, kStressRandStrLen
, randomStr
);
5539 SNPrintF( hostname
, sizeof( hostname
), "%s.com", randomStr
);
5541 nextMs
= RandomRange( context
->durationMinMs
, context
->durationMaxMs
);
5543 if( !printedHeader
)
5545 FPrintF( stdout
, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
5546 printedHeader
= true;
5548 FPrintF( stdout
, "%{du:time} %3u%c %9s %8u\n",
5549 NULL
, context
->connectionNumber
, isConnectionNew
? '*': ' ', hostname
, nextMs
);
5551 DNSServiceForget( &context
->sdRef
);
5552 sdRef
= context
->mainRef
;
5553 err
= DNSServiceGetAddrInfo( &sdRef
, context
->flags
| kDNSServiceFlagsShareConnection
, context
->interfaceIndex
,
5554 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, hostname
, GetAddrInfoStressCallback
, NULL
);
5555 require_noerr( err
, exit
);
5556 context
->sdRef
= sdRef
;
5558 context
->requestCount
++;
5560 dispatch_after_f( dispatch_time_milliseconds( nextMs
), dispatch_get_main_queue(), context
, GetAddrInfoStressEvent
);
5563 if( err
) exit( 1 );
5566 //===========================================================================================================================
5567 // GetAddrInfoStressCallback
5568 //===========================================================================================================================
5570 static void DNSSD_API
5571 GetAddrInfoStressCallback(
5572 DNSServiceRef inSDRef
,
5573 DNSServiceFlags inFlags
,
5574 uint32_t inInterfaceIndex
,
5575 DNSServiceErrorType inError
,
5576 const char * inHostname
,
5577 const struct sockaddr
* inSockAddr
,
5583 Unused( inInterfaceIndex
);
5585 Unused( inHostname
);
5586 Unused( inSockAddr
);
5588 Unused( inContext
);
5591 //===========================================================================================================================
5593 //===========================================================================================================================
5597 sockaddr_ip serverAddr
;
5603 dispatch_source_t readSource
;
5610 Boolean printRawRData
; // True if RDATA results are not to be formatted.
5611 uint8_t msgBuf
[ 512 ];
5615 static void DNSQueryPrintPrologue( const DNSQueryContext
*inContext
);
5616 static void DNSQueryReadHandler( void *inContext
);
5617 static void DNSQueryCancelHandler( void *inContext
);
5619 static void DNSQueryCmd( void )
5622 DNSQueryContext
* context
= NULL
;
5624 size_t msgLen
, sendLen
;
5626 // Check command parameters.
5628 if( gDNSQuery_TimeLimitSecs
< -1 )
5630 FPrintF( stdout
, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs
);
5634 if( ( gDNSQuery_Flags
< INT16_MIN
) || ( gDNSQuery_Flags
> UINT16_MAX
) )
5636 FPrintF( stdout
, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags
);
5643 context
= (DNSQueryContext
*) calloc( 1, sizeof( *context
) );
5644 require_action( context
, exit
, err
= kNoMemoryErr
);
5646 context
->name
= gDNSQuery_Name
;
5647 context
->sock
= kInvalidSocketRef
;
5648 context
->timeLimitSecs
= gDNSQuery_TimeLimitSecs
;
5649 context
->queryID
= (uint16_t) Random32();
5650 context
->useTCP
= gDNSQuery_UseTCP
? true : false;
5651 context
->printRawRData
= gDNSQuery_RawRData
? true : false;
5653 #if( TARGET_OS_DARWIN )
5654 if( gDNSQuery_Server
)
5657 err
= StringToSockAddr( gDNSQuery_Server
, &context
->serverAddr
, sizeof( context
->serverAddr
), NULL
);
5658 require_noerr( err
, exit
);
5660 #if( TARGET_OS_DARWIN )
5663 err
= GetDefaultDNSServer( &context
->serverAddr
);
5664 require_noerr( err
, exit
);
5667 if( SockAddrGetPort( &context
->serverAddr
) == 0 ) SockAddrSetPort( &context
->serverAddr
, kDNSPort
);
5669 err
= RecordTypeFromArgString( gDNSQuery_Type
, &context
->type
);
5670 require_noerr( err
, exit
);
5672 // Write query message.
5674 check_compile_time_code( sizeof( context
->msgBuf
) >= ( kDNSQueryMessageMaxLen
+ 2 ) );
5676 msgPtr
= context
->useTCP
? &context
->msgBuf
[ 2 ] : context
->msgBuf
;
5677 err
= WriteDNSQueryMessage( msgPtr
, context
->queryID
, (uint16_t) gDNSQuery_Flags
, context
->name
, context
->type
,
5678 kDNSServiceClass_IN
, &msgLen
);
5679 require_noerr( err
, exit
);
5680 check( msgLen
<= UINT16_MAX
);
5682 if( context
->useTCP
)
5684 WriteBig16( context
->msgBuf
, msgLen
);
5685 sendLen
= 2 + msgLen
;
5692 DNSQueryPrintPrologue( context
);
5694 if( gDNSQuery_Verbose
)
5696 FPrintF( stdout
, "DNS message to send:\n\n%{du:dnsmsg}", msgPtr
, msgLen
);
5697 FPrintF( stdout
, "---\n" );
5700 if( context
->useTCP
)
5702 // Create TCP socket.
5704 context
->sock
= socket( context
->serverAddr
.sa
.sa_family
, SOCK_STREAM
, IPPROTO_TCP
);
5705 err
= map_socket_creation_errno( context
->sock
);
5706 require_noerr( err
, exit
);
5708 err
= SocketConnect( context
->sock
, &context
->serverAddr
, 5 );
5709 require_noerr( err
, exit
);
5713 // Create UDP socket.
5715 err
= UDPClientSocketOpen( AF_UNSPEC
, &context
->serverAddr
, 0, -1, NULL
, &context
->sock
);
5716 require_noerr( err
, exit
);
5719 context
->sendTicks
= UpTicks();
5720 err
= SocketWriteAll( context
->sock
, context
->msgBuf
, sendLen
, 5 );
5721 require_noerr( err
, exit
);
5723 if( context
->timeLimitSecs
== 0 ) goto exit
;
5725 err
= DispatchReadSourceCreate( context
->sock
, NULL
, DNSQueryReadHandler
, DNSQueryCancelHandler
, context
,
5726 &context
->readSource
);
5727 require_noerr( err
, exit
);
5728 dispatch_resume( context
->readSource
);
5730 if( context
->timeLimitSecs
> 0 )
5732 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
5740 dispatch_source_forget( &context
->readSource
);
5741 ForgetSocket( &context
->sock
);
5744 if( err
) exit( 1 );
5747 //===========================================================================================================================
5748 // DNSQueryPrintPrologue
5749 //===========================================================================================================================
5751 static void DNSQueryPrintPrologue( const DNSQueryContext
*inContext
)
5753 const int timeLimitSecs
= inContext
->timeLimitSecs
;
5755 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
5756 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->type
), inContext
->type
);
5757 FPrintF( stdout
, "Server: %##a\n", &inContext
->serverAddr
);
5758 FPrintF( stdout
, "Transport: %s\n", inContext
->useTCP
? "TCP" : "UDP" );
5759 FPrintF( stdout
, "Time limit: " );
5760 if( timeLimitSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
5761 else FPrintF( stdout
, "∞\n" );
5762 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5763 FPrintF( stdout
, "---\n" );
5766 //===========================================================================================================================
5767 // DNSQueryReadHandler
5768 //===========================================================================================================================
5770 static void DNSQueryReadHandler( void *inContext
)
5774 const uint64_t nowTicks
= UpTicks();
5775 DNSQueryContext
* const context
= (DNSQueryContext
*) inContext
;
5777 gettimeofday( &now
, NULL
);
5779 if( context
->useTCP
)
5781 if( !context
->haveTCPLen
)
5783 err
= SocketReadData( context
->sock
, &context
->msgBuf
, 2, &context
->msgOffset
);
5784 if( err
== EWOULDBLOCK
) { err
= kNoErr
; goto exit
; }
5785 require_noerr( err
, exit
);
5787 context
->msgOffset
= 0;
5788 context
->msgLen
= ReadBig16( context
->msgBuf
);
5789 context
->haveTCPLen
= true;
5790 if( context
->msgLen
<= sizeof( context
->msgBuf
) )
5792 context
->msgPtr
= context
->msgBuf
;
5796 context
->msgPtr
= (uint8_t *) malloc( context
->msgLen
);
5797 require_action( context
->msgPtr
, exit
, err
= kNoMemoryErr
);
5801 err
= SocketReadData( context
->sock
, context
->msgPtr
, context
->msgLen
, &context
->msgOffset
);
5802 if( err
== EWOULDBLOCK
) { err
= kNoErr
; goto exit
; }
5803 require_noerr( err
, exit
);
5804 context
->msgOffset
= 0;
5805 context
->haveTCPLen
= false;
5809 sockaddr_ip fromAddr
;
5811 context
->msgPtr
= context
->msgBuf
;
5812 err
= SocketRecvFrom( context
->sock
, context
->msgPtr
, sizeof( context
->msgBuf
), &context
->msgLen
, &fromAddr
,
5813 sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
5814 require_noerr( err
, exit
);
5816 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
5819 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
5820 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
5821 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
5822 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
5823 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, context
->msgPtr
, context
->msgLen
);
5825 if( ( context
->msgLen
>= kDNSHeaderLength
) && ( DNSHeaderGetID( (DNSHeader
*) context
->msgPtr
) == context
->queryID
) )
5827 Exit( kExitReason_ReceivedResponse
);
5831 if( err
) dispatch_source_forget( &context
->readSource
);
5834 //===========================================================================================================================
5835 // DNSQueryCancelHandler
5836 //===========================================================================================================================
5838 static void DNSQueryCancelHandler( void *inContext
)
5840 DNSQueryContext
* const context
= (DNSQueryContext
*) inContext
;
5842 check( !context
->readSource
);
5843 ForgetSocket( &context
->sock
);
5844 if( context
->msgPtr
!= context
->msgBuf
) ForgetMem( &context
->msgPtr
);
5846 dispatch_async_f( dispatch_get_main_queue(), NULL
, Exit
);
5849 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
5850 //===========================================================================================================================
5852 //===========================================================================================================================
5854 #define kDNSCryptPort 443
5856 #define kDNSCryptMinPadLength 8
5857 #define kDNSCryptMaxPadLength 256
5858 #define kDNSCryptBlockSize 64
5859 #define kDNSCryptCertMinimumLength 124
5860 #define kDNSCryptClientMagicLength 8
5861 #define kDNSCryptResolverMagicLength 8
5862 #define kDNSCryptHalfNonceLength 12
5863 #define kDNSCryptCertMagicLength 4
5865 check_compile_time( ( kDNSCryptHalfNonceLength
* 2 ) == crypto_box_NONCEBYTES
);
5867 static const uint8_t kDNSCryptCertMagic
[ kDNSCryptCertMagicLength
] = { 'D', 'N', 'S', 'C' };
5868 static const uint8_t kDNSCryptResolverMagic
[ kDNSCryptResolverMagicLength
] =
5870 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
5875 uint8_t certMagic
[ kDNSCryptCertMagicLength
];
5876 uint8_t esVersion
[ 2 ];
5877 uint8_t minorVersion
[ 2 ];
5878 uint8_t signature
[ crypto_sign_BYTES
];
5879 uint8_t publicKey
[ crypto_box_PUBLICKEYBYTES
];
5880 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5881 uint8_t serial
[ 4 ];
5882 uint8_t startTime
[ 4 ];
5883 uint8_t endTime
[ 4 ];
5884 uint8_t extensions
[ 1 ]; // Variably-sized extension data.
5888 check_compile_time( offsetof( DNSCryptCert
, extensions
) == kDNSCryptCertMinimumLength
);
5892 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5893 uint8_t clientPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5894 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5895 uint8_t poly1305MAC
[ 16 ];
5897 } DNSCryptQueryHeader
;
5899 check_compile_time( sizeof( DNSCryptQueryHeader
) == 68 );
5900 check_compile_time( sizeof( DNSCryptQueryHeader
) >= crypto_box_ZEROBYTES
);
5901 check_compile_time( ( sizeof( DNSCryptQueryHeader
) - crypto_box_ZEROBYTES
+ crypto_box_BOXZEROBYTES
) ==
5902 offsetof( DNSCryptQueryHeader
, poly1305MAC
) );
5906 uint8_t resolverMagic
[ kDNSCryptResolverMagicLength
];
5907 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5908 uint8_t resolverNonce
[ kDNSCryptHalfNonceLength
];
5909 uint8_t poly1305MAC
[ 16 ];
5911 } DNSCryptResponseHeader
;
5913 check_compile_time( sizeof( DNSCryptResponseHeader
) == 48 );
5914 check_compile_time( offsetof( DNSCryptResponseHeader
, poly1305MAC
) >= crypto_box_BOXZEROBYTES
);
5915 check_compile_time( ( offsetof( DNSCryptResponseHeader
, poly1305MAC
) - crypto_box_BOXZEROBYTES
+ crypto_box_ZEROBYTES
) ==
5916 sizeof( DNSCryptResponseHeader
) );
5920 sockaddr_ip serverAddr
;
5922 const char * providerName
;
5924 const uint8_t * certPtr
;
5926 dispatch_source_t readSource
;
5931 Boolean printRawRData
;
5932 uint8_t serverPublicSignKey
[ crypto_sign_PUBLICKEYBYTES
];
5933 uint8_t serverPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5934 uint8_t clientPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5935 uint8_t clientSecretKey
[ crypto_box_SECRETKEYBYTES
];
5936 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5937 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5938 uint8_t nmKey
[ crypto_box_BEFORENMBYTES
];
5939 uint8_t msgBuf
[ 512 ];
5943 static void DNSCryptReceiveCertHandler( void *inContext
);
5944 static void DNSCryptReceiveResponseHandler( void *inContext
);
5945 static void DNSCryptProceed( void *inContext
);
5946 static OSStatus
DNSCryptProcessCert( DNSCryptContext
*inContext
);
5947 static OSStatus
DNSCryptBuildQuery( DNSCryptContext
*inContext
);
5948 static OSStatus
DNSCryptSendQuery( DNSCryptContext
*inContext
);
5949 static void DNSCryptPrintCertificate( const DNSCryptCert
*inCert
, size_t inLen
);
5951 static void DNSCryptCmd( void )
5954 DNSCryptContext
* context
= NULL
;
5955 size_t writtenBytes
;
5957 SocketContext
* sockCtx
;
5958 SocketRef sock
= kInvalidSocketRef
;
5961 // Check command parameters.
5963 if( gDNSCrypt_TimeLimitSecs
< -1 )
5965 FPrintF( stdout
, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs
);
5972 context
= (DNSCryptContext
*) calloc( 1, sizeof( *context
) );
5973 require_action( context
, exit
, err
= kNoMemoryErr
);
5975 context
->providerName
= gDNSCrypt_ProviderName
;
5976 context
->qname
= gDNSCrypt_Name
;
5977 context
->timeLimitSecs
= gDNSCrypt_TimeLimitSecs
;
5978 context
->printRawRData
= gDNSCrypt_RawRData
? true : false;
5980 err
= crypto_box_keypair( context
->clientPublicKey
, context
->clientSecretKey
);
5981 require_noerr( err
, exit
);
5983 err
= HexToData( gDNSCrypt_ProviderKey
, kSizeCString
, kHexToData_DefaultFlags
,
5984 context
->serverPublicSignKey
, sizeof( context
->serverPublicSignKey
), &writtenBytes
, &totalBytes
, &ptr
);
5985 if( err
|| ( *ptr
!= '\0' ) )
5987 FPrintF( stderr
, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey
);
5990 else if( totalBytes
!= sizeof( context
->serverPublicSignKey
) )
5992 FPrintF( stderr
, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
5993 totalBytes
, sizeof( context
->serverPublicSignKey
) );
5997 check( writtenBytes
== totalBytes
);
5999 err
= StringToSockAddr( gDNSCrypt_Server
, &context
->serverAddr
, sizeof( context
->serverAddr
), NULL
);
6000 require_noerr( err
, exit
);
6001 if( SockAddrGetPort( &context
->serverAddr
) == 0 ) SockAddrSetPort( &context
->serverAddr
, kDNSCryptPort
);
6003 err
= RecordTypeFromArgString( gDNSCrypt_Type
, &context
->qtype
);
6004 require_noerr( err
, exit
);
6006 // Write query message.
6008 context
->queryID
= (uint16_t) Random32();
6009 err
= WriteDNSQueryMessage( context
->msgBuf
, context
->queryID
, kDNSHeaderFlag_RecursionDesired
, context
->providerName
,
6010 kDNSServiceType_TXT
, kDNSServiceClass_IN
, &context
->msgLen
);
6011 require_noerr( err
, exit
);
6013 // Create UDP socket.
6015 err
= UDPClientSocketOpen( AF_UNSPEC
, &context
->serverAddr
, 0, -1, NULL
, &sock
);
6016 require_noerr( err
, exit
);
6020 context
->sendTicks
= UpTicks();
6021 err
= SocketWriteAll( sock
, context
->msgBuf
, context
->msgLen
, 5 );
6022 require_noerr( err
, exit
);
6024 err
= SocketContextCreate( sock
, context
, &sockCtx
);
6025 require_noerr( err
, exit
);
6026 sock
= kInvalidSocketRef
;
6028 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, DNSCryptReceiveCertHandler
, SocketContextCancelHandler
, sockCtx
,
6029 &context
->readSource
);
6030 if( err
) ForgetSocketContext( &sockCtx
);
6031 require_noerr( err
, exit
);
6033 dispatch_resume( context
->readSource
);
6035 if( context
->timeLimitSecs
> 0 )
6037 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
6043 if( context
) free( context
);
6044 ForgetSocket( &sock
);
6045 if( err
) exit( 1 );
6048 //===========================================================================================================================
6049 // DNSCryptReceiveCertHandler
6050 //===========================================================================================================================
6052 static void DNSCryptReceiveCertHandler( void *inContext
)
6056 const uint64_t nowTicks
= UpTicks();
6057 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6058 DNSCryptContext
* const context
= (DNSCryptContext
*) sockCtx
->userContext
;
6059 const DNSHeader
* hdr
;
6060 sockaddr_ip fromAddr
;
6061 const uint8_t * ptr
;
6062 const uint8_t * txtPtr
;
6064 unsigned int answerCount
, i
;
6065 uint8_t targetName
[ kDomainNameLengthMax
];
6067 gettimeofday( &now
, NULL
);
6069 dispatch_source_forget( &context
->readSource
);
6071 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &context
->msgLen
,
6072 &fromAddr
, sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6073 require_noerr( err
, exit
);
6074 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
6076 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6077 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
6078 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
6079 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
6080 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, context
->msgBuf
, context
->msgLen
);
6082 require_action_quiet( context
->msgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
6084 hdr
= (DNSHeader
*) context
->msgBuf
;
6085 require_action_quiet( DNSHeaderGetID( hdr
) == context
->queryID
, exit
, err
= kMismatchErr
);
6087 err
= DNSMessageGetAnswerSection( context
->msgBuf
, context
->msgLen
, &ptr
);
6088 require_noerr( err
, exit
);
6090 err
= DomainNameFromString( targetName
, context
->providerName
, NULL
);
6091 require_noerr( err
, exit
);
6093 answerCount
= DNSHeaderGetAnswerCount( hdr
);
6094 for( i
= 0; i
< answerCount
; ++i
)
6098 uint8_t name
[ kDomainNameLengthMax
];
6100 err
= DNSMessageExtractRecord( context
->msgBuf
, context
->msgLen
, ptr
, name
, &type
, &class, NULL
, &txtPtr
, &txtLen
,
6102 require_noerr( err
, exit
);
6104 if( ( type
== kDNSServiceType_TXT
) && ( class == kDNSServiceClass_IN
) && DomainNameEqual( name
, targetName
) )
6110 if( txtLen
< ( 1 + kDNSCryptCertMinimumLength
) )
6112 FPrintF( stderr
, "TXT record length is too short (%u < %u)\n", txtLen
, kDNSCryptCertMinimumLength
+ 1 );
6116 if( txtPtr
[ 0 ] < kDNSCryptCertMinimumLength
)
6118 FPrintF( stderr
, "TXT record value length is too short (%u < %u)\n", txtPtr
[ 0 ], kDNSCryptCertMinimumLength
);
6123 context
->certLen
= txtPtr
[ 0 ];
6124 context
->certPtr
= &txtPtr
[ 1 ];
6126 dispatch_async_f( dispatch_get_main_queue(), context
, DNSCryptProceed
);
6129 if( err
) Exit( NULL
);
6132 //===========================================================================================================================
6133 // DNSCryptReceiveResponseHandler
6134 //===========================================================================================================================
6136 static void DNSCryptReceiveResponseHandler( void *inContext
)
6140 const uint64_t nowTicks
= UpTicks();
6141 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6142 DNSCryptContext
* const context
= (DNSCryptContext
*) sockCtx
->userContext
;
6143 sockaddr_ip fromAddr
;
6144 DNSCryptResponseHeader
* hdr
;
6145 const uint8_t * end
;
6146 uint8_t * ciphertext
;
6147 uint8_t * plaintext
;
6148 const uint8_t * response
;
6149 uint8_t nonce
[ crypto_box_NONCEBYTES
];
6151 gettimeofday( &now
, NULL
);
6153 dispatch_source_forget( &context
->readSource
);
6155 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &context
->msgLen
,
6156 &fromAddr
, sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6157 require_noerr( err
, exit
);
6158 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
6160 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6161 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
6162 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
6163 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
6165 if( context
->msgLen
< sizeof( DNSCryptResponseHeader
) )
6167 FPrintF( stderr
, "DNSCrypt response is too short.\n" );
6172 hdr
= (DNSCryptResponseHeader
*) context
->msgBuf
;
6174 if( memcmp( hdr
->resolverMagic
, kDNSCryptResolverMagic
, kDNSCryptResolverMagicLength
) != 0 )
6176 FPrintF( stderr
, "DNSCrypt response resolver magic %#H != %#H\n",
6177 hdr
->resolverMagic
, kDNSCryptResolverMagicLength
, INT_MAX
,
6178 kDNSCryptResolverMagic
, kDNSCryptResolverMagicLength
, INT_MAX
);
6183 if( memcmp( hdr
->clientNonce
, context
->clientNonce
, kDNSCryptHalfNonceLength
) != 0 )
6185 FPrintF( stderr
, "DNSCrypt response client nonce mismatch.\n" );
6190 memcpy( nonce
, hdr
->clientNonce
, crypto_box_NONCEBYTES
);
6192 ciphertext
= hdr
->poly1305MAC
- crypto_box_BOXZEROBYTES
;
6193 memset( ciphertext
, 0, crypto_box_BOXZEROBYTES
);
6195 plaintext
= (uint8_t *)( hdr
+ 1 ) - crypto_box_ZEROBYTES
;
6196 check( plaintext
== ciphertext
);
6198 end
= context
->msgBuf
+ context
->msgLen
;
6200 err
= crypto_box_open_afternm( plaintext
, ciphertext
, (size_t)( end
- ciphertext
), nonce
, context
->nmKey
);
6201 require_noerr( err
, exit
);
6203 response
= plaintext
+ crypto_box_ZEROBYTES
;
6204 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, response
, (size_t)( end
- response
) );
6205 Exit( kExitReason_ReceivedResponse
);
6208 if( err
) Exit( NULL
);
6211 //===========================================================================================================================
6213 //===========================================================================================================================
6215 static void DNSCryptProceed( void *inContext
)
6218 DNSCryptContext
* const context
= (DNSCryptContext
*) inContext
;
6220 err
= DNSCryptProcessCert( context
);
6221 require_noerr_quiet( err
, exit
);
6223 err
= DNSCryptBuildQuery( context
);
6224 require_noerr_quiet( err
, exit
);
6226 err
= DNSCryptSendQuery( context
);
6227 require_noerr_quiet( err
, exit
);
6230 if( err
) Exit( NULL
);
6233 //===========================================================================================================================
6234 // DNSCryptProcessCert
6235 //===========================================================================================================================
6237 static OSStatus
DNSCryptProcessCert( DNSCryptContext
*inContext
)
6240 const DNSCryptCert
* const cert
= (DNSCryptCert
*) inContext
->certPtr
;
6241 const uint8_t * const certEnd
= inContext
->certPtr
+ inContext
->certLen
;
6243 time_t startTimeSecs
, endTimeSecs
;
6246 unsigned long long tempLen
;
6248 DNSCryptPrintCertificate( cert
, inContext
->certLen
);
6250 if( memcmp( cert
->certMagic
, kDNSCryptCertMagic
, kDNSCryptCertMagicLength
) != 0 )
6252 FPrintF( stderr
, "DNSCrypt certificate magic %#H != %#H\n",
6253 cert
->certMagic
, kDNSCryptCertMagicLength
, INT_MAX
,
6254 kDNSCryptCertMagic
, kDNSCryptCertMagicLength
, INT_MAX
);
6259 startTimeSecs
= (time_t) ReadBig32( cert
->startTime
);
6260 endTimeSecs
= (time_t) ReadBig32( cert
->endTime
);
6262 gettimeofday( &now
, NULL
);
6263 if( now
.tv_sec
< startTimeSecs
)
6265 FPrintF( stderr
, "DNSCrypt certificate start time is in the future.\n" );
6269 if( now
.tv_sec
>= endTimeSecs
)
6271 FPrintF( stderr
, "DNSCrypt certificate has expired.\n" );
6276 signedLen
= (size_t)( certEnd
- cert
->signature
);
6277 tempBuf
= (uint8_t *) malloc( signedLen
);
6278 require_action( tempBuf
, exit
, err
= kNoMemoryErr
);
6279 err
= crypto_sign_open( tempBuf
, &tempLen
, cert
->signature
, signedLen
, inContext
->serverPublicSignKey
);
6283 FPrintF( stderr
, "DNSCrypt certificate failed verification.\n" );
6284 err
= kAuthenticationErr
;
6288 memcpy( inContext
->serverPublicKey
, cert
->publicKey
, crypto_box_PUBLICKEYBYTES
);
6289 memcpy( inContext
->clientMagic
, cert
->clientMagic
, kDNSCryptClientMagicLength
);
6291 err
= crypto_box_beforenm( inContext
->nmKey
, inContext
->serverPublicKey
, inContext
->clientSecretKey
);
6292 require_noerr( err
, exit
);
6294 inContext
->certPtr
= NULL
;
6295 inContext
->certLen
= 0;
6296 inContext
->msgLen
= 0;
6302 //===========================================================================================================================
6303 // DNSCryptBuildQuery
6304 //===========================================================================================================================
6306 static OSStatus
DNSCryptPadQuery( uint8_t *inMsgPtr
, size_t inMsgLen
, size_t inMaxLen
, size_t *outPaddedLen
);
6308 static OSStatus
DNSCryptBuildQuery( DNSCryptContext
*inContext
)
6311 DNSCryptQueryHeader
* const hdr
= (DNSCryptQueryHeader
*) inContext
->msgBuf
;
6312 uint8_t * const queryPtr
= (uint8_t *)( hdr
+ 1 );
6314 size_t paddedQueryLen
;
6315 const uint8_t * const msgLimit
= inContext
->msgBuf
+ sizeof( inContext
->msgBuf
);
6316 const uint8_t * padLimit
;
6317 uint8_t nonce
[ crypto_box_NONCEBYTES
];
6319 check_compile_time_code( sizeof( inContext
->msgBuf
) >= ( sizeof( DNSCryptQueryHeader
) + kDNSQueryMessageMaxLen
) );
6321 inContext
->queryID
= (uint16_t) Random32();
6322 err
= WriteDNSQueryMessage( queryPtr
, inContext
->queryID
, kDNSHeaderFlag_RecursionDesired
, inContext
->qname
,
6323 inContext
->qtype
, kDNSServiceClass_IN
, &queryLen
);
6324 require_noerr( err
, exit
);
6326 padLimit
= &queryPtr
[ queryLen
+ kDNSCryptMaxPadLength
];
6327 if( padLimit
> msgLimit
) padLimit
= msgLimit
;
6329 err
= DNSCryptPadQuery( queryPtr
, queryLen
, (size_t)( padLimit
- queryPtr
), &paddedQueryLen
);
6330 require_noerr( err
, exit
);
6332 memset( queryPtr
- crypto_box_ZEROBYTES
, 0, crypto_box_ZEROBYTES
);
6333 RandomBytes( inContext
->clientNonce
, kDNSCryptHalfNonceLength
);
6334 memcpy( nonce
, inContext
->clientNonce
, kDNSCryptHalfNonceLength
);
6335 memset( &nonce
[ kDNSCryptHalfNonceLength
], 0, kDNSCryptHalfNonceLength
);
6337 err
= crypto_box_afternm( queryPtr
- crypto_box_ZEROBYTES
, queryPtr
- crypto_box_ZEROBYTES
,
6338 paddedQueryLen
+ crypto_box_ZEROBYTES
, nonce
, inContext
->nmKey
);
6339 require_noerr( err
, exit
);
6341 memcpy( hdr
->clientMagic
, inContext
->clientMagic
, kDNSCryptClientMagicLength
);
6342 memcpy( hdr
->clientPublicKey
, inContext
->clientPublicKey
, crypto_box_PUBLICKEYBYTES
);
6343 memcpy( hdr
->clientNonce
, nonce
, kDNSCryptHalfNonceLength
);
6345 inContext
->msgLen
= (size_t)( &queryPtr
[ paddedQueryLen
] - inContext
->msgBuf
);
6351 static OSStatus
DNSCryptPadQuery( uint8_t *inMsgPtr
, size_t inMsgLen
, size_t inMaxLen
, size_t *outPaddedLen
)
6356 require_action_quiet( ( inMsgLen
+ kDNSCryptMinPadLength
) <= inMaxLen
, exit
, err
= kSizeErr
);
6358 paddedLen
= inMsgLen
+ kDNSCryptMinPadLength
+
6359 arc4random_uniform( (uint32_t)( inMaxLen
- ( inMsgLen
+ kDNSCryptMinPadLength
) + 1 ) );
6360 paddedLen
+= ( kDNSCryptBlockSize
- ( paddedLen
% kDNSCryptBlockSize
) );
6361 if( paddedLen
> inMaxLen
) paddedLen
= inMaxLen
;
6363 inMsgPtr
[ inMsgLen
] = 0x80;
6364 memset( &inMsgPtr
[ inMsgLen
+ 1 ], 0, paddedLen
- ( inMsgLen
+ 1 ) );
6366 if( outPaddedLen
) *outPaddedLen
= paddedLen
;
6373 //===========================================================================================================================
6374 // DNSCryptSendQuery
6375 //===========================================================================================================================
6377 static OSStatus
DNSCryptSendQuery( DNSCryptContext
*inContext
)
6380 SocketContext
* sockCtx
;
6381 SocketRef sock
= kInvalidSocketRef
;
6383 check( inContext
->msgLen
> 0 );
6384 check( !inContext
->readSource
);
6386 err
= UDPClientSocketOpen( AF_UNSPEC
, &inContext
->serverAddr
, 0, -1, NULL
, &sock
);
6387 require_noerr( err
, exit
);
6389 inContext
->sendTicks
= UpTicks();
6390 err
= SocketWriteAll( sock
, inContext
->msgBuf
, inContext
->msgLen
, 5 );
6391 require_noerr( err
, exit
);
6393 err
= SocketContextCreate( sock
, inContext
, &sockCtx
);
6394 require_noerr( err
, exit
);
6395 sock
= kInvalidSocketRef
;
6397 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, DNSCryptReceiveResponseHandler
, SocketContextCancelHandler
, sockCtx
,
6398 &inContext
->readSource
);
6399 if( err
) ForgetSocketContext( &sockCtx
);
6400 require_noerr( err
, exit
);
6402 dispatch_resume( inContext
->readSource
);
6405 ForgetSocket( &sock
);
6409 //===========================================================================================================================
6410 // DNSCryptPrintCertificate
6411 //===========================================================================================================================
6413 #define kCertTimeStrBufLen 32
6415 static char * CertTimeStr( time_t inTime
, char inBuffer
[ kCertTimeStrBufLen
] );
6417 static void DNSCryptPrintCertificate( const DNSCryptCert
*inCert
, size_t inLen
)
6419 time_t startTime
, endTime
;
6421 char timeBuf
[ kCertTimeStrBufLen
];
6423 check( inLen
>= kDNSCryptCertMinimumLength
);
6425 startTime
= (time_t) ReadBig32( inCert
->startTime
);
6426 endTime
= (time_t) ReadBig32( inCert
->endTime
);
6428 FPrintF( stdout
, "DNSCrypt certificate (%zu bytes):\n", inLen
);
6429 FPrintF( stdout
, "Cert Magic: %#H\n", inCert
->certMagic
, kDNSCryptCertMagicLength
, INT_MAX
);
6430 FPrintF( stdout
, "ES Version: %u\n", ReadBig16( inCert
->esVersion
) );
6431 FPrintF( stdout
, "Minor Version: %u\n", ReadBig16( inCert
->minorVersion
) );
6432 FPrintF( stdout
, "Signature: %H\n", inCert
->signature
, crypto_sign_BYTES
/ 2, INT_MAX
);
6433 FPrintF( stdout
, " %H\n", &inCert
->signature
[ crypto_sign_BYTES
/ 2 ], crypto_sign_BYTES
/ 2, INT_MAX
);
6434 FPrintF( stdout
, "Public Key: %H\n", inCert
->publicKey
, sizeof( inCert
->publicKey
), INT_MAX
);
6435 FPrintF( stdout
, "Client Magic: %H\n", inCert
->clientMagic
, kDNSCryptClientMagicLength
, INT_MAX
);
6436 FPrintF( stdout
, "Serial: %u\n", ReadBig32( inCert
->serial
) );
6437 FPrintF( stdout
, "Start Time: %u (%s)\n", (uint32_t) startTime
, CertTimeStr( startTime
, timeBuf
) );
6438 FPrintF( stdout
, "End Time: %u (%s)\n", (uint32_t) endTime
, CertTimeStr( endTime
, timeBuf
) );
6440 if( inLen
> kDNSCryptCertMinimumLength
)
6442 extLen
= (int)( inLen
- kDNSCryptCertMinimumLength
);
6443 FPrintF( stdout
, "Extensions: %.1H\n", inCert
->extensions
, extLen
, extLen
);
6445 FPrintF( stdout
, "\n" );
6448 static char * CertTimeStr( time_t inTime
, char inBuffer
[ kCertTimeStrBufLen
] )
6452 tm
= localtime( &inTime
);
6455 dlogassert( "localtime() returned a NULL pointer.\n" );
6460 strftime( inBuffer
, kCertTimeStrBufLen
, "%a %b %d %H:%M:%S %Z %Y", tm
);
6466 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
6468 //===========================================================================================================================
6470 //===========================================================================================================================
6474 const char * qnameStr
; // Name (QNAME) of the record being queried as a C string.
6475 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
6476 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
6477 int localPort
; // The port number to which the sockets are bound.
6478 int receiveSecs
; // After send, the amount of time to spend receiving.
6479 uint32_t ifIndex
; // Index of the interface over which to send the query.
6480 uint16_t qtype
; // The type (QTYPE) of the record being queried.
6481 Boolean isQU
; // True if the query is QU, i.e., requests unicast responses.
6482 Boolean allResponses
; // True if all mDNS messages received should be printed.
6483 Boolean printRawRData
; // True if RDATA should be printed as hexdumps.
6484 Boolean useIPv4
; // True if the query should be sent via IPv4 multicast.
6485 Boolean useIPv6
; // True if the query should be sent via IPv6 multicast.
6486 char ifName
[ IF_NAMESIZE
+ 1 ]; // Name of the interface over which to send the query.
6487 uint8_t qname
[ kDomainNameLengthMax
]; // Buffer to hold the QNAME in DNS label format.
6488 uint8_t msgBuf
[ kMDNSMessageSizeMax
]; // mDNS message buffer.
6492 static void MDNSQueryPrintPrologue( const MDNSQueryContext
*inContext
);
6493 static void MDNSQueryReadHandler( void *inContext
);
6495 static void MDNSQueryCmd( void )
6498 MDNSQueryContext
* context
;
6499 SocketRef sockV4
= kInvalidSocketRef
;
6500 SocketRef sockV6
= kInvalidSocketRef
;
6502 const char * ifname
;
6504 unsigned int sendCount
;
6506 // Check command parameters.
6508 if( gMDNSQuery_ReceiveSecs
< -1 )
6510 FPrintF( stdout
, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs
);
6515 context
= (MDNSQueryContext
*) calloc( 1, sizeof( *context
) );
6516 require_action( context
, exit
, err
= kNoMemoryErr
);
6518 context
->qnameStr
= gMDNSQuery_Name
;
6519 context
->receiveSecs
= gMDNSQuery_ReceiveSecs
;
6520 context
->isQU
= gMDNSQuery_IsQU
? true : false;
6521 context
->allResponses
= gMDNSQuery_AllResponses
? true : false;
6522 context
->printRawRData
= gMDNSQuery_RawRData
? true : false;
6523 context
->useIPv4
= ( gMDNSQuery_UseIPv4
|| !gMDNSQuery_UseIPv6
) ? true : false;
6524 context
->useIPv6
= ( gMDNSQuery_UseIPv6
|| !gMDNSQuery_UseIPv4
) ? true : false;
6526 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
6527 require_noerr_quiet( err
, exit
);
6529 ifname
= if_indextoname( context
->ifIndex
, context
->ifName
);
6530 require_action( ifname
, exit
, err
= kNameErr
);
6532 err
= RecordTypeFromArgString( gMDNSQuery_Type
, &context
->qtype
);
6533 require_noerr( err
, exit
);
6535 // Set up IPv4 socket.
6537 if( context
->useIPv4
)
6539 err
= CreateMulticastSocket( GetMDNSMulticastAddrV4(),
6540 gMDNSQuery_SourcePort
? gMDNSQuery_SourcePort
: ( context
->isQU
? context
->localPort
: kMDNSPort
),
6541 ifname
, context
->ifIndex
, !context
->isQU
, &context
->localPort
, &sockV4
);
6542 require_noerr( err
, exit
);
6545 // Set up IPv6 socket.
6547 if( context
->useIPv6
)
6549 err
= CreateMulticastSocket( GetMDNSMulticastAddrV6(),
6550 gMDNSQuery_SourcePort
? gMDNSQuery_SourcePort
: ( context
->isQU
? context
->localPort
: kMDNSPort
),
6551 ifname
, context
->ifIndex
, !context
->isQU
, &context
->localPort
, &sockV6
);
6552 require_noerr( err
, exit
);
6555 // Craft mDNS query message.
6557 check_compile_time_code( sizeof( context
->msgBuf
) >= kDNSQueryMessageMaxLen
);
6558 err
= WriteDNSQueryMessage( context
->msgBuf
, kDefaultMDNSMessageID
, kDefaultMDNSQueryFlags
, context
->qnameStr
,
6559 context
->qtype
, context
->isQU
? ( kDNSServiceClass_IN
| kQClassUnicastResponseBit
) : kDNSServiceClass_IN
, &msgLen
);
6560 require_noerr( err
, exit
);
6564 MDNSQueryPrintPrologue( context
);
6566 // Send mDNS query message.
6569 if( IsValidSocket( sockV4
) )
6571 const struct sockaddr
* const mcastAddr4
= GetMDNSMulticastAddrV4();
6573 n
= sendto( sockV4
, context
->msgBuf
, msgLen
, 0, mcastAddr4
, SockAddrGetSize( mcastAddr4
) );
6574 err
= map_socket_value_errno( sockV4
, n
== (ssize_t
) msgLen
, n
);
6577 FPrintF( stderr
, "*** Failed to send query on IPv4 socket with error %#m\n", err
);
6578 ForgetSocket( &sockV4
);
6585 if( IsValidSocket( sockV6
) )
6587 const struct sockaddr
* const mcastAddr6
= GetMDNSMulticastAddrV6();
6589 n
= sendto( sockV6
, context
->msgBuf
, msgLen
, 0, mcastAddr6
, SockAddrGetSize( mcastAddr6
) );
6590 err
= map_socket_value_errno( sockV6
, n
== (ssize_t
) msgLen
, n
);
6593 FPrintF( stderr
, "*** Failed to send query on IPv6 socket with error %#m\n", err
);
6594 ForgetSocket( &sockV6
);
6601 require_action_quiet( sendCount
> 0, exit
, err
= kUnexpectedErr
);
6603 // If there's no wait period after the send, then exit.
6605 if( context
->receiveSecs
== 0 ) goto exit
;
6607 // Create dispatch read sources for socket(s).
6609 if( IsValidSocket( sockV4
) )
6611 SocketContext
* sockCtx
;
6613 err
= SocketContextCreate( sockV4
, context
, &sockCtx
);
6614 require_noerr( err
, exit
);
6615 sockV4
= kInvalidSocketRef
;
6617 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, MDNSQueryReadHandler
, SocketContextCancelHandler
, sockCtx
,
6618 &context
->readSourceV4
);
6619 if( err
) ForgetSocketContext( &sockCtx
);
6620 require_noerr( err
, exit
);
6622 dispatch_resume( context
->readSourceV4
);
6625 if( IsValidSocket( sockV6
) )
6627 SocketContext
* sockCtx
;
6629 err
= SocketContextCreate( sockV6
, context
, &sockCtx
);
6630 require_noerr( err
, exit
);
6631 sockV6
= kInvalidSocketRef
;
6633 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, MDNSQueryReadHandler
, SocketContextCancelHandler
, sockCtx
,
6634 &context
->readSourceV6
);
6635 if( err
) ForgetSocketContext( &sockCtx
);
6636 require_noerr( err
, exit
);
6638 dispatch_resume( context
->readSourceV6
);
6641 if( context
->receiveSecs
> 0 )
6643 dispatch_after_f( dispatch_time_seconds( context
->receiveSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
6649 ForgetSocket( &sockV4
);
6650 ForgetSocket( &sockV6
);
6651 if( err
) exit( 1 );
6654 //===========================================================================================================================
6656 //===========================================================================================================================
6658 static void _MDNSColliderCmdStopHandler( void *inContext
, OSStatus inError
);
6660 static void MDNSColliderCmd( void )
6663 MDNSColliderRef collider
= NULL
;
6664 uint8_t * rdataPtr
= NULL
;
6665 size_t rdataLen
= 0;
6666 const char * ifname
;
6668 MDNSColliderProtocols protocols
;
6670 char ifName
[ IF_NAMESIZE
+ 1 ];
6671 uint8_t name
[ kDomainNameLengthMax
];
6673 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
6674 require_noerr_quiet( err
, exit
);
6676 ifname
= if_indextoname( ifIndex
, ifName
);
6679 FPrintF( stderr
, "error: Invalid interface name or index: %s\n", gInterface
);
6684 err
= DomainNameFromString( name
, gMDNSCollider_Name
, NULL
);
6687 FPrintF( stderr
, "error: Invalid record name: %s\n", gMDNSCollider_Name
);
6691 err
= RecordTypeFromArgString( gMDNSCollider_Type
, &type
);
6692 require_noerr_quiet( err
, exit
);
6694 if( gMDNSCollider_RecordData
)
6696 err
= RecordDataFromArgString( gMDNSCollider_RecordData
, &rdataPtr
, &rdataLen
);
6697 require_noerr_quiet( err
, exit
);
6700 err
= MDNSColliderCreate( dispatch_get_main_queue(), &collider
);
6701 require_noerr( err
, exit
);
6703 err
= MDNSColliderSetProgram( collider
, gMDNSCollider_Program
);
6706 FPrintF( stderr
, "error: Failed to set program string: '%s'\n", gMDNSCollider_Program
);
6710 err
= MDNSColliderSetRecord( collider
, name
, type
, rdataPtr
, rdataLen
);
6711 require_noerr( err
, exit
);
6712 ForgetMem( &rdataPtr
);
6714 protocols
= kMDNSColliderProtocol_None
;
6715 if( gMDNSCollider_UseIPv4
|| !gMDNSCollider_UseIPv6
) protocols
|= kMDNSColliderProtocol_IPv4
;
6716 if( gMDNSCollider_UseIPv6
|| !gMDNSCollider_UseIPv4
) protocols
|= kMDNSColliderProtocol_IPv6
;
6717 MDNSColliderSetProtocols( collider
, protocols
);
6718 MDNSColliderSetInterfaceIndex( collider
, ifIndex
);
6719 MDNSColliderSetStopHandler( collider
, _MDNSColliderCmdStopHandler
, collider
);
6721 err
= MDNSColliderStart( collider
);
6722 require_noerr( err
, exit
);
6727 FreeNullSafe( rdataPtr
);
6728 CFReleaseNullSafe( collider
);
6729 if( err
) exit( 1 );
6732 static void _MDNSColliderCmdStopHandler( void *inContext
, OSStatus inError
)
6734 MDNSColliderRef
const collider
= (MDNSColliderRef
) inContext
;
6736 CFRelease( collider
);
6737 exit( inError
? 1 : 0 );
6740 //===========================================================================================================================
6741 // MDNSQueryPrintPrologue
6742 //===========================================================================================================================
6744 static void MDNSQueryPrintPrologue( const MDNSQueryContext
*inContext
)
6746 const int receiveSecs
= inContext
->receiveSecs
;
6748 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, inContext
->ifName
);
6749 FPrintF( stdout
, "Name: %s\n", inContext
->qnameStr
);
6750 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->qtype
), inContext
->qtype
);
6751 FPrintF( stdout
, "Class: IN (%s)\n", inContext
->isQU
? "QU" : "QM" );
6752 FPrintF( stdout
, "Local port: %d\n", inContext
->localPort
);
6753 FPrintF( stdout
, "IP protocols: %?s%?s%?s\n",
6754 inContext
->useIPv4
, "IPv4", ( inContext
->useIPv4
&& inContext
->useIPv6
), ", ", inContext
->useIPv6
, "IPv6" );
6755 FPrintF( stdout
, "Receive duration: " );
6756 if( receiveSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", receiveSecs
, receiveSecs
!= 1, 's' );
6757 else FPrintF( stdout
, "∞\n" );
6758 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
6761 //===========================================================================================================================
6762 // MDNSQueryReadHandler
6763 //===========================================================================================================================
6765 static void MDNSQueryReadHandler( void *inContext
)
6769 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6770 MDNSQueryContext
* const context
= (MDNSQueryContext
*) sockCtx
->userContext
;
6772 sockaddr_ip fromAddr
;
6773 Boolean foundAnswer
= false;
6775 gettimeofday( &now
, NULL
);
6777 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &msgLen
, &fromAddr
,
6778 sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6779 require_noerr( err
, exit
);
6781 if( !context
->allResponses
&& ( msgLen
>= kDNSHeaderLength
) )
6783 const uint8_t * ptr
;
6784 const DNSHeader
* const hdr
= (DNSHeader
*) context
->msgBuf
;
6785 unsigned int rrCount
, i
;
6786 uint16_t type
, class;
6787 uint8_t name
[ kDomainNameLengthMax
];
6789 err
= DNSMessageGetAnswerSection( context
->msgBuf
, msgLen
, &ptr
);
6790 require_noerr( err
, exit
);
6792 if( context
->qname
[ 0 ] == 0 )
6794 err
= DomainNameAppendString( context
->qname
, context
->qnameStr
, NULL
);
6795 require_noerr( err
, exit
);
6798 rrCount
= DNSHeaderGetAnswerCount( hdr
) + DNSHeaderGetAuthorityCount( hdr
) + DNSHeaderGetAdditionalCount( hdr
);
6799 for( i
= 0; i
< rrCount
; ++i
)
6801 err
= DNSMessageExtractRecord( context
->msgBuf
, msgLen
, ptr
, name
, &type
, &class, NULL
, NULL
, NULL
, &ptr
);
6802 require_noerr( err
, exit
);
6804 if( ( ( context
->qtype
== kDNSServiceType_ANY
) || ( type
== context
->qtype
) ) &&
6805 DomainNameEqual( name
, context
->qname
) )
6812 if( context
->allResponses
|| foundAnswer
)
6814 FPrintF( stdout
, "---\n" );
6815 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6816 FPrintF( stdout
, "Source: %##a\n", &fromAddr
);
6817 FPrintF( stdout
, "Message size: %zu\n\n%#.*{du:dnsmsg}",
6818 msgLen
, context
->printRawRData
? 1 : 0, context
->msgBuf
, msgLen
);
6822 if( err
) exit( 1 );
6825 //===========================================================================================================================
6827 //===========================================================================================================================
6829 static void PIDToUUIDCmd( void )
6833 struct proc_uniqidentifierinfo info
;
6835 n
= proc_pidinfo( gPIDToUUID_PID
, PROC_PIDUNIQIDENTIFIERINFO
, 1, &info
, sizeof( info
) );
6836 require_action_quiet( n
== (int) sizeof( info
), exit
, err
= kUnknownErr
);
6838 FPrintF( stdout
, "%#U\n", info
.p_uuid
);
6842 if( err
) exit( 1 );
6845 //===========================================================================================================================
6847 //===========================================================================================================================
6849 typedef struct DNSServerPrivate
* DNSServerRef
;
6853 DNSServerRef server
; // Reference to the DNS server.
6854 dispatch_source_t sigIntSource
; // Dispatch SIGINT source.
6855 dispatch_source_t sigTermSource
; // Dispatch SIGTERM source.
6856 const char * domainOverride
; // If non-NULL, the server is to use this domain instead of "d.test.".
6857 #if( TARGET_OS_DARWIN )
6858 dispatch_source_t processMonitor
; // Process monitor source for process being followed, if any.
6859 pid_t followPID
; // PID of process being followed, if any. (If it exits, we exit).
6860 Boolean addedResolver
; // True if system DNS settings contains a resolver entry for server.
6862 Boolean loopbackOnly
; // True if the server should be bound to the loopback interface.
6864 } DNSServerCmdContext
;
6868 kDNSServerEvent_Started
= 1,
6869 kDNSServerEvent_Stopped
= 2
6871 } DNSServerEventType
;
6873 typedef void ( *DNSServerEventHandler_f
)( DNSServerEventType inType
, uintptr_t inEventData
, void *inContext
);
6875 CFTypeID
DNSServerGetTypeID( void );
6878 dispatch_queue_t inQueue
,
6879 DNSServerEventHandler_f inEventHandler
,
6880 void * inEventContext
,
6881 unsigned int inResponseDelayMs
,
6882 uint32_t inDefaultTTL
,
6884 Boolean inLoopbackOnly
,
6885 const char * inDomain
,
6886 Boolean inBadUDPMode
,
6887 DNSServerRef
* outServer
);
6888 static void DNSServerStart( DNSServerRef inServer
);
6889 static void DNSServerStop( DNSServerRef inServer
);
6891 #define ForgetDNSServer( X ) ForgetCustomEx( X, DNSServerStop, CFRelease )
6893 static void DNSServerCmdContextFree( DNSServerCmdContext
*inContext
);
6894 static void DNSServerCmdEventHandler( DNSServerEventType inType
, uintptr_t inEventData
, void *inContext
);
6895 static void DNSServerCmdSigIntHandler( void *inContext
);
6896 static void DNSServerCmdSigTermHandler( void *inContext
);
6897 #if( TARGET_OS_DARWIN )
6898 static void DNSServerCmdFollowedProcessHandler( void *inContext
);
6901 ulog_define_ex( "com.apple.dnssdutil", DNSServer
, kLogLevelInfo
, kLogFlags_None
, "DNSServer", NULL
);
6902 #define ds_ulog( LEVEL, ... ) ulog( &log_category_from_name( DNSServer ), (LEVEL), __VA_ARGS__ )
6904 static void DNSServerCmd( void )
6907 DNSServerCmdContext
* context
= NULL
;
6909 if( gDNSServer_Foreground
)
6911 LogControl( "DNSServer:output=file;stdout,DNSServer:flags=time;prefix" );
6914 err
= CheckIntegerArgument( gDNSServer_ResponseDelayMs
, "response delay (ms)", 0, INT_MAX
);
6915 require_noerr_quiet( err
, exit
);
6917 err
= CheckIntegerArgument( gDNSServer_DefaultTTL
, "default TTL", 0, INT32_MAX
);
6918 require_noerr_quiet( err
, exit
);
6920 err
= CheckIntegerArgument( gDNSServer_Port
, "port number", -UINT16_MAX
, UINT16_MAX
);
6921 require_noerr_quiet( err
, exit
);
6923 context
= (DNSServerCmdContext
*) calloc( 1, sizeof( *context
) );
6924 require_action( context
, exit
, err
= kNoMemoryErr
);
6926 context
->domainOverride
= gDNSServer_DomainOverride
;
6927 context
->loopbackOnly
= gDNSServer_LoopbackOnly
? true : false;
6929 #if( TARGET_OS_DARWIN )
6930 if( gDNSServer_FollowPID
)
6932 err
= StringToPID( gDNSServer_FollowPID
, &context
->followPID
);
6933 if( err
|| ( context
->followPID
< 0 ) )
6935 FPrintF( stderr
, "error: Invalid follow PID: %s\n", gDNSServer_FollowPID
);
6940 err
= DispatchProcessMonitorCreate( context
->followPID
, DISPATCH_PROC_EXIT
, dispatch_get_main_queue(),
6941 DNSServerCmdFollowedProcessHandler
, NULL
, context
, &context
->processMonitor
);
6942 require_noerr( err
, exit
);
6943 dispatch_resume( context
->processMonitor
);
6947 context
->followPID
= -1;
6951 signal( SIGINT
, SIG_IGN
);
6952 err
= DispatchSignalSourceCreate( SIGINT
, DNSServerCmdSigIntHandler
, context
, &context
->sigIntSource
);
6953 require_noerr( err
, exit
);
6954 dispatch_resume( context
->sigIntSource
);
6956 signal( SIGTERM
, SIG_IGN
);
6957 err
= DispatchSignalSourceCreate( SIGTERM
, DNSServerCmdSigTermHandler
, context
, &context
->sigTermSource
);
6958 require_noerr( err
, exit
);
6959 dispatch_resume( context
->sigTermSource
);
6961 err
= DNSServerCreate( dispatch_get_main_queue(), DNSServerCmdEventHandler
, context
,
6962 (unsigned int) gDNSServer_ResponseDelayMs
, (uint32_t) gDNSServer_DefaultTTL
, gDNSServer_Port
, context
->loopbackOnly
,
6963 context
->domainOverride
, gDNSServer_BadUDPMode
? true : false, &context
->server
);
6964 require_noerr( err
, exit
);
6966 DNSServerStart( context
->server
);
6970 FPrintF( stderr
, "Failed to start DNS server: %#m\n", err
);
6971 if( context
) DNSServerCmdContextFree( context
);
6972 if( err
) exit( 1 );
6975 //===========================================================================================================================
6976 // DNSServerCmdContextFree
6977 //===========================================================================================================================
6979 static void DNSServerCmdContextFree( DNSServerCmdContext
*inContext
)
6981 ForgetCF( &inContext
->server
);
6982 dispatch_source_forget( &inContext
->sigIntSource
);
6983 dispatch_source_forget( &inContext
->sigTermSource
);
6984 #if( TARGET_OS_DARWIN )
6985 dispatch_source_forget( &inContext
->processMonitor
);
6990 //===========================================================================================================================
6991 // DNSServerCmdEventHandler
6992 //===========================================================================================================================
6994 #if( TARGET_OS_DARWIN )
6995 static OSStatus
_DNSServerCmdLoopbackResolverAdd( const char *inDomain
, int inPort
);
6996 static OSStatus
_DNSServerCmdLoopbackResolverRemove( void );
6999 static void DNSServerCmdEventHandler( DNSServerEventType inType
, uintptr_t inEventData
, void *inContext
)
7002 DNSServerCmdContext
* const context
= (DNSServerCmdContext
*) inContext
;
7004 if( inType
== kDNSServerEvent_Started
)
7006 #if( TARGET_OS_DARWIN )
7007 const int port
= (int) inEventData
;
7009 err
= _DNSServerCmdLoopbackResolverAdd( context
->domainOverride
? context
->domainOverride
: "d.test.", port
);
7012 ds_ulog( kLogLevelError
, "Failed to add loopback resolver to DNS configuration for \"d.test.\" domain: %#m\n",
7014 if( context
->loopbackOnly
) ForgetDNSServer( &context
->server
);
7018 context
->addedResolver
= true;
7022 else if( inType
== kDNSServerEvent_Stopped
)
7024 const OSStatus stopError
= (OSStatus
) inEventData
;
7026 if( stopError
) ds_ulog( kLogLevelError
, "The server stopped unexpectedly with error: %#m.\n", stopError
);
7029 #if( TARGET_OS_DARWIN )
7030 if( context
->addedResolver
)
7032 err
= _DNSServerCmdLoopbackResolverRemove();
7035 ds_ulog( kLogLevelError
, "Failed to remove loopback resolver from DNS configuration: %#m\n", err
);
7039 context
->addedResolver
= false;
7042 else if( context
->loopbackOnly
)
7047 DNSServerCmdContextFree( context
);
7048 exit( ( stopError
|| err
) ? 1 : 0 );
7052 #if( TARGET_OS_DARWIN )
7053 //===========================================================================================================================
7054 // _DNSServerCmdLoopbackResolverAdd
7055 //===========================================================================================================================
7057 static OSStatus
_DNSServerCmdLoopbackResolverAdd( const char *inDomain
, int inPort
)
7060 SCDynamicStoreRef store
;
7061 CFPropertyListRef plist
= NULL
;
7062 CFStringRef key
= NULL
;
7063 const uint32_t loopbackV4
= htonl( INADDR_LOOPBACK
);
7066 store
= SCDynamicStoreCreate( NULL
, CFSTR( "com.apple.dnssdutil" ), NULL
, NULL
);
7067 err
= map_scerror( store
);
7068 require_noerr( err
, exit
);
7070 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
7083 kSCPropNetDNSSupplementalMatchDomains
, inDomain
,
7084 kSCPropNetDNSServerAddresses
, &loopbackV4
, in6addr_loopback
.s6_addr
,
7085 kSCPropNetDNSServerPort
, inPort
);
7086 require_noerr( err
, exit
);
7088 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
,
7089 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS
);
7090 require_action( key
, exit
, err
= kUnknownErr
);
7092 success
= SCDynamicStoreSetValue( store
, key
, plist
);
7093 require_action( success
, exit
, err
= kUnknownErr
);
7096 CFReleaseNullSafe( store
);
7097 CFReleaseNullSafe( plist
);
7098 CFReleaseNullSafe( key
);
7102 //===========================================================================================================================
7103 // _DNSServerCmdLoopbackResolverRemove
7104 //===========================================================================================================================
7106 static OSStatus
_DNSServerCmdLoopbackResolverRemove( void )
7109 SCDynamicStoreRef store
;
7110 CFStringRef key
= NULL
;
7113 store
= SCDynamicStoreCreate( NULL
, CFSTR( "com.apple.dnssdutil" ), NULL
, NULL
);
7114 err
= map_scerror( store
);
7115 require_noerr( err
, exit
);
7117 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
,
7118 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS
);
7119 require_action( key
, exit
, err
= kUnknownErr
);
7121 success
= SCDynamicStoreRemoveValue( store
, key
);
7122 require_action( success
, exit
, err
= kUnknownErr
);
7125 CFReleaseNullSafe( store
);
7126 CFReleaseNullSafe( key
);
7131 //===========================================================================================================================
7132 // DNSServerCmdSigIntHandler
7133 //===========================================================================================================================
7135 static void _DNSServerCmdShutdown( DNSServerCmdContext
*inContext
, int inSignal
);
7137 static void DNSServerCmdSigIntHandler( void *inContext
)
7139 _DNSServerCmdShutdown( (DNSServerCmdContext
*) inContext
, SIGINT
);
7142 //===========================================================================================================================
7143 // DNSServerCmdSigTermHandler
7144 //===========================================================================================================================
7146 static void DNSServerCmdSigTermHandler( void *inContext
)
7148 _DNSServerCmdShutdown( (DNSServerCmdContext
*) inContext
, SIGTERM
);
7151 #if( TARGET_OS_DARWIN )
7152 //===========================================================================================================================
7153 // DNSServerCmdFollowedProcessHandler
7154 //===========================================================================================================================
7156 static void DNSServerCmdFollowedProcessHandler( void *inContext
)
7158 DNSServerCmdContext
* const context
= (DNSServerCmdContext
*) inContext
;
7160 if( dispatch_source_get_data( context
->processMonitor
) & DISPATCH_PROC_EXIT
) _DNSServerCmdShutdown( context
, 0 );
7164 //===========================================================================================================================
7165 // _DNSServerCmdExternalExit
7166 //===========================================================================================================================
7168 #define SignalNumberToString( X ) ( \
7169 ( (X) == SIGINT ) ? "SIGINT" : \
7170 ( (X) == SIGTERM ) ? "SIGTERM" : \
7173 static void _DNSServerCmdShutdown( DNSServerCmdContext
*inContext
, int inSignal
)
7175 dispatch_source_forget( &inContext
->sigIntSource
);
7176 dispatch_source_forget( &inContext
->sigTermSource
);
7177 #if( TARGET_OS_DARWIN )
7178 dispatch_source_forget( &inContext
->processMonitor
);
7182 ds_ulog( kLogLevelNotice
, "Exiting: followed process (%lld) exited\n", (int64_t) inContext
->followPID
);
7187 ds_ulog( kLogLevelNotice
, "Exiting: received signal %d (%s)\n", inSignal
, SignalNumberToString( inSignal
) );
7190 ForgetDNSServer( &inContext
->server
);
7193 //===========================================================================================================================
7195 //===========================================================================================================================
7197 #define kDDotTestDomainName (const uint8_t *) "\x01" "d" "\x04" "test"
7199 typedef struct DNSDelayedResponse DNSDelayedResponse
;
7200 struct DNSDelayedResponse
7202 DNSDelayedResponse
* next
;
7203 sockaddr_ip destAddr
;
7204 uint64_t targetTicks
;
7209 struct DNSServerPrivate
7211 CFRuntimeBase base
; // CF object base.
7212 uint8_t * domain
; // Parent domain of server's resource records.
7213 dispatch_queue_t queue
; // Queue for DNS server's events.
7214 dispatch_source_t readSourceUDPv4
; // Read source for IPv4 UDP socket.
7215 dispatch_source_t readSourceUDPv6
; // Read source for IPv6 UDP socket.
7216 dispatch_source_t readSourceTCPv4
; // Read source for IPv4 TCP socket.
7217 dispatch_source_t readSourceTCPv6
; // Read source for IPv6 TCP socket.
7218 SocketRef sockUDPv4
;
7219 SocketRef sockUDPv6
;
7220 DNSServerEventHandler_f eventHandler
;
7221 void * eventContext
;
7222 DNSDelayedResponse
* responseList
;
7223 dispatch_source_t responseTimer
;
7224 unsigned int responseDelayMs
;
7225 uint32_t defaultTTL
;
7226 uint32_t serial
; // Serial number for SOA record.
7227 int port
; // Port to use for receiving and sending DNS messages.
7230 Boolean loopbackOnly
;
7231 Boolean badUDPMode
; // True if the server runs in Bad UDP mode.
7234 static void _DNSServerUDPReadHandler( void *inContext
);
7235 static void _DNSServerTCPReadHandler( void *inContext
);
7236 static void _DNSDelayedResponseFree( DNSDelayedResponse
*inResponse
);
7237 static void _DNSDelayedResponseFreeList( DNSDelayedResponse
*inList
);
7239 CF_CLASS_DEFINE( DNSServer
);
7243 dispatch_queue_t inQueue
,
7244 DNSServerEventHandler_f inEventHandler
,
7245 void * inEventContext
,
7246 unsigned int inResponseDelayMs
,
7247 uint32_t inDefaultTTL
,
7249 Boolean inLoopbackOnly
,
7250 const char * inDomain
,
7251 Boolean inBadUDPMode
,
7252 DNSServerRef
* outServer
)
7255 DNSServerRef obj
= NULL
;
7257 require_action_quiet( inDefaultTTL
<= INT32_MAX
, exit
, err
= kRangeErr
);
7259 CF_OBJECT_CREATE( DNSServer
, obj
, err
, exit
);
7261 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
7262 obj
->eventHandler
= inEventHandler
;
7263 obj
->eventContext
= inEventContext
;
7264 obj
->responseDelayMs
= inResponseDelayMs
;
7265 obj
->defaultTTL
= inDefaultTTL
;
7267 obj
->loopbackOnly
= inLoopbackOnly
;
7268 obj
->badUDPMode
= inBadUDPMode
;
7272 err
= StringToDomainName( inDomain
, &obj
->domain
, NULL
);
7273 require_noerr_quiet( err
, exit
);
7277 err
= DomainNameDup( kDDotTestDomainName
, &obj
->domain
, NULL
);
7278 require_noerr_quiet( err
, exit
);
7286 CFReleaseNullSafe( obj
);
7290 //===========================================================================================================================
7291 // _DNSServerFinalize
7292 //===========================================================================================================================
7294 static void _DNSServerFinalize( CFTypeRef inObj
)
7296 DNSServerRef
const me
= (DNSServerRef
) inObj
;
7298 check( !me
->readSourceUDPv4
);
7299 check( !me
->readSourceUDPv6
);
7300 check( !me
->readSourceTCPv4
);
7301 check( !me
->readSourceTCPv6
);
7302 check( !me
->responseTimer
);
7303 ForgetMem( &me
->domain
);
7304 dispatch_forget( &me
->queue
);
7307 //===========================================================================================================================
7309 //===========================================================================================================================
7311 static void _DNSServerStart( void *inContext
);
7312 static void _DNSServerStop( void *inContext
, OSStatus inError
);
7314 static void DNSServerStart( DNSServerRef me
)
7317 dispatch_async_f( me
->queue
, me
, _DNSServerStart
);
7320 static void _DNSServerStart( void *inContext
)
7324 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7325 SocketRef sock
= kInvalidSocketRef
;
7326 SocketContext
* sockCtx
= NULL
;
7327 const uint32_t loopbackV4
= htonl( INADDR_LOOPBACK
);
7328 int year
, month
, day
;
7330 // Create IPv4 UDP socket.
7331 // Initially, me->port is the port requested by the user. If it's 0, then the user wants any available ephemeral port.
7332 // If it's negative, then the user would like a port number equal to its absolute value, but will settle for any
7333 // available ephemeral port, if it's not available. The actual port number that was used will be stored in me->port and
7334 // used for the remaining sockets.
7336 err
= _ServerSocketOpenEx2( AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
, me
->loopbackOnly
? &loopbackV4
: NULL
,
7337 me
->port
, &me
->port
, kSocketBufferSize_DontSet
, me
->loopbackOnly
? true : false, &sock
);
7338 require_noerr( err
, exit
);
7339 check( me
->port
> 0 );
7341 // Create read source for IPv4 UDP socket.
7343 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7344 require_noerr( err
, exit
);
7345 sock
= kInvalidSocketRef
;
7347 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerUDPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7348 &me
->readSourceUDPv4
);
7349 require_noerr( err
, exit
);
7350 dispatch_resume( me
->readSourceUDPv4
);
7351 me
->sockUDPv4
= sockCtx
->sock
;
7354 // Create IPv6 UDP socket.
7356 err
= _ServerSocketOpenEx2( AF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
, me
->loopbackOnly
? &in6addr_loopback
: NULL
,
7357 me
->port
, NULL
, kSocketBufferSize_DontSet
, me
->loopbackOnly
? true : false, &sock
);
7358 require_noerr( err
, exit
);
7360 // Create read source for IPv6 UDP socket.
7362 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7363 require_noerr( err
, exit
);
7364 sock
= kInvalidSocketRef
;
7366 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerUDPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7367 &me
->readSourceUDPv6
);
7368 require_noerr( err
, exit
);
7369 dispatch_resume( me
->readSourceUDPv6
);
7370 me
->sockUDPv6
= sockCtx
->sock
;
7373 // Create IPv4 TCP socket.
7375 err
= _ServerSocketOpenEx2( AF_INET
, SOCK_STREAM
, IPPROTO_TCP
, me
->loopbackOnly
? &loopbackV4
: NULL
,
7376 me
->port
, NULL
, kSocketBufferSize_DontSet
, false, &sock
);
7377 require_noerr( err
, exit
);
7379 // Create read source for IPv4 TCP socket.
7381 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7382 require_noerr( err
, exit
);
7383 sock
= kInvalidSocketRef
;
7385 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerTCPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7386 &me
->readSourceTCPv4
);
7387 require_noerr( err
, exit
);
7388 dispatch_resume( me
->readSourceTCPv4
);
7391 // Create IPv6 TCP socket.
7393 err
= _ServerSocketOpenEx2( AF_INET6
, SOCK_STREAM
, IPPROTO_TCP
, me
->loopbackOnly
? &in6addr_loopback
: NULL
,
7394 me
->port
, NULL
, kSocketBufferSize_DontSet
, false, &sock
);
7395 require_noerr( err
, exit
);
7397 // Create read source for IPv6 TCP socket.
7399 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7400 require_noerr( err
, exit
);
7401 sock
= kInvalidSocketRef
;
7403 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerTCPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7404 &me
->readSourceTCPv6
);
7405 require_noerr( err
, exit
);
7406 dispatch_resume( me
->readSourceTCPv6
);
7409 ds_ulog( kLogLevelInfo
, "Server is using port %d.\n", me
->port
);
7410 if( me
->eventHandler
) me
->eventHandler( kDNSServerEvent_Started
, (uintptr_t) me
->port
, me
->eventContext
);
7412 // Create the serial number for the server's SOA record in the YYYMMDDnn convention recommended by
7413 // <https://tools.ietf.org/html/rfc1912#section-2.2> using the current time.
7415 gettimeofday( &now
, NULL
);
7416 SecondsToYMD_HMS( ( INT64_C_safe( kDaysToUnixEpoch
) * kSecondsPerDay
) + now
.tv_sec
, &year
, &month
, &day
,
7418 me
->serial
= (uint32_t)( ( year
* 1000000 ) + ( month
* 10000 ) + ( day
* 100 ) + 1 );
7421 ForgetSocket( &sock
);
7422 if( sockCtx
) SocketContextRelease( sockCtx
);
7423 if( err
) _DNSServerStop( me
, err
);
7426 //===========================================================================================================================
7428 //===========================================================================================================================
7430 static void _DNSServerUserStop( void *inContext
);
7431 static void _DNSServerStop2( void *inContext
);
7433 static void DNSServerStop( DNSServerRef me
)
7436 dispatch_async_f( me
->queue
, me
, _DNSServerUserStop
);
7439 static void _DNSServerUserStop( void *inContext
)
7441 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7443 _DNSServerStop( me
, kNoErr
);
7447 static void _DNSServerStop( void *inContext
, OSStatus inError
)
7449 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7451 me
->stopError
= inError
;
7452 dispatch_source_forget( &me
->readSourceUDPv4
);
7453 dispatch_source_forget( &me
->readSourceUDPv6
);
7454 dispatch_source_forget( &me
->readSourceTCPv4
);
7455 dispatch_source_forget( &me
->readSourceTCPv6
);
7456 dispatch_source_forget( &me
->responseTimer
);
7457 me
->sockUDPv4
= kInvalidSocketRef
;
7458 me
->sockUDPv6
= kInvalidSocketRef
;
7460 if( me
->responseList
)
7462 _DNSDelayedResponseFreeList( me
->responseList
);
7463 me
->responseList
= NULL
;
7465 dispatch_async_f( me
->queue
, me
, _DNSServerStop2
);
7468 static void _DNSServerStop2( void *inContext
)
7470 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7475 if( me
->eventHandler
) me
->eventHandler( kDNSServerEvent_Stopped
, (uintptr_t) me
->stopError
, me
->eventContext
);
7481 //===========================================================================================================================
7482 // _DNSDelayedResponseFree
7483 //===========================================================================================================================
7485 static void _DNSDelayedResponseFree( DNSDelayedResponse
*inResponse
)
7487 ForgetMem( &inResponse
->msgPtr
);
7491 //===========================================================================================================================
7492 // _DNSDelayedResponseFreeList
7493 //===========================================================================================================================
7495 static void _DNSDelayedResponseFreeList( DNSDelayedResponse
*inList
)
7497 DNSDelayedResponse
* response
;
7499 while( ( response
= inList
) != NULL
)
7501 inList
= response
->next
;
7502 _DNSDelayedResponseFree( response
);
7506 //===========================================================================================================================
7507 // _DNSServerUDPReadHandler
7508 //===========================================================================================================================
7511 _DNSServerAnswerQuery(
7512 DNSServerRef inServer
,
7513 const uint8_t * inQueryPtr
,
7516 uint8_t ** outResponsePtr
,
7517 size_t * outResponseLen
);
7519 #define _DNSServerAnswerQueryForUDP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7520 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, false, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7522 #define _DNSServerAnswerQueryForTCP( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7523 _DNSServerAnswerQuery( IN_SERVER, IN_QUERY_PTR, IN_QUERY_LEN, true, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7526 _DNSServerScheduleDelayedResponse(
7527 DNSServerRef inServer
,
7528 const struct sockaddr
* inDestAddr
,
7531 static void _DNSServerUDPDelayedSend( void *inContext
);
7533 static void _DNSServerUDPReadHandler( void *inContext
)
7536 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
7537 DNSServerRef
const me
= (DNSServerRef
) sockCtx
->userContext
;
7540 sockaddr_ip clientAddr
;
7541 socklen_t clientAddrLen
;
7542 uint8_t * responsePtr
= NULL
; // malloc'd
7546 gettimeofday( &now
, NULL
);
7550 clientAddrLen
= (socklen_t
) sizeof( clientAddr
);
7551 n
= recvfrom( sockCtx
->sock
, (char *) msg
, sizeof( msg
), 0, &clientAddr
.sa
, &clientAddrLen
);
7552 err
= map_socket_value_errno( sockCtx
->sock
, n
>= 0, n
);
7553 require_noerr( err
, exit
);
7555 ds_ulog( kLogLevelInfo
, "UDP server received %zd bytes from %##a at %{du:time}.\n", n
, &clientAddr
, &now
);
7557 if( n
< kDNSHeaderLength
)
7559 ds_ulog( kLogLevelInfo
, "UDP DNS message is too small (%zd < %d).\n", n
, kDNSHeaderLength
);
7563 ds_ulog( kLogLevelInfo
, "UDP received message:\n\n%1{du:dnsmsg}", msg
, (size_t) n
);
7567 err
= _DNSServerAnswerQueryForUDP( me
, msg
, (size_t) n
, &responsePtr
, &responseLen
);
7568 require_noerr_quiet( err
, exit
);
7570 // Schedule response.
7572 if( me
->responseDelayMs
> 0 )
7574 err
= _DNSServerScheduleDelayedResponse( me
, &clientAddr
.sa
, responsePtr
, responseLen
);
7575 require_noerr( err
, exit
);
7580 ds_ulog( kLogLevelInfo
, "UDP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen
, responsePtr
, responseLen
);
7582 n
= sendto( sockCtx
->sock
, (char *) responsePtr
, responseLen
, 0, &clientAddr
.sa
, clientAddrLen
);
7583 err
= map_socket_value_errno( sockCtx
->sock
, n
== (ssize_t
) responseLen
, n
);
7584 require_noerr( err
, exit
);
7588 FreeNullSafe( responsePtr
);
7593 _DNSServerScheduleDelayedResponse(
7595 const struct sockaddr
* inDestAddr
,
7600 DNSDelayedResponse
* response
;
7601 DNSDelayedResponse
** responsePtr
;
7602 DNSDelayedResponse
* newResponse
;
7603 uint64_t targetTicks
;
7605 targetTicks
= UpTicks() + MillisecondsToUpTicks( me
->responseDelayMs
);
7607 newResponse
= (DNSDelayedResponse
*) calloc( 1, sizeof( *newResponse
) );
7608 require_action( newResponse
, exit
, err
= kNoMemoryErr
);
7610 if( !me
->responseList
|| ( targetTicks
< me
->responseList
->targetTicks
) )
7612 dispatch_source_forget( &me
->responseTimer
);
7614 err
= DispatchTimerCreate( dispatch_time_milliseconds( me
->responseDelayMs
), DISPATCH_TIME_FOREVER
,
7615 ( (uint64_t) me
->responseDelayMs
) * kNanosecondsPerMillisecond
/ 10, me
->queue
, _DNSServerUDPDelayedSend
,
7616 NULL
, me
, &me
->responseTimer
);
7617 require_noerr( err
, exit
);
7618 dispatch_resume( me
->responseTimer
);
7621 SockAddrCopy( inDestAddr
, &newResponse
->destAddr
);
7622 newResponse
->targetTicks
= targetTicks
;
7623 newResponse
->msgPtr
= inMsgPtr
;
7624 newResponse
->msgLen
= inMsgLen
;
7626 for( responsePtr
= &me
->responseList
; ( response
= *responsePtr
) != NULL
; responsePtr
= &response
->next
)
7628 if( newResponse
->targetTicks
< response
->targetTicks
) break;
7630 newResponse
->next
= response
;
7631 *responsePtr
= newResponse
;
7636 if( newResponse
) _DNSDelayedResponseFree( newResponse
);
7640 static void _DNSServerUDPDelayedSend( void *inContext
)
7643 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7644 DNSDelayedResponse
* response
;
7648 uint64_t remainingNs
;
7649 DNSDelayedResponse
* freeList
= NULL
;
7651 dispatch_source_forget( &me
->responseTimer
);
7653 nowTicks
= UpTicks();
7654 while( ( ( response
= me
->responseList
) != NULL
) && ( response
->targetTicks
<= nowTicks
) )
7656 me
->responseList
= response
->next
;
7658 ds_ulog( kLogLevelInfo
, "UDP sending %zu byte response (delayed):\n\n%1{du:dnsmsg}",
7659 response
->msgLen
, response
->msgPtr
, response
->msgLen
);
7661 sock
= ( response
->destAddr
.sa
.sa_family
== AF_INET
) ? me
->sockUDPv4
: me
->sockUDPv6
;
7662 n
= sendto( sock
, (char *) response
->msgPtr
, response
->msgLen
, 0, &response
->destAddr
.sa
,
7663 SockAddrGetSize( &response
->destAddr
) );
7664 err
= map_socket_value_errno( sock
, n
== (ssize_t
) response
->msgLen
, n
);
7667 response
->next
= freeList
;
7668 freeList
= response
;
7669 nowTicks
= UpTicks();
7674 check( response
->targetTicks
> nowTicks
);
7675 remainingNs
= UpTicksToNanoseconds( response
->targetTicks
- nowTicks
);
7676 if( remainingNs
> INT64_MAX
) remainingNs
= INT64_MAX
;
7678 err
= DispatchTimerCreate( dispatch_time( DISPATCH_TIME_NOW
, (int64_t) remainingNs
), DISPATCH_TIME_FOREVER
, 0,
7679 me
->queue
, _DNSServerUDPDelayedSend
, NULL
, me
, &me
->responseTimer
);
7680 require_noerr( err
, exit
);
7681 dispatch_resume( me
->responseTimer
);
7685 if( freeList
) _DNSDelayedResponseFreeList( freeList
);
7688 //===========================================================================================================================
7689 // _DNSServerAnswerQuery
7690 //===========================================================================================================================
7692 #define kLabelPrefix_Alias "alias"
7693 #define kLabelPrefix_AliasTTL "alias-ttl"
7694 #define kLabelPrefix_Count "count-"
7695 #define kLabelPrefix_Tag "tag-"
7696 #define kLabelPrefix_TTL "ttl-"
7697 #define kLabel_IPv4 "ipv4"
7698 #define kLabel_IPv6 "ipv6"
7699 #define kLabelPrefix_SRV "srv-"
7701 #define kMaxAliasTTLCount ( ( kDomainLabelLengthMax - sizeof_string( kLabelPrefix_AliasTTL ) ) / 2 )
7702 #define kMaxParsedSRVCount ( kDomainNameLengthMax / ( 1 + sizeof_string( kLabelPrefix_SRV ) + 5 ) )
7706 uint16_t priority
; // Priority from SRV label.
7707 uint16_t weight
; // Weight from SRV label.
7708 uint16_t port
; // Port number from SRV label.
7709 uint16_t targetLen
; // Total length of the target hostname labels that follow an SRV label.
7710 const uint8_t * targetPtr
; // Pointer to the target hostname embedded in a domain name.
7715 _DNSServerInitializeResponseMessage(
7718 unsigned int inFlags
,
7719 const uint8_t * inQName
,
7720 unsigned int inQType
,
7721 unsigned int inQClass
);
7723 _DNSServerAnswerQueryDynamically(
7724 DNSServerRef inServer
,
7725 const uint8_t * inQName
,
7726 unsigned int inQType
,
7727 unsigned int inQClass
,
7729 DataBuffer
* inDB
);
7731 _DNSServerNameIsSRVName(
7732 DNSServerRef inServer
,
7733 const uint8_t * inName
,
7734 const uint8_t ** outDomainPtr
,
7735 size_t * outDomainLen
,
7736 ParsedSRV inSRVArray
[ kMaxParsedSRVCount
],
7737 size_t * outSRVCount
);
7739 _DNSServerNameIsHostname(
7740 DNSServerRef inServer
,
7741 const uint8_t * inName
,
7742 uint32_t * outAliasCount
,
7743 uint32_t inAliasTTLs
[ kMaxAliasTTLCount
],
7744 size_t * outAliasTTLCount
,
7745 unsigned int * outCount
,
7746 unsigned int * outRandCount
,
7749 Boolean
* outHasAAAA
,
7750 Boolean
* outHasSOA
);
7753 _DNSServerAnswerQuery(
7755 const uint8_t * const inQueryPtr
,
7756 const size_t inQueryLen
,
7758 uint8_t ** outResponsePtr
,
7759 size_t * outResponseLen
)
7763 const uint8_t * ptr
;
7764 const uint8_t * const queryEnd
= &inQueryPtr
[ inQueryLen
];
7765 const DNSHeader
* qhdr
;
7766 unsigned int msgID
, qflags
, qtype
, qclass
, rflags
;
7767 uint8_t qname
[ kDomainNameLengthMax
];
7769 DataBuffer_Init( &dataBuf
, NULL
, 0, kDNSMaxTCPMessageSize
);
7771 require_action_quiet( inQueryLen
>= kDNSHeaderLength
, exit
, err
= kUnderrunErr
);
7773 qhdr
= (const DNSHeader
*) inQueryPtr
;
7774 msgID
= DNSHeaderGetID( qhdr
);
7775 qflags
= DNSHeaderGetFlags( qhdr
);
7777 // Minimal checking of the query message's header.
7779 if( ( qflags
& kDNSHeaderFlag_Response
) || // The message must be a query, not a response.
7780 ( DNSFlagsGetOpCode( qflags
) != kDNSOpCode_Query
) || // OPCODE must be QUERY (standard query).
7781 ( DNSHeaderGetQuestionCount( qhdr
) != 1 ) ) // There should be a single question.
7789 ptr
= (const uint8_t *) &qhdr
[ 1 ];
7790 err
= DNSMessageExtractDomainName( inQueryPtr
, inQueryLen
, ptr
, qname
, &ptr
);
7791 require_noerr( err
, exit
);
7793 // Get QTYPE and QCLASS.
7795 require_action_quiet( ( queryEnd
- ptr
) >= 4, exit
, err
= kUnderrunErr
);
7796 qtype
= DNSQuestionFixedFieldsGetType( (const DNSQuestionFixedFields
*) ptr
);
7797 qclass
= DNSQuestionFixedFieldsGetClass( (const DNSQuestionFixedFields
*) ptr
);
7800 // Create a tentative response message.
7802 rflags
= kDNSHeaderFlag_Response
;
7803 if( qflags
& kDNSHeaderFlag_RecursionDesired
) rflags
|= kDNSHeaderFlag_RecursionDesired
;
7804 DNSFlagsSetOpCode( rflags
, kDNSOpCode_Query
);
7806 if( me
->badUDPMode
&& !inForTCP
) msgID
= (uint16_t)( msgID
+ 1 );
7807 err
= _DNSServerInitializeResponseMessage( &dataBuf
, msgID
, rflags
, qname
, qtype
, qclass
);
7808 require_noerr( err
, exit
);
7810 err
= _DNSServerAnswerQueryDynamically( me
, qname
, qtype
, qclass
, inForTCP
, &dataBuf
);
7813 DNSFlagsSetRCode( rflags
, kDNSRCode_ServerFailure
);
7814 err
= _DNSServerInitializeResponseMessage( &dataBuf
, msgID
, rflags
, qname
, qtype
, qclass
);
7815 require_noerr( err
, exit
);
7818 err
= DataBuffer_Detach( &dataBuf
, outResponsePtr
, outResponseLen
);
7819 require_noerr( err
, exit
);
7822 DataBuffer_Free( &dataBuf
);
7827 _DNSServerInitializeResponseMessage(
7830 unsigned int inFlags
,
7831 const uint8_t * inQName
,
7832 unsigned int inQType
,
7833 unsigned int inQClass
)
7838 DataBuffer_Reset( inDB
);
7840 memset( &header
, 0, sizeof( header
) );
7841 DNSHeaderSetID( &header
, inID
);
7842 DNSHeaderSetFlags( &header
, inFlags
);
7843 DNSHeaderSetQuestionCount( &header
, 1 );
7845 err
= DataBuffer_Append( inDB
, &header
, sizeof( header
) );
7846 require_noerr( err
, exit
);
7848 err
= _DataBuffer_AppendDNSQuestion( inDB
, inQName
, DomainNameLength( inQName
), (uint16_t) inQType
,
7849 (uint16_t) inQClass
);
7850 require_noerr( err
, exit
);
7857 _DNSServerAnswerQueryDynamically(
7859 const uint8_t * const inQName
,
7860 const unsigned int inQType
,
7861 const unsigned int inQClass
,
7862 const Boolean inForTCP
,
7863 DataBuffer
* const inDB
)
7867 unsigned int flags
, rcode
;
7868 uint32_t aliasCount
, i
;
7869 uint32_t aliasTTLs
[ kMaxAliasTTLCount
];
7870 size_t aliasTTLCount
;
7871 unsigned int addrCount
, randCount
;
7873 ParsedSRV srvArray
[ kMaxParsedSRVCount
];
7875 const uint8_t * srvDomainPtr
;
7876 size_t srvDomainLen
;
7877 unsigned int answerCount
;
7878 Boolean notImplemented
, truncated
;
7879 Boolean useAliasTTLs
, nameExists
, nameHasA
, nameHasAAAA
, nameHasSRV
, nameHasSOA
;
7880 uint8_t namePtr
[ 2 ];
7881 DNSRecordFixedFields fields
;
7886 require_action_quiet( inQClass
== kDNSServiceClass_IN
, done
, notImplemented
= true );
7888 notImplemented
= false;
7891 nameHasAAAA
= false;
7893 useAliasTTLs
= false;
7898 if( _DNSServerNameIsHostname( me
, inQName
, &aliasCount
, aliasTTLs
, &aliasTTLCount
, &addrCount
, &randCount
, &ttl
,
7899 &nameHasA
, &nameHasAAAA
, &nameHasSOA
) )
7901 check( !( ( aliasCount
> 0 ) && ( aliasTTLCount
> 0 ) ) );
7902 check( ( addrCount
>= 1 ) && ( addrCount
<= 255 ) );
7903 check( ( randCount
== 0 ) || ( ( randCount
>= addrCount
) && ( randCount
<= 255 ) ) );
7904 check( nameHasA
|| nameHasAAAA
);
7906 if( aliasTTLCount
> 0 )
7908 aliasCount
= (uint32_t) aliasTTLCount
;
7909 useAliasTTLs
= true;
7913 else if( _DNSServerNameIsSRVName( me
, inQName
, &srvDomainPtr
, &srvDomainLen
, srvArray
, &srvCount
) )
7918 require_quiet( nameExists
, done
);
7920 if( aliasCount
> 0 )
7923 uint8_t rdataLabel
[ 1 + kDomainLabelLengthMax
+ 1 ];
7925 // If aliasCount is non-zero, then the first label of QNAME is either "alias" or "alias-<N>". superPtr is a name
7926 // compression pointer to the second label of QNAME, i.e., the immediate superdomain name of QNAME. It's used for
7927 // the RDATA of CNAME records whose canonical name ends with the superdomain name. It may also be used to construct
7928 // CNAME record names, when the offset to the previous CNAME's RDATA doesn't fit in a compression pointer.
7930 const uint8_t superPtr
[ 2 ] = { 0xC0, (uint8_t)( kDNSHeaderLength
+ 1 + inQName
[ 0 ] ) };
7932 // The name of the first CNAME record is equal to QNAME, so nameOffset is set to offset of QNAME.
7934 nameOffset
= kDNSHeaderLength
;
7936 for( i
= aliasCount
; i
>= 1; --i
)
7942 uint8_t nameLabel
[ 1 + kDomainLabelLengthMax
];
7944 if( nameOffset
<= kDNSCompressionOffsetMax
)
7946 WriteDNSCompressionPtr( namePtr
, nameOffset
);
7947 nameLen
= sizeof( namePtr
);
7951 memcpy( nameLabel
, rdataLabel
, 1 + rdataLabel
[ 0 ] );
7952 nameLen
= 1 + nameLabel
[ 0 ] + sizeof( superPtr
);
7957 char * dst
= (char *) &rdataLabel
[ 1 ];
7958 char * const lim
= (char *) &rdataLabel
[ countof( rdataLabel
) ];
7962 err
= SNPrintF_Add( &dst
, lim
, kLabelPrefix_AliasTTL
);
7963 require_noerr( err
, exit
);
7965 for( j
= aliasCount
- ( i
- 1 ); j
< aliasCount
; ++j
)
7967 err
= SNPrintF_Add( &dst
, lim
, "-%u", aliasTTLs
[ j
] );
7968 require_noerr( err
, exit
);
7973 err
= SNPrintF_Add( &dst
, lim
, kLabelPrefix_Alias
"%?{end}-%u", i
== 2, i
- 1 );
7974 require_noerr( err
, exit
);
7976 rdataLabel
[ 0 ] = (uint8_t)( dst
- (char *) &rdataLabel
[ 1 ] );
7977 rdataLen
= 1 + rdataLabel
[ 0 ] + sizeof( superPtr
);
7981 rdataLen
= sizeof( superPtr
);
7986 size_t recordLen
= nameLen
+ sizeof( fields
) + rdataLen
;
7988 if( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
)
7996 // Set CNAME record's NAME.
7998 if( nameOffset
<= kDNSCompressionOffsetMax
)
8000 err
= DataBuffer_Append( inDB
, namePtr
, sizeof( namePtr
) );
8001 require_noerr( err
, exit
);
8005 err
= DataBuffer_Append( inDB
, nameLabel
, 1 + nameLabel
[ 0 ] );
8006 require_noerr( err
, exit
);
8008 err
= DataBuffer_Append( inDB
, superPtr
, sizeof( superPtr
) );
8009 require_noerr( err
, exit
);
8012 // Set CNAME record's TYPE, CLASS, TTL, and RDLENGTH.
8014 aliasTTL
= useAliasTTLs
? aliasTTLs
[ aliasCount
- i
] : me
->defaultTTL
;
8015 DNSRecordFixedFieldsSet( &fields
, kDNSServiceType_CNAME
, kDNSServiceClass_IN
, aliasTTL
, (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 WriteDNSCompressionPtr( 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 DNSRecordFixedFieldsSet( &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 DNSRecordFixedFieldsSet( &fields
, kDNSServiceType_SRV
, kDNSServiceClass_IN
, me
->defaultTTL
, 0 );
8144 for( i
= 0; i
< srvCount
; ++i
)
8146 SRVRecordDataFixedFields 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 SRVRecordDataFixedFieldsSet( &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
= NextLabel( 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
= NextLabel( 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
= NextLabel( 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
= NextLabel( 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
= NextLabel( 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( "com.apple.dnssdutil", 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 err
= StringToPID( gMDNSReplier_FollowPID
, &context
->followPID
);
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 SRVRecordDataFixedFields
* fields
;
9563 uint8_t * targetPtr
;
9565 err
= DomainNameDupLower( inName
, &recordName
, NULL
);
9566 require_noerr( err
, exit
);
9568 rdataLen
= sizeof( SRVRecordDataFixedFields
) + 1 + hostname
[ 0 ] + 10 + kLocalNameLen
;
9569 rdataPtr
= (uint8_t *) malloc( rdataLen
);
9570 require_action( rdataPtr
, exit
, err
= kNoMemoryErr
);
9572 lim
= &rdataPtr
[ rdataLen
];
9574 fields
= (SRVRecordDataFixedFields
*) rdataPtr
;
9575 SRVRecordDataFixedFieldsSet( 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 DNSRecordFixedFields 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 DNSRecordFixedFieldsSet( &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 WriteDNSCompressionPtr( 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 static Boolean
_MDNSReplierAboutRecordNameMatch( const MDNSReplierContext
*inContext
, const uint8_t *inName
)
10138 const uint8_t * subname
;
10139 const uint8_t * const hostname
= inContext
->hostname
;
10140 int nameMatches
= false;
10142 if( strnicmpx( &inName
[ 1 ], inName
[ 0 ], "about" ) != 0 ) goto exit
;
10143 subname
= NextLabel( inName
);
10145 if( !MemIEqual( &subname
[ 1 ], subname
[ 0 ], &hostname
[ 1 ], hostname
[ 0 ] ) ) goto exit
;
10146 subname
= NextLabel( subname
);
10148 if( !DomainNameEqual( subname
, kLocalName
) ) goto exit
;
10149 nameMatches
= true;
10152 return( nameMatches
? true : false );
10155 //===========================================================================================================================
10156 // _MDNSReplierHostnameMatch
10157 //===========================================================================================================================
10160 _MDNSReplierHostnameMatch(
10161 const MDNSReplierContext
* inContext
,
10162 const uint8_t * inName
,
10163 unsigned int * outIndex
)
10166 const uint8_t * ptr
;
10167 const uint8_t * end
;
10169 const uint8_t * const hostname
= inContext
->hostname
;
10170 int nameMatches
= false;
10172 require_quiet( inName
[ 0 ] >= hostname
[ 0 ], exit
);
10173 if( memicmp( &inName
[ 1 ], &hostname
[ 1 ], hostname
[ 0 ] ) != 0 ) goto exit
;
10175 ptr
= &inName
[ 1 + hostname
[ 0 ] ];
10176 end
= &inName
[ 1 + inName
[ 0 ] ];
10179 require_quiet( *ptr
== '-', exit
);
10182 err
= DecimalTextToUInt32( (const char *) ptr
, (const char *) end
, &index
, (const char **) &ptr
);
10183 require_noerr_quiet( err
, exit
);
10184 require_quiet( ( index
>= 2 ) && ( index
<= UINT16_MAX
), exit
);
10185 require_quiet( ptr
== end
, exit
);
10192 if( !DomainNameEqual( ptr
, kLocalName
) ) goto exit
;
10193 nameMatches
= true;
10195 if( outIndex
) *outIndex
= index
;
10198 return( nameMatches
? true : false );
10201 //===========================================================================================================================
10202 // _MDNSReplierCreateTXTRecord
10203 //===========================================================================================================================
10205 static OSStatus
_MDNSReplierCreateTXTRecord( const uint8_t *inRecordName
, size_t inSize
, uint8_t **outTXT
)
10210 size_t i
, wholeCount
, remCount
;
10213 uint8_t txtStr
[ 16 ];
10215 require_action_quiet( inSize
> 0, exit
, err
= kSizeErr
);
10217 txt
= (uint8_t *) malloc( inSize
);
10218 require_action( txt
, exit
, err
= kNoMemoryErr
);
10220 hash
= FNV1( inRecordName
, DomainNameLength( inRecordName
) );
10223 n
= MemPrintF( &txtStr
[ 1 ], 15, "hash=0x%08X", hash
);
10227 wholeCount
= inSize
/ 16;
10228 for( i
= 0; i
< wholeCount
; ++i
)
10230 memcpy( ptr
, txtStr
, 16 );
10234 remCount
= inSize
% 16;
10237 txtStr
[ 0 ] = (uint8_t)( remCount
- 1 );
10238 memcpy( ptr
, txtStr
, remCount
);
10241 check( ptr
== &txt
[ inSize
] );
10250 //===========================================================================================================================
10251 // _MRResourceRecordCreate
10252 //===========================================================================================================================
10255 _MRResourceRecordCreate(
10260 uint16_t inRDLength
,
10262 MRResourceRecord
** outRecord
)
10265 MRResourceRecord
* obj
;
10267 obj
= (MRResourceRecord
*) calloc( 1, sizeof( *obj
) );
10268 require_action( obj
, exit
, err
= kNoMemoryErr
);
10270 obj
->name
= inName
;
10271 obj
->type
= inType
;
10272 obj
->class = inClass
;
10274 obj
->rdlength
= inRDLength
;
10275 obj
->rdata
= inRData
;
10277 if( inType
== kDNSServiceType_SRV
)
10279 require_action_quiet( obj
->rdlength
> sizeof( SRVRecordDataFixedFields
), exit
, err
= kMalformedErr
);
10280 obj
->target
= obj
->rdata
+ sizeof( SRVRecordDataFixedFields
);
10288 FreeNullSafe( obj
);
10292 //===========================================================================================================================
10293 // _MRResourceRecordFree
10294 //===========================================================================================================================
10296 static void _MRResourceRecordFree( MRResourceRecord
*inRecord
)
10298 ForgetMem( &inRecord
->name
);
10299 ForgetMem( &inRecord
->rdata
);
10303 //===========================================================================================================================
10304 // _MRResourceRecordFreeList
10305 //===========================================================================================================================
10307 static void _MRResourceRecordFreeList( MRResourceRecord
*inList
)
10309 MRResourceRecord
* record
;
10311 while( ( record
= inList
) != NULL
)
10313 inList
= record
->next
;
10314 _MRResourceRecordFree( record
);
10318 //===========================================================================================================================
10319 // _MRNameOffsetItemCreate
10320 //===========================================================================================================================
10322 static OSStatus
_MRNameOffsetItemCreate( const uint8_t *inName
, uint16_t inOffset
, MRNameOffsetItem
**outItem
)
10325 MRNameOffsetItem
* obj
;
10328 require_action_quiet( inOffset
<= kDNSCompressionOffsetMax
, exit
, err
= kSizeErr
);
10330 nameLen
= DomainNameLength( inName
);
10331 obj
= (MRNameOffsetItem
*) calloc( 1, offsetof( MRNameOffsetItem
, name
) + nameLen
);
10332 require_action( obj
, exit
, err
= kNoMemoryErr
);
10334 obj
->offset
= inOffset
;
10335 memcpy( obj
->name
, inName
, nameLen
);
10344 //===========================================================================================================================
10345 // _MRNameOffsetItemFree
10346 //===========================================================================================================================
10348 static void _MRNameOffsetItemFree( MRNameOffsetItem
*inItem
)
10353 //===========================================================================================================================
10354 // _MRNameOffsetItemFreeList
10355 //===========================================================================================================================
10357 static void _MRNameOffsetItemFreeList( MRNameOffsetItem
*inList
)
10359 MRNameOffsetItem
* item
;
10361 while( ( item
= inList
) != NULL
)
10363 inList
= item
->next
;
10364 _MRNameOffsetItemFree( item
);
10368 //===========================================================================================================================
10370 //===========================================================================================================================
10372 #define kGAIPerfGAITimeLimitMs 500 // Allow at most 500 ms for a DNSServiceGetAddrInfo() operation to complete.
10373 #define kGAIPerfStandardTTL ( 1 * kSecondsPerHour )
10375 typedef struct GAITesterPrivate
* GAITesterRef
;
10376 typedef struct GAITestCase GAITestCase
;
10380 const char * name
; // Domain name that was resolved.
10381 uint64_t connectionTimeUs
; // Time in microseconds that it took to create a DNS-SD connection.
10382 uint64_t firstTimeUs
; // Time in microseconds that it took to get the first address result.
10383 uint64_t timeUs
; // Time in microseconds that it took to get all expected address results.
10386 } GAITestItemResult
;
10388 typedef void ( *GAITesterStopHandler_f
)( void *inContext
, OSStatus inError
);
10390 ( *GAITesterResultsHandler_f
)(
10391 const char * inCaseTitle
,
10392 NanoTime64 inCaseStartTime
,
10393 NanoTime64 inCaseEndTime
,
10394 const GAITestItemResult
* inResultArray
,
10395 size_t inResultCount
,
10396 void * inContext
);
10398 typedef unsigned int GAITestAddrType
;
10399 #define kGAITestAddrType_None 0
10400 #define kGAITestAddrType_IPv4 ( 1U << 0 )
10401 #define kGAITestAddrType_IPv6 ( 1U << 1 )
10402 #define kGAITestAddrType_Both ( kGAITestAddrType_IPv4 | kGAITestAddrType_IPv6 )
10404 #define GAITestAddrTypeIsValid( X ) \
10405 ( ( (X) & kGAITestAddrType_Both ) && ( ( (X) & ~kGAITestAddrType_Both ) == 0 ) )
10409 GAITesterRef tester
; // GAI tester object.
10410 CFMutableArrayRef testCaseResults
; // Array of test case results.
10411 unsigned int callDelayMs
; // Amount of time to wait before calling DNSServiceGetAddrInfo().
10412 unsigned int serverDelayMs
; // Amount of additional time to have server delay its responses.
10413 unsigned int defaultIterCount
; // Default test case iteration count.
10414 dispatch_source_t sigIntSource
; // Dispatch source for SIGINT.
10415 dispatch_source_t sigTermSource
; // Dispatch source for SIGTERM.
10416 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout.
10417 OutputFormatType outputFormat
; // Format of test results output.
10418 Boolean appendNewline
; // True if a newline character should be appended to JSON output.
10419 Boolean skipPathEval
; // True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
10420 Boolean badUDPMode
; // True if the test DNS server is to run in Bad UDP mode.
10421 Boolean testFailed
; // True if at least one test case iteration failed.
10425 static void GAIPerfContextFree( GAIPerfContext
*inContext
);
10426 static OSStatus
GAIPerfAddAdvancedTestCases( GAIPerfContext
*inContext
);
10427 static OSStatus
GAIPerfAddBasicTestCases( GAIPerfContext
*inContext
);
10428 static void GAIPerfTesterStopHandler( void *inContext
, OSStatus inError
);
10430 GAIPerfResultsHandler(
10431 const char * inCaseTitle
,
10432 NanoTime64 inCaseStartTime
,
10433 NanoTime64 inCaseEndTime
,
10434 const GAITestItemResult
* inResultArray
,
10435 size_t inResultCount
,
10436 void * inContext
);
10437 static void GAIPerfSignalHandler( void *inContext
);
10439 CFTypeID
GAITesterGetTypeID( void );
10442 dispatch_queue_t inQueue
,
10444 int inServerDelayMs
,
10445 int inServerDefaultTTL
,
10446 Boolean inSkipPathEvaluation
,
10447 Boolean inBadUDPMode
,
10448 GAITesterRef
* outTester
);
10449 static void GAITesterStart( GAITesterRef inTester
);
10450 static void GAITesterStop( GAITesterRef inTester
);
10451 static OSStatus
GAITesterAddTestCase( GAITesterRef inTester
, GAITestCase
*inCase
);
10453 GAITesterSetStopHandler(
10454 GAITesterRef inTester
,
10455 GAITesterStopHandler_f inEventHandler
,
10456 void * inEventContext
);
10458 GAITesterSetResultsHandler(
10459 GAITesterRef inTester
,
10460 GAITesterResultsHandler_f inResultsHandler
,
10461 void * inResultsContext
);
10463 static OSStatus
GAITestCaseCreate( const char *inTitle
, GAITestCase
**outCase
);
10464 static void GAITestCaseFree( GAITestCase
*inCase
);
10466 GAITestCaseAddItem(
10467 GAITestCase
* inCase
,
10468 unsigned int inAliasCount
,
10469 unsigned int inAddressCount
,
10471 GAITestAddrType inHasAddrs
,
10472 GAITestAddrType inWantAddrs
,
10473 unsigned int inTimeLimitMs
,
10474 unsigned int inItemCount
);
10476 GAITestCaseAddLocalHostItem(
10477 GAITestCase
* inCase
,
10478 GAITestAddrType inWantAddrs
,
10479 unsigned int inTimeLimitMs
,
10480 unsigned int inItemCount
);
10482 static void GAIPerfCmd( void )
10485 GAIPerfContext
* context
= NULL
;
10487 err
= CheckRootUser();
10488 require_noerr_quiet( err
, exit
);
10490 err
= CheckIntegerArgument( gGAIPerf_CallDelayMs
, "call delay (ms)", 0, INT_MAX
);
10491 require_noerr_quiet( err
, exit
);
10493 err
= CheckIntegerArgument( gGAIPerf_ServerDelayMs
, "server delay (ms)", 0, INT_MAX
);
10494 require_noerr_quiet( err
, exit
);
10496 err
= CheckIntegerArgument( gGAIPerf_IterationCount
, "iteration count", 1, INT_MAX
);
10497 require_noerr_quiet( err
, exit
);
10499 context
= (GAIPerfContext
*) calloc( 1, sizeof( *context
) );
10500 require_action( context
, exit
, err
= kNoMemoryErr
);
10502 context
->testCaseResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
10503 require_action( context
->testCaseResults
, exit
, err
= kNoMemoryErr
);
10505 context
->callDelayMs
= (unsigned int) gGAIPerf_CallDelayMs
;
10506 context
->serverDelayMs
= (unsigned int) gGAIPerf_ServerDelayMs
;
10507 context
->defaultIterCount
= (unsigned int) gGAIPerf_IterationCount
;
10508 context
->appendNewline
= gGAIPerf_OutputAppendNewline
? true : false;
10509 context
->skipPathEval
= gGAIPerf_SkipPathEvalulation
? true : false;
10510 context
->badUDPMode
= gGAIPerf_BadUDPMode
? true : false;
10512 if( gGAIPerf_OutputFilePath
)
10514 context
->outputFilePath
= strdup( gGAIPerf_OutputFilePath
);
10515 require_action( context
->outputFilePath
, exit
, err
= kNoMemoryErr
);
10518 context
->outputFormat
= (OutputFormatType
) CLIArgToValue( "format", gGAIPerf_OutputFormat
, &err
,
10519 kOutputFormatStr_JSON
, kOutputFormatType_JSON
,
10520 kOutputFormatStr_XML
, kOutputFormatType_XML
,
10521 kOutputFormatStr_Binary
, kOutputFormatType_Binary
,
10523 require_noerr_quiet( err
, exit
);
10525 err
= GAITesterCreate( dispatch_get_main_queue(), (int) context
->callDelayMs
, (int) context
->serverDelayMs
,
10526 kGAIPerfStandardTTL
, context
->skipPathEval
, context
->badUDPMode
, &context
->tester
);
10527 require_noerr( err
, exit
);
10529 check( gGAIPerf_TestSuite
);
10530 if( strcasecmp( gGAIPerf_TestSuite
, kGAIPerfTestSuiteName_Basic
) == 0 )
10532 err
= GAIPerfAddBasicTestCases( context
);
10533 require_noerr( err
, exit
);
10535 else if( strcasecmp( gGAIPerf_TestSuite
, kGAIPerfTestSuiteName_Advanced
) == 0 )
10537 err
= GAIPerfAddAdvancedTestCases( context
);
10538 require_noerr( err
, exit
);
10542 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
, kGAIPerfGAITimeLimitMs
, 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
, kGAIPerfGAITimeLimitMs
, 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
, kGAIPerfGAITimeLimitMs
,
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
, kGAIPerfGAITimeLimitMs
, 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
, kGAIPerfGAITimeLimitMs
,
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
, kGAIPerfGAITimeLimitMs
,
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
->appendNewline
, 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 startTimeStr
[ 32 ];
10928 char endTimeStr
[ 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
, _NanoTime64ToDateString( inCaseStartTime
, startTimeStr
, sizeof( startTimeStr
) ),
11072 kGAIPerfTestCaseKey_EndTime
, _NanoTime64ToDateString( inCaseEndTime
, endTimeStr
, sizeof( endTimeStr
) ),
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 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
,
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 OSStatus
GetAnyMDNSInterface( char inNameBuf
[ IF_NAMESIZE
+ 1 ], uint32_t *outIndex
);
12284 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext
);
12285 static void DNSSD_API
12286 _MDNSDiscoveryTestAboutQueryCallback(
12287 DNSServiceRef inSDRef
,
12288 DNSServiceFlags inFlags
,
12289 uint32_t inInterfaceIndex
,
12290 DNSServiceErrorType inError
,
12291 const char * inFullName
,
12294 uint16_t inRDataLen
,
12295 const void * inRDataPtr
,
12297 void * inContext
);
12299 _MDNSDiscoveryTestServiceBrowserCallback(
12300 ServiceBrowserResults
* inResults
,
12302 void * inContext
);
12303 static Boolean
_MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName
, const uint8_t *inTXTPtr
, size_t inTXTLen
);
12305 static void MDNSDiscoveryTestCmd( void )
12308 MDNSDiscoveryTestContext
* context
;
12309 char queryName
[ sizeof_field( MDNSDiscoveryTestContext
, hostname
) + 15 ];
12311 context
= (MDNSDiscoveryTestContext
*) calloc( 1, sizeof( *context
) );
12312 require_action( context
, exit
, err
= kNoMemoryErr
);
12314 err
= CheckIntegerArgument( gMDNSDiscoveryTest_InstanceCount
, "instance count", 1, UINT16_MAX
);
12315 require_noerr_quiet( err
, exit
);
12317 err
= CheckIntegerArgument( gMDNSDiscoveryTest_TXTSize
, "TXT size", 1, UINT16_MAX
);
12318 require_noerr_quiet( err
, exit
);
12320 err
= CheckIntegerArgument( gMDNSDiscoveryTest_BrowseTimeSecs
, "browse time (seconds)", 1, INT_MAX
);
12321 require_noerr_quiet( err
, exit
);
12323 err
= CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountA
, "A record count", 0, 64 );
12324 require_noerr_quiet( err
, exit
);
12326 err
= CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountAAAA
, "AAAA record count", 0, 64 );
12327 require_noerr_quiet( err
, exit
);
12329 err
= CheckDoubleArgument( gMDNSDiscoveryTest_UnicastDropRate
, "unicast drop rate", 0.0, 1.0 );
12330 require_noerr_quiet( err
, exit
);
12332 err
= CheckDoubleArgument( gMDNSDiscoveryTest_MulticastDropRate
, "multicast drop rate", 0.0, 1.0 );
12333 require_noerr_quiet( err
, exit
);
12335 err
= CheckIntegerArgument( gMDNSDiscoveryTest_MaxDropCount
, "drop count", 0, 255 );
12336 require_noerr_quiet( err
, exit
);
12338 context
->replierPID
= -1;
12339 context
->instanceCount
= (unsigned int) gMDNSDiscoveryTest_InstanceCount
;
12340 context
->txtSize
= (unsigned int) gMDNSDiscoveryTest_TXTSize
;
12341 context
->browseTimeSecs
= (unsigned int) gMDNSDiscoveryTest_BrowseTimeSecs
;
12342 context
->recordCountA
= (unsigned int) gMDNSDiscoveryTest_RecordCountA
;
12343 context
->recordCountAAAA
= (unsigned int) gMDNSDiscoveryTest_RecordCountAAAA
;
12344 context
->ucastDropRate
= gMDNSDiscoveryTest_UnicastDropRate
;
12345 context
->mcastDropRate
= gMDNSDiscoveryTest_MulticastDropRate
;
12346 context
->maxDropCount
= (unsigned int) gMDNSDiscoveryTest_MaxDropCount
;
12347 context
->outputFilePath
= gMDNSDiscoveryTest_OutputFilePath
;
12348 context
->outputAppendNewline
= gMDNSDiscoveryTest_OutputAppendNewline
? true : false;
12349 context
->noAdditionals
= gMDNSDiscoveryTest_NoAdditionals
? true : false;
12350 context
->useIPv4
= ( gMDNSDiscoveryTest_UseIPv4
|| !gMDNSDiscoveryTest_UseIPv6
) ? true : false;
12351 context
->useIPv6
= ( gMDNSDiscoveryTest_UseIPv6
|| !gMDNSDiscoveryTest_UseIPv4
) ? true : false;
12353 if( gMDNSDiscoveryTest_Interface
)
12355 err
= InterfaceIndexFromArgString( gMDNSDiscoveryTest_Interface
, &context
->ifIndex
);
12356 require_noerr_quiet( err
, exit
);
12360 err
= GetAnyMDNSInterface( NULL
, &context
->ifIndex
);
12361 require_noerr_quiet( err
, exit
);
12364 context
->outputFormat
= (OutputFormatType
) CLIArgToValue( "format", gMDNSDiscoveryTest_OutputFormat
, &err
,
12365 kOutputFormatStr_JSON
, kOutputFormatType_JSON
,
12366 kOutputFormatStr_XML
, kOutputFormatType_XML
,
12367 kOutputFormatStr_Binary
, kOutputFormatType_Binary
,
12369 require_noerr_quiet( err
, exit
);
12371 if( gMDNSDiscoveryTest_FlushCache
)
12373 err
= CheckRootUser();
12374 require_noerr_quiet( err
, exit
);
12376 err
= systemf( NULL
, "killall -HUP mDNSResponder" );
12377 require_noerr( err
, exit
);
12379 context
->flushedCache
= true;
12382 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( context
->hostname
) - 1,
12383 context
->hostname
);
12384 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( context
->tag
) - 1, context
->tag
);
12386 ASPrintF( &context
->serviceType
, "_t-%s-%u-%u._tcp", context
->tag
, context
->txtSize
, context
->instanceCount
);
12387 require_action( context
->serviceType
, exit
, err
= kUnknownErr
);
12389 err
= ASPrintF( &context
->replierCommand
,
12390 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount %u "
12391 "--countA %u --countAAAA %u --udrop %.1f --mdrop %.1f --maxDropCount %u %?s%?s%?s",
12392 (int64_t) getpid(),
12396 context
->instanceCount
,
12397 context
->recordCountA
,
12398 context
->recordCountAAAA
,
12399 context
->ucastDropRate
,
12400 context
->mcastDropRate
,
12401 context
->maxDropCount
,
12402 context
->noAdditionals
, " --noAdditionals",
12403 context
->useIPv4
, " --ipv4",
12404 context
->useIPv6
, " --ipv6" );
12405 require_action_quiet( context
->replierCommand
, exit
, err
= kUnknownErr
);
12407 err
= SpawnCommand( &context
->replierPID
, "%s", context
->replierCommand
);
12408 require_noerr_quiet( err
, exit
);
12410 // Query for the replier's about TXT record. A response means it's fully up and running.
12412 SNPrintF( queryName
, sizeof( queryName
), "about.%s.local.", context
->hostname
);
12413 err
= DNSServiceQueryRecord( &context
->query
, kDNSServiceFlagsForceMulticast
, context
->ifIndex
, queryName
,
12414 kDNSServiceType_TXT
, kDNSServiceClass_IN
, _MDNSDiscoveryTestAboutQueryCallback
, context
);
12415 require_noerr( err
, exit
);
12417 err
= DNSServiceSetDispatchQueue( context
->query
, dispatch_get_main_queue() );
12418 require_noerr( err
, exit
);
12420 err
= DispatchTimerCreate( dispatch_time_seconds( kMDNSDiscoveryTestFirstQueryTimeoutSecs
),
12421 DISPATCH_TIME_FOREVER
, UINT64_C_safe( kMDNSDiscoveryTestFirstQueryTimeoutSecs
) * kNanosecondsPerSecond
/ 10, NULL
,
12422 _MDNSDiscoveryTestFirstQueryTimeout
, NULL
, context
, &context
->queryTimer
);
12423 require_noerr( err
, exit
);
12424 dispatch_resume( context
->queryTimer
);
12426 context
->startTime
= NanoTimeGetCurrent();
12433 //===========================================================================================================================
12434 // _MDNSDiscoveryTestFirstQueryTimeout
12435 //===========================================================================================================================
12437 static void _MDNSDiscoveryTestFirstQueryTimeout( void *inContext
)
12439 MDNSDiscoveryTestContext
* const context
= (MDNSDiscoveryTestContext
*) inContext
;
12441 dispatch_source_forget( &context
->queryTimer
);
12443 FPrintF( stderr
, "error: Query for mdnsreplier's \"about\" TXT record timed out.\n" );
12447 //===========================================================================================================================
12448 // _MDNSDiscoveryTestAboutQueryCallback
12449 //===========================================================================================================================
12451 static void DNSSD_API
12452 _MDNSDiscoveryTestAboutQueryCallback(
12453 DNSServiceRef inSDRef
,
12454 DNSServiceFlags inFlags
,
12455 uint32_t inInterfaceIndex
,
12456 DNSServiceErrorType inError
,
12457 const char * inFullName
,
12460 uint16_t inRDataLen
,
12461 const void * inRDataPtr
,
12466 MDNSDiscoveryTestContext
* const context
= (MDNSDiscoveryTestContext
*) inContext
;
12469 Unused( inInterfaceIndex
);
12470 Unused( inFullName
);
12473 Unused( inRDataLen
);
12474 Unused( inRDataPtr
);
12478 require_noerr( err
, exit
);
12479 require_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
);
12481 DNSServiceForget( &context
->query
);
12482 dispatch_source_forget( &context
->queryTimer
);
12484 err
= ServiceBrowserCreate( dispatch_get_main_queue(), 0, "local.", context
->browseTimeSecs
, false, &context
->browser
);
12485 require_noerr( err
, exit
);
12487 err
= ServiceBrowserAddServiceType( context
->browser
, context
->serviceType
);
12488 require_noerr( err
, exit
);
12490 ServiceBrowserSetCallback( context
->browser
, _MDNSDiscoveryTestServiceBrowserCallback
, context
);
12491 ServiceBrowserStart( context
->browser
);
12494 if( err
) exit( 1 );
12497 //===========================================================================================================================
12498 // _MDNSDiscoveryTestServiceBrowserCallback
12499 //===========================================================================================================================
12501 #define kMDNSDiscoveryTestResultsKey_ReplierInfo CFSTR( "replierInfo" )
12502 #define kMDNSDiscoveryTestResultsKey_StartTime CFSTR( "startTime" )
12503 #define kMDNSDiscoveryTestResultsKey_EndTime CFSTR( "endTime" )
12504 #define kMDNSDiscoveryTestResultsKey_BrowseTimeSecs CFSTR( "browseTimeSecs" )
12505 #define kMDNSDiscoveryTestResultsKey_ServiceType CFSTR( "serviceType" )
12506 #define kMDNSDiscoveryTestResultsKey_FlushedCache CFSTR( "flushedCache" )
12507 #define kMDNSDiscoveryTestResultsKey_UnexpectedInstances CFSTR( "unexpectedInstances" )
12508 #define kMDNSDiscoveryTestResultsKey_MissingInstances CFSTR( "missingInstances" )
12509 #define kMDNSDiscoveryTestResultsKey_IncorrectInstances CFSTR( "incorrectInstances" )
12510 #define kMDNSDiscoveryTestResultsKey_Success CFSTR( "success" )
12511 #define kMDNSDiscoveryTestResultsKey_TotalResolveTime CFSTR( "totalResolveTimeUs" )
12513 #define kMDNSDiscoveryTestReplierInfoKey_Command CFSTR( "command" )
12514 #define kMDNSDiscoveryTestReplierInfoKey_InstanceCount CFSTR( "instanceCount" )
12515 #define kMDNSDiscoveryTestReplierInfoKey_TXTSize CFSTR( "txtSize" )
12516 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountA CFSTR( "recordCountA" )
12517 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA CFSTR( "recordCountAAAA" )
12518 #define kMDNSDiscoveryTestReplierInfoKey_Hostname CFSTR( "hostname" )
12519 #define kMDNSDiscoveryTestReplierInfoKey_NoAdditionals CFSTR( "noAdditionals" )
12520 #define kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate CFSTR( "ucastDropRate" )
12521 #define kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate CFSTR( "mcastDropRate" )
12522 #define kMDNSDiscoveryTestReplierInfoKey_MaxDropCount CFSTR( "maxDropCount" )
12524 #define kMDNSDiscoveryTestUnexpectedInstanceKey_Name CFSTR( "name" )
12525 #define kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex CFSTR( "interfaceIndex" )
12527 #define kMDNSDiscoveryTestIncorrectInstanceKey_Name CFSTR( "name" )
12528 #define kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve CFSTR( "didResolve" )
12529 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname CFSTR( "badHostname" )
12530 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadPort CFSTR( "badPort" )
12531 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT CFSTR( "badTXT" )
12532 #define kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs CFSTR( "unexpectedAddrs" )
12533 #define kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs CFSTR( "missingAddrs" )
12535 static void _MDNSDiscoveryTestServiceBrowserCallback( ServiceBrowserResults
*inResults
, OSStatus inError
, void *inContext
)
12538 MDNSDiscoveryTestContext
* const context
= (MDNSDiscoveryTestContext
*) inContext
;
12539 const SBRDomain
* domain
;
12540 const SBRServiceType
* type
;
12541 const SBRServiceInstance
* instance
;
12542 const SBRServiceInstance
** instanceArray
= NULL
;
12543 const SBRIPAddress
* ipaddr
;
12544 size_t hostnameLen
;
12549 CFMutableArrayRef unexpectedInstances
;
12550 CFMutableArrayRef missingInstances
;
12551 CFMutableArrayRef incorrectInstances
;
12552 CFMutableDictionaryRef plist
= NULL
;
12553 CFMutableDictionaryRef badDict
= NULL
;
12554 CFMutableArrayRef unexpectedAddrs
= NULL
;
12555 CFMutableArrayRef missingAddrs
= NULL
;
12556 uint64_t maxResolveTimeUs
;
12557 int success
= false;
12558 char startTimeStr
[ 32 ];
12559 char endTimeStr
[ 32 ];
12561 context
->endTime
= NanoTimeGetCurrent();
12564 require_noerr( err
, exit
);
12566 _NanoTime64ToDateString( context
->startTime
, startTimeStr
, sizeof( startTimeStr
) );
12567 _NanoTime64ToDateString( context
->endTime
, endTimeStr
, sizeof( endTimeStr
) );
12568 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
12572 "%kO=%s" // replierCommand
12573 "%kO=%lli" // txtSize
12574 "%kO=%lli" // instanceCount
12575 "%kO=%lli" // recordCountA
12576 "%kO=%lli" // recordCountAAAA
12577 "%kO=%s" // hostname
12578 "%kO=%b" // noAdditionals
12579 "%kO=%f" // ucastDropRate
12580 "%kO=%f" // mcastDropRate
12581 "%kO=%i" // maxDropCount
12583 "%kO=%s" // startTime
12584 "%kO=%s" // endTime
12585 "%kO=%lli" // browseTimeSecs
12586 "%kO=%s" // serviceType
12587 "%kO=%b" // flushedCache
12588 "%kO=[%@]" // unexpectedInstances
12589 "%kO=[%@]" // missingInstances
12590 "%kO=[%@]" // incorrectInstances
12592 kMDNSDiscoveryTestResultsKey_ReplierInfo
,
12593 kMDNSDiscoveryTestReplierInfoKey_Command
, context
->replierCommand
,
12594 kMDNSDiscoveryTestReplierInfoKey_InstanceCount
, (int64_t) context
->instanceCount
,
12595 kMDNSDiscoveryTestReplierInfoKey_TXTSize
, (int64_t) context
->txtSize
,
12596 kMDNSDiscoveryTestReplierInfoKey_RecordCountA
, (int64_t) context
->recordCountA
,
12597 kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA
, (int64_t) context
->recordCountAAAA
,
12598 kMDNSDiscoveryTestReplierInfoKey_Hostname
, context
->hostname
,
12599 kMDNSDiscoveryTestReplierInfoKey_NoAdditionals
, context
->noAdditionals
,
12600 kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate
, context
->ucastDropRate
,
12601 kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate
, context
->mcastDropRate
,
12602 kMDNSDiscoveryTestReplierInfoKey_MaxDropCount
, context
->maxDropCount
,
12603 kMDNSDiscoveryTestResultsKey_StartTime
, startTimeStr
,
12604 kMDNSDiscoveryTestResultsKey_EndTime
, endTimeStr
,
12605 kMDNSDiscoveryTestResultsKey_BrowseTimeSecs
, (int64_t) context
->browseTimeSecs
,
12606 kMDNSDiscoveryTestResultsKey_ServiceType
, context
->serviceType
,
12607 kMDNSDiscoveryTestResultsKey_FlushedCache
, context
->flushedCache
,
12608 kMDNSDiscoveryTestResultsKey_UnexpectedInstances
, &unexpectedInstances
,
12609 kMDNSDiscoveryTestResultsKey_MissingInstances
, &missingInstances
,
12610 kMDNSDiscoveryTestResultsKey_IncorrectInstances
, &incorrectInstances
);
12611 require_noerr( err
, exit
);
12613 for( domain
= inResults
->domainList
; domain
&& ( strcasecmp( domain
->name
, "local.") != 0 ); domain
= domain
->next
) {}
12614 require_action( domain
, exit
, err
= kInternalErr
);
12616 for( type
= domain
->typeList
; type
&& ( strcasecmp( type
->name
, context
->serviceType
) != 0 ); type
= type
->next
) {}
12617 require_action( type
, exit
, err
= kInternalErr
);
12619 instanceArray
= (const SBRServiceInstance
**) calloc( context
->instanceCount
, sizeof( *instanceArray
) );
12620 require_action( instanceArray
, exit
, err
= kNoMemoryErr
);
12622 hostnameLen
= strlen( context
->hostname
);
12623 for( instance
= type
->instanceList
; instance
; instance
= instance
->next
)
12625 unsigned int instanceNumber
= 0;
12627 if( strcmp_prefix( instance
->name
, context
->hostname
) == 0 )
12629 ptr
= &instance
->name
[ hostnameLen
];
12630 if( ( ptr
[ 0 ] == ' ' ) && ( ptr
[ 1 ] == '(' ) )
12633 for( end
= ptr
; isdigit_safe( *end
); ++end
) {}
12634 if( DecimalTextToUInt32( ptr
, end
, &u32
, &ptr
) == kNoErr
)
12636 if( ( u32
>= 2 ) && ( u32
<= context
->instanceCount
) && ( ptr
[ 0 ] == ')' ) && ( ptr
[ 1 ] == '\0' ) )
12638 instanceNumber
= u32
;
12642 else if( *ptr
== '\0' )
12644 instanceNumber
= 1;
12647 if( ( instanceNumber
!= 0 ) && ( instance
->ifIndex
== context
->ifIndex
) )
12649 check( !instanceArray
[ instanceNumber
- 1 ] );
12650 instanceArray
[ instanceNumber
- 1 ] = instance
;
12654 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, unexpectedInstances
,
12659 kMDNSDiscoveryTestUnexpectedInstanceKey_Name
, instance
->name
,
12660 kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex
, (int64_t) instance
->ifIndex
);
12661 require_noerr( err
, exit
);
12665 maxResolveTimeUs
= 0;
12666 for( i
= 1; i
<= context
->instanceCount
; ++i
)
12668 int isHostnameValid
;
12671 instance
= instanceArray
[ i
- 1 ];
12676 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingInstances
, "%s", context
->hostname
);
12677 require_noerr( err
, exit
);
12681 char * instanceName
= NULL
;
12683 ASPrintF( &instanceName
, "%s (%u)", context
->hostname
, i
);
12684 require_action( instanceName
, exit
, err
= kUnknownErr
);
12686 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingInstances
, "%s", instanceName
);
12687 free( instanceName
);
12688 require_noerr( err
, exit
);
12693 if( !instance
->hostname
)
12695 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, incorrectInstances
,
12700 kMDNSDiscoveryTestIncorrectInstanceKey_Name
, instance
->name
,
12701 kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve
, false );
12702 require_noerr( err
, exit
);
12706 badDict
= CFDictionaryCreateMutable( NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
12707 require_action( badDict
, exit
, err
= kNoMemoryErr
);
12709 isHostnameValid
= false;
12710 if( strcmp_prefix( instance
->hostname
, context
->hostname
) == 0 )
12712 ptr
= &instance
->hostname
[ hostnameLen
];
12715 if( strcmp( ptr
, ".local." ) == 0 ) isHostnameValid
= true;
12717 else if( *ptr
== '-' )
12720 for( end
= ptr
; isdigit_safe( *end
); ++end
) {}
12721 if( DecimalTextToUInt32( ptr
, end
, &u32
, &ptr
) == kNoErr
)
12723 if( ( u32
== i
) && ( strcmp( ptr
, ".local." ) == 0 ) ) isHostnameValid
= true;
12727 if( !isHostnameValid
)
12729 err
= CFDictionarySetCString( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname
, instance
->hostname
,
12731 require_noerr( err
, exit
);
12734 if( instance
->port
!= (uint16_t)( kMDNSReplierPortBase
+ context
->txtSize
) )
12736 err
= CFDictionarySetInt64( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_BadPort
, instance
->port
);
12737 require_noerr( err
, exit
);
12740 isTXTValid
= false;
12741 if( instance
->txtLen
== context
->txtSize
)
12743 uint8_t name
[ kDomainNameLengthMax
];
12745 err
= DomainNameFromString( name
, instance
->name
, NULL
);
12746 require_noerr( err
, exit
);
12748 err
= DomainNameAppendString( name
, type
->name
, NULL
);
12749 require_noerr( err
, exit
);
12751 err
= DomainNameAppendString( name
, "local", NULL
);
12752 require_noerr( err
, exit
);
12754 if( _MDNSDiscoveryTestTXTRecordIsValid( name
, instance
->txtPtr
, instance
->txtLen
) ) isTXTValid
= true;
12758 char * hexStr
= NULL
;
12760 ASPrintF( &hexStr
, "%.4H", instance
->txtPtr
, (int) instance
->txtLen
, (int) instance
->txtLen
);
12761 require_action( hexStr
, exit
, err
= kUnknownErr
);
12763 err
= CFDictionarySetCString( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT
, hexStr
, kSizeCString
);
12765 require_noerr( err
, exit
);
12768 if( isHostnameValid
)
12770 uint64_t addrV4Bitmap
, addrV6Bitmap
, bitmask
, resolveTimeUs
;
12772 uint8_t addrV4
[ 4 ];
12773 uint8_t addrV6
[ 16 ];
12775 if( context
->recordCountA
< 64 ) addrV4Bitmap
= ( UINT64_C( 1 ) << context
->recordCountA
) - 1;
12776 else addrV4Bitmap
= ~UINT64_C( 0 );
12778 if( context
->recordCountAAAA
< 64 ) addrV6Bitmap
= ( UINT64_C( 1 ) << context
->recordCountAAAA
) - 1;
12779 else addrV6Bitmap
= ~UINT64_C( 0 );
12782 WriteBig16( &addrV4
[ 1 ], i
);
12785 memcpy( addrV6
, kMDNSReplierBaseAddrV6
, 16 );
12786 WriteBig16( &addrV6
[ 12 ], i
);
12788 unexpectedAddrs
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
12789 require_action( unexpectedAddrs
, exit
, err
= kNoMemoryErr
);
12792 for( ipaddr
= instance
->ipaddrList
; ipaddr
; ipaddr
= ipaddr
->next
)
12794 const uint8_t * addrPtr
;
12796 int isAddrValid
= false;
12798 if( ipaddr
->sip
.sa
.sa_family
== AF_INET
)
12800 addrPtr
= (const uint8_t *) &ipaddr
->sip
.v4
.sin_addr
.s_addr
;
12801 lsb
= addrPtr
[ 3 ];
12802 if( ( memcmp( addrPtr
, addrV4
, 3 ) == 0 ) && ( lsb
>= 1 ) && ( lsb
<= context
->recordCountA
) )
12804 bitmask
= UINT64_C( 1 ) << ( lsb
- 1 );
12805 addrV4Bitmap
&= ~bitmask
;
12806 isAddrValid
= true;
12809 else if( ipaddr
->sip
.sa
.sa_family
== AF_INET6
)
12811 addrPtr
= ipaddr
->sip
.v6
.sin6_addr
.s6_addr
;
12812 lsb
= addrPtr
[ 15 ];
12813 if( ( memcmp( addrPtr
, addrV6
, 15 ) == 0 ) && ( lsb
>= 1 ) && ( lsb
<= context
->recordCountAAAA
) )
12815 bitmask
= UINT64_C( 1 ) << ( lsb
- 1 );
12816 addrV6Bitmap
&= ~bitmask
;
12817 isAddrValid
= true;
12822 if( ipaddr
->resolveTimeUs
> resolveTimeUs
) resolveTimeUs
= ipaddr
->resolveTimeUs
;
12826 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, unexpectedAddrs
, "%##a", &ipaddr
->sip
);
12827 require_noerr( err
, exit
);
12831 resolveTimeUs
+= ( instance
->discoverTimeUs
+ instance
->resolveTimeUs
);
12832 if( resolveTimeUs
> maxResolveTimeUs
) maxResolveTimeUs
= resolveTimeUs
;
12834 if( CFArrayGetCount( unexpectedAddrs
) > 0 )
12836 CFDictionarySetValue( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs
, unexpectedAddrs
);
12838 ForgetCF( &unexpectedAddrs
);
12840 missingAddrs
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
12841 require_action( missingAddrs
, exit
, err
= kNoMemoryErr
);
12843 for( j
= 1; addrV4Bitmap
!= 0; ++j
)
12845 bitmask
= UINT64_C( 1 ) << ( j
- 1 );
12846 if( addrV4Bitmap
& bitmask
)
12848 addrV4Bitmap
&= ~bitmask
;
12849 addrV4
[ 3 ] = (uint8_t) j
;
12850 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingAddrs
, "%.4a", addrV4
);
12851 require_noerr( err
, exit
);
12854 for( j
= 1; addrV6Bitmap
!= 0; ++j
)
12856 bitmask
= UINT64_C( 1 ) << ( j
- 1 );
12857 if( addrV6Bitmap
& bitmask
)
12859 addrV6Bitmap
&= ~bitmask
;
12860 addrV6
[ 15 ] = (uint8_t) j
;
12861 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, missingAddrs
, "%.16a", addrV6
);
12862 require_noerr( err
, exit
);
12866 if( CFArrayGetCount( missingAddrs
) > 0 )
12868 CFDictionarySetValue( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs
, missingAddrs
);
12870 ForgetCF( &missingAddrs
);
12873 if( CFDictionaryGetCount( badDict
) > 0 )
12875 err
= CFDictionarySetCString( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_Name
, instance
->name
,
12877 require_noerr( err
, exit
);
12879 CFDictionarySetBoolean( badDict
, kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve
, true );
12880 CFArrayAppendValue( incorrectInstances
, badDict
);
12882 ForgetCF( &badDict
);
12885 if( ( CFArrayGetCount( unexpectedInstances
) == 0 ) &&
12886 ( CFArrayGetCount( missingInstances
) == 0 ) &&
12887 ( CFArrayGetCount( incorrectInstances
) == 0 ) )
12889 err
= CFDictionarySetInt64( plist
, kMDNSDiscoveryTestResultsKey_TotalResolveTime
, (int64_t) maxResolveTimeUs
);
12890 require_noerr( err
, exit
);
12897 CFDictionarySetBoolean( plist
, kMDNSDiscoveryTestResultsKey_Success
, success
);
12899 err
= OutputPropertyList( plist
, context
->outputFormat
, context
->outputAppendNewline
, context
->outputFilePath
);
12900 require_noerr_quiet( err
, exit
);
12903 ForgetCF( &context
->browser
);
12904 if( context
->replierPID
!= -1 )
12906 kill( context
->replierPID
, SIGTERM
);
12907 context
->replierPID
= -1;
12909 FreeNullSafe( instanceArray
);
12910 CFReleaseNullSafe( plist
);
12911 CFReleaseNullSafe( badDict
);
12912 CFReleaseNullSafe( unexpectedAddrs
);
12913 CFReleaseNullSafe( missingAddrs
);
12914 exit( err
? 1 : ( success
? 0 : 2 ) );
12917 //===========================================================================================================================
12918 // _MDNSDiscoveryTestTXTRecordIsValid
12919 //===========================================================================================================================
12921 static Boolean
_MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName
, const uint8_t *inTXTPtr
, size_t inTXTLen
)
12925 const uint8_t * ptr
;
12926 size_t i
, wholeCount
, remCount
;
12927 uint8_t txtStr
[ 16 ];
12929 if( inTXTLen
== 0 ) return( false );
12931 hash
= FNV1( inRecordName
, DomainNameLength( inRecordName
) );
12934 n
= MemPrintF( &txtStr
[ 1 ], 15, "hash=0x%08X", hash
);
12938 wholeCount
= inTXTLen
/ 16;
12939 for( i
= 0; i
< wholeCount
; ++i
)
12941 if( memcmp( ptr
, txtStr
, 16 ) != 0 ) return( false );
12945 remCount
= inTXTLen
% 16;
12948 txtStr
[ 0 ] = (uint8_t)( remCount
- 1 );
12949 if( memcmp( ptr
, txtStr
, remCount
) != 0 ) return( false );
12952 check( ptr
== &inTXTPtr
[ inTXTLen
] );
12956 //===========================================================================================================================
12958 //===========================================================================================================================
12960 #define kDotLocalTestPreparationTimeLimitSecs 5
12961 #define kDotLocalTestSubTestDurationSecs 5
12963 // Constants for SRV record query subtest.
12965 #define kDotLocalTestSRV_Priority 1
12966 #define kDotLocalTestSRV_Weight 0
12967 #define kDotLocalTestSRV_Port 80
12968 #define kDotLocalTestSRV_TargetName ( (const uint8_t *) "\x03" "www" "\x07" "example" "\x03" "com" )
12969 #define kDotLocalTestSRV_TargetStr "www.example.com."
12970 #define kDotLocalTestSRV_ResultStr "1 0 80 " kDotLocalTestSRV_TargetStr
12974 kDotLocalTestState_Unset
= 0,
12975 kDotLocalTestState_Preparing
= 1,
12976 kDotLocalTestState_GAIMDNSOnly
= 2,
12977 kDotLocalTestState_GAIDNSOnly
= 3,
12978 kDotLocalTestState_GAIBoth
= 4,
12979 kDotLocalTestState_GAINeither
= 5,
12980 kDotLocalTestState_GAINoSuchRecord
= 6,
12981 kDotLocalTestState_QuerySRV
= 7,
12982 kDotLocalTestState_Done
= 8
12984 } DotLocalTestState
;
12988 const char * testDesc
; // Description of the current subtest.
12989 char * queryName
; // Query name for GetAddrInfo or QueryRecord operation.
12990 dispatch_source_t timer
; // Timer used for limiting the time for each subtest.
12991 NanoTime64 startTime
; // Timestamp of when the subtest started.
12992 NanoTime64 endTime
; // Timestamp of when the subtest ended.
12993 CFMutableArrayRef correctResults
; // Operation results that were expected.
12994 CFMutableArrayRef duplicateResults
; // Operation results that were expected, but were already received.
12995 CFMutableArrayRef unexpectedResults
; // Operation results that were unexpected.
12996 OSStatus error
; // Subtest's error code.
12997 uint32_t addrDNSv4
; // If hasDNSv4 is true, the expected DNS IPv4 address for queryName.
12998 uint32_t addrMDNSv4
; // If hasMDNSv4 is true, the expected MDNS IPv4 address for queryName.
12999 uint8_t addrDNSv6
[ 16 ]; // If hasDNSv6 is true, the expected DNS IPv6 address for queryName.
13000 uint8_t addrMDNSv6
[ 16 ]; // If hasMDNSv6 is true, the expected MDNS IPv6 address for queryName.
13001 Boolean hasDNSv4
; // True if queryName has a DNS IPv4 address.
13002 Boolean hasDNSv6
; // True if queryName has a DNS IPv6 address.
13003 Boolean hasMDNSv4
; // True if queryName has an MDNS IPv4 address.
13004 Boolean hasMDNSv6
; // True if queryName has an MDNS IPv6 address.
13005 Boolean needDNSv4
; // True if operation is expecting, but hasn't received a DNS IPv4 result.
13006 Boolean needDNSv6
; // True if operation is expecting, but hasn't received a DNS IPv6 result.
13007 Boolean needMDNSv4
; // True if operation is expecting, but hasn't received an MDNS IPv4 result.
13008 Boolean needMDNSv6
; // True if operation is expecting, but hasn't received an MDNS IPv6 result.
13009 Boolean needSRV
; // True if operation is expecting, but hasn't received an SRV result.
13015 dispatch_source_t timer
; // Timer used for limiting the time for each state/subtest.
13016 DotLocalSubtest
* subtest
; // Current subtest's state.
13017 DNSServiceRef connection
; // Shared connection for DNS-SD operations.
13018 DNSServiceRef op
; // Reference for the current DNS-SD operation.
13019 DNSServiceRef op2
; // Reference for mdnsreplier probe query used during preparing state.
13020 DNSRecordRef localSOARef
; // Reference returned by DNSServiceRegisterRecord() for local. SOA record.
13021 char * replierCmd
; // Command used to invoke the mdnsreplier.
13022 char * serverCmd
; // Command used to invoke the test DNS server.
13023 CFMutableArrayRef reportsGAI
; // Reports for subtests that use DNSServiceGetAddrInfo.
13024 CFMutableArrayRef reportsQuerySRV
; // Reports for subtests that use DNSServiceQueryRecord for SRV records.
13025 NanoTime64 startTime
; // Timestamp for when the test started.
13026 NanoTime64 endTime
; // Timestamp for when the test ended.
13027 DotLocalTestState state
; // The test's current state.
13028 pid_t replierPID
; // PID of spawned mdnsreplier.
13029 pid_t serverPID
; // PID of spawned test DNS server.
13030 uint32_t ifIndex
; // Interface index used for mdnsreplier.
13031 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout.
13032 OutputFormatType outputFormat
; // Format of test results output.
13033 Boolean appendNewline
; // True if a newline character should be appended to JSON output.
13034 Boolean registeredSOA
; // True if the dummy local. SOA record was successfully registered.
13035 Boolean serverIsReady
; // True if response was received for test DNS server probe query.
13036 Boolean replierIsReady
; // True if response was received for mdnsreplier probe query.
13037 Boolean testFailed
; // True if at least one subtest failed.
13038 char labelStr
[ 20 + 1 ]; // Unique label string used for for making the query names used by subtests.
13039 // The format of this string is "dotlocal-test-<six random chars>".
13040 } DotLocalTestContext
;
13042 static void _DotLocalTestStateMachine( DotLocalTestContext
*inContext
);
13043 static void DNSSD_API
13044 _DotLocalTestProbeQueryRecordCallback(
13045 DNSServiceRef inSDRef
,
13046 DNSServiceFlags inFlags
,
13047 uint32_t inInterfaceIndex
,
13048 DNSServiceErrorType inError
,
13049 const char * inFullName
,
13052 uint16_t inRDataLen
,
13053 const void * inRDataPtr
,
13055 void * inContext
);
13056 static void DNSSD_API
13057 _DotLocalTestRegisterRecordCallback(
13058 DNSServiceRef inSDRef
,
13059 DNSRecordRef inRecordRef
,
13060 DNSServiceFlags inFlags
,
13061 DNSServiceErrorType inError
,
13062 void * inContext
);
13063 static void _DotLocalTestTimerHandler( void *inContext
);
13064 static void DNSSD_API
13065 _DotLocalTestGAICallback(
13066 DNSServiceRef inSDRef
,
13067 DNSServiceFlags inFlags
,
13068 uint32_t inInterfaceIndex
,
13069 DNSServiceErrorType inError
,
13070 const char * inHostname
,
13071 const struct sockaddr
* inSockAddr
,
13073 void * inContext
);
13074 static void DNSSD_API
13075 _DotLocalTestQueryRecordCallback(
13076 DNSServiceRef inSDRef
,
13077 DNSServiceFlags inFlags
,
13078 uint32_t inInterfaceIndex
,
13079 DNSServiceErrorType inError
,
13080 const char * inFullName
,
13083 uint16_t inRDataLen
,
13084 const void * inRDataPtr
,
13086 void * inContext
);
13088 static void DotLocalTestCmd( void )
13091 DotLocalTestContext
* context
;
13092 uint8_t * rdataPtr
;
13094 DNSServiceFlags flags
;
13095 char queryName
[ 64 ];
13096 char randBuf
[ 6 + 1 ]; // Large enough for four and six character random strings below.
13098 context
= (DotLocalTestContext
*) calloc( 1, sizeof( *context
) );
13099 require_action( context
, exit
, err
= kNoMemoryErr
);
13101 context
->startTime
= NanoTimeGetCurrent();
13102 context
->endTime
= kNanoTime_Invalid
;
13104 context
->state
= kDotLocalTestState_Preparing
;
13106 if( gDotLocalTest_Interface
)
13108 err
= InterfaceIndexFromArgString( gDotLocalTest_Interface
, &context
->ifIndex
);
13109 require_noerr_quiet( err
, exit
);
13113 err
= GetAnyMDNSInterface( NULL
, &context
->ifIndex
);
13114 require_noerr_quiet( err
, exit
);
13117 if( gDotLocalTest_OutputFilePath
)
13119 context
->outputFilePath
= strdup( gDotLocalTest_OutputFilePath
);
13120 require_action( context
->outputFilePath
, exit
, err
= kNoMemoryErr
);
13123 context
->outputFormat
= (OutputFormatType
) CLIArgToValue( "format", gDotLocalTest_OutputFormat
, &err
,
13124 kOutputFormatStr_JSON
, kOutputFormatType_JSON
,
13125 kOutputFormatStr_XML
, kOutputFormatType_XML
,
13126 kOutputFormatStr_Binary
, kOutputFormatType_Binary
,
13128 require_noerr_quiet( err
, exit
);
13130 context
->appendNewline
= gDotLocalTest_OutputAppendNewline
? true : false;
13132 context
->reportsGAI
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13133 require_action( context
->reportsGAI
, exit
, err
= kNoMemoryErr
);
13135 context
->reportsQuerySRV
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13136 require_action( context
->reportsQuerySRV
, exit
, err
= kNoMemoryErr
);
13138 SNPrintF( context
->labelStr
, sizeof( context
->labelStr
), "dotlocal-test-%s",
13139 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, 6, randBuf
) );
13141 // Spawn an mdnsreplier.
13143 err
= ASPrintF( &context
->replierCmd
,
13144 "dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount 2 --countA 1"
13146 (int64_t) getpid(), context
->ifIndex
, context
->labelStr
,
13147 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, 4, randBuf
) );
13148 require_action_quiet( context
->replierCmd
, exit
, err
= kUnknownErr
);
13150 err
= SpawnCommand( &context
->replierPID
, "%s", context
->replierCmd
);
13151 require_noerr( err
, exit
);
13153 // Spawn a test DNS server
13155 err
= ASPrintF( &context
->serverCmd
,
13156 "dnssdutil server --loopback --follow %lld --port 0 --defaultTTL 300 --domain %s.local.",
13157 (int64_t) getpid(), context
->labelStr
);
13158 require_action_quiet( context
->serverCmd
, exit
, err
= kUnknownErr
);
13160 err
= SpawnCommand( &context
->serverPID
, "%s", context
->serverCmd
);
13161 require_noerr( err
, exit
);
13163 // Create a shared DNS-SD connection.
13165 err
= DNSServiceCreateConnection( &context
->connection
);
13166 require_noerr( err
, exit
);
13168 err
= DNSServiceSetDispatchQueue( context
->connection
, dispatch_get_main_queue() );
13169 require_noerr( err
, exit
);
13171 // Create probe query for DNS server, i.e., query for any name that has an A record.
13173 SNPrintF( queryName
, sizeof( queryName
), "tag-dotlocal-test-probe.ipv4.%s.local.", context
->labelStr
);
13175 flags
= kDNSServiceFlagsShareConnection
;
13176 #if( TARGET_OS_WATCH )
13177 flags
|= kDNSServiceFlagsPathEvaluationDone
;
13180 context
->op
= context
->connection
;
13181 err
= DNSServiceQueryRecord( &context
->op
, flags
, kDNSServiceInterfaceIndexAny
, queryName
, kDNSServiceType_A
,
13182 kDNSServiceClass_IN
, _DotLocalTestProbeQueryRecordCallback
, context
);
13183 require_noerr( err
, exit
);
13185 // Create probe query for mdnsreplier's "about" TXT record.
13187 SNPrintF( queryName
, sizeof( queryName
), "about.%s.local.", context
->labelStr
);
13189 flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsForceMulticast
;
13190 #if( TARGET_OS_WATCH )
13191 flags
|= kDNSServiceFlagsPathEvaluationDone
;
13194 context
->op2
= context
->connection
;
13195 err
= DNSServiceQueryRecord( &context
->op2
, flags
, context
->ifIndex
, queryName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
,
13196 _DotLocalTestProbeQueryRecordCallback
, context
);
13197 require_noerr( err
, exit
);
13199 // Register a dummy local. SOA record.
13201 err
= CreateSOARecordData( kRootLabel
, kRootLabel
, 1976040101, 1 * kSecondsPerDay
, 2 * kSecondsPerHour
,
13202 1000 * kSecondsPerHour
, 2 * kSecondsPerDay
, &rdataPtr
, &rdataLen
);
13203 require_noerr( err
, exit
);
13205 err
= DNSServiceRegisterRecord( context
->connection
, &context
->localSOARef
, kDNSServiceFlagsUnique
,
13206 kDNSServiceInterfaceIndexLocalOnly
, "local.", kDNSServiceType_SOA
, kDNSServiceClass_IN
, 1,
13207 rdataPtr
, 1 * kSecondsPerHour
, _DotLocalTestRegisterRecordCallback
, context
);
13208 require_noerr( err
, exit
);
13210 // Start timer for probe responses and SOA record registration.
13212 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestPreparationTimeLimitSecs
),
13213 INT64_C_safe( kDotLocalTestPreparationTimeLimitSecs
) * kNanosecondsPerSecond
/ 10, dispatch_get_main_queue(),
13214 _DotLocalTestTimerHandler
, context
, &context
->timer
);
13215 require_noerr( err
, exit
);
13216 dispatch_resume( context
->timer
);
13221 if( err
) ErrQuit( 1, "error: %#m\n", err
);
13224 //===========================================================================================================================
13225 // _DotLocalTestStateMachine
13226 //===========================================================================================================================
13228 static OSStatus
_DotLocalSubtestCreate( DotLocalSubtest
**outSubtest
);
13229 static void _DotLocalSubtestFree( DotLocalSubtest
*inSubtest
);
13230 static OSStatus
_DotLocalTestStartSubtest( DotLocalTestContext
*inContext
);
13231 static OSStatus
_DotLocalTestFinalizeSubtest( DotLocalTestContext
*inContext
);
13232 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext
*inContext
) ATTRIBUTE_NORETURN
;
13234 static void _DotLocalTestStateMachine( DotLocalTestContext
*inContext
)
13237 DotLocalTestState nextState
;
13239 DNSServiceForget( &inContext
->op
);
13240 DNSServiceForget( &inContext
->op2
);
13241 dispatch_source_forget( &inContext
->timer
);
13243 switch( inContext
->state
)
13245 case kDotLocalTestState_Preparing
: nextState
= kDotLocalTestState_GAIMDNSOnly
; break;
13246 case kDotLocalTestState_GAIMDNSOnly
: nextState
= kDotLocalTestState_GAIDNSOnly
; break;
13247 case kDotLocalTestState_GAIDNSOnly
: nextState
= kDotLocalTestState_GAIBoth
; break;
13248 case kDotLocalTestState_GAIBoth
: nextState
= kDotLocalTestState_GAINeither
; break;
13249 case kDotLocalTestState_GAINeither
: nextState
= kDotLocalTestState_GAINoSuchRecord
; break;
13250 case kDotLocalTestState_GAINoSuchRecord
: nextState
= kDotLocalTestState_QuerySRV
; break;
13251 case kDotLocalTestState_QuerySRV
: nextState
= kDotLocalTestState_Done
; break;
13252 default: err
= kStateErr
; goto exit
;
13255 if( inContext
->state
== kDotLocalTestState_Preparing
)
13257 if( !inContext
->registeredSOA
|| !inContext
->serverIsReady
|| !inContext
->replierIsReady
)
13259 FPrintF( stderr
, "Preparation timed out: Registered SOA? %s. Server ready? %s. mdnsreplier ready? %s.\n",
13260 YesNoStr( inContext
->registeredSOA
),
13261 YesNoStr( inContext
->serverIsReady
),
13262 YesNoStr( inContext
->replierIsReady
) );
13263 err
= kNotPreparedErr
;
13269 err
= _DotLocalTestFinalizeSubtest( inContext
);
13270 require_noerr( err
, exit
);
13273 inContext
->state
= nextState
;
13274 if( inContext
->state
== kDotLocalTestState_Done
) _DotLocalTestFinalizeAndExit( inContext
);
13275 err
= _DotLocalTestStartSubtest( inContext
);
13278 if( err
) ErrQuit( 1, "error: %#m\n", err
);
13281 //===========================================================================================================================
13282 // _DotLocalSubtestCreate
13283 //===========================================================================================================================
13285 static OSStatus
_DotLocalSubtestCreate( DotLocalSubtest
**outSubtest
)
13288 DotLocalSubtest
* obj
;
13290 obj
= (DotLocalSubtest
*) calloc( 1, sizeof( *obj
) );
13291 require_action( obj
, exit
, err
= kNoMemoryErr
);
13293 obj
->correctResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13294 require_action( obj
->correctResults
, exit
, err
= kNoMemoryErr
);
13296 obj
->duplicateResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13297 require_action( obj
->duplicateResults
, exit
, err
= kNoMemoryErr
);
13299 obj
->unexpectedResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
13300 require_action( obj
->unexpectedResults
, exit
, err
= kNoMemoryErr
);
13307 if( obj
) _DotLocalSubtestFree( obj
);
13311 //===========================================================================================================================
13312 // _DotLocalSubtestFree
13313 //===========================================================================================================================
13315 static void _DotLocalSubtestFree( DotLocalSubtest
*inSubtest
)
13317 ForgetMem( &inSubtest
->queryName
);
13318 ForgetCF( &inSubtest
->correctResults
);
13319 ForgetCF( &inSubtest
->duplicateResults
);
13320 ForgetCF( &inSubtest
->unexpectedResults
);
13324 //===========================================================================================================================
13325 // _DotLocalTestStartSubtest
13326 //===========================================================================================================================
13328 static OSStatus
_DotLocalTestStartSubtest( DotLocalTestContext
*inContext
)
13331 DotLocalSubtest
* subtest
= NULL
;
13332 DNSServiceRef op
= NULL
;
13333 DNSServiceFlags flags
;
13335 err
= _DotLocalSubtestCreate( &subtest
);
13336 require_noerr( err
, exit
);
13338 if( inContext
->state
== kDotLocalTestState_GAIMDNSOnly
)
13340 ASPrintF( &subtest
->queryName
, "%s-2.local.", inContext
->labelStr
);
13341 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13343 subtest
->hasMDNSv4
= subtest
->needMDNSv4
= true;
13344 subtest
->hasMDNSv6
= subtest
->needMDNSv6
= true;
13346 subtest
->addrMDNSv4
= htonl( 0x00000201 ); // 0.0.2.1
13347 memcpy( subtest
->addrMDNSv6
, kMDNSReplierBaseAddrV6
, 16 ); // 2001:db8:2::2:1
13348 subtest
->addrMDNSv6
[ 13 ] = 2;
13349 subtest
->addrMDNSv6
[ 15 ] = 1;
13351 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAIMDNSOnly
;
13354 else if( inContext
->state
== kDotLocalTestState_GAIDNSOnly
)
13356 ASPrintF( &subtest
->queryName
, "tag-dns-only.%s.local.", inContext
->labelStr
);
13357 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13359 subtest
->hasDNSv4
= subtest
->needDNSv4
= true;
13360 subtest
->hasDNSv6
= subtest
->needDNSv6
= true;
13362 subtest
->addrDNSv4
= htonl( kDNSServerBaseAddrV4
+ 1 ); // 203.0.113.1
13363 memcpy( subtest
->addrDNSv6
, kDNSServerBaseAddrV6
, 16 ); // 2001:db8:1::1
13364 subtest
->addrDNSv6
[ 15 ] = 1;
13366 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAIDNSOnly
;
13369 else if( inContext
->state
== kDotLocalTestState_GAIBoth
)
13371 ASPrintF( &subtest
->queryName
, "%s.local.", inContext
->labelStr
);
13372 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13374 subtest
->hasDNSv4
= subtest
->needDNSv4
= true;
13375 subtest
->hasDNSv6
= subtest
->needDNSv6
= true;
13376 subtest
->hasMDNSv4
= subtest
->needMDNSv4
= true;
13377 subtest
->hasMDNSv6
= subtest
->needMDNSv6
= true;
13379 subtest
->addrDNSv4
= htonl( kDNSServerBaseAddrV4
+ 1 ); // 203.0.113.1
13380 memcpy( subtest
->addrDNSv6
, kDNSServerBaseAddrV6
, 16 ); // 2001:db8:1::1
13381 subtest
->addrDNSv6
[ 15 ] = 1;
13383 subtest
->addrMDNSv4
= htonl( 0x00000101 ); // 0.0.1.1
13384 memcpy( subtest
->addrMDNSv6
, kMDNSReplierBaseAddrV6
, 16 ); // 2001:db8:2::1:1
13385 subtest
->addrMDNSv6
[ 13 ] = 1;
13386 subtest
->addrMDNSv6
[ 15 ] = 1;
13388 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAIBoth
;
13391 else if( inContext
->state
== kDotLocalTestState_GAINeither
)
13393 ASPrintF( &subtest
->queryName
, "doesnotexit-%s.local.", inContext
->labelStr
);
13394 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13396 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAINeither
;
13399 else if( inContext
->state
== kDotLocalTestState_GAINoSuchRecord
)
13401 ASPrintF( &subtest
->queryName
, "doesnotexit-dns.%s.local.", inContext
->labelStr
);
13402 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13404 subtest
->hasDNSv4
= subtest
->needDNSv4
= true;
13405 subtest
->hasDNSv6
= subtest
->needDNSv6
= true;
13406 subtest
->testDesc
= kDotLocalTestSubtestDesc_GAINoSuchRecord
;
13409 else if( inContext
->state
== kDotLocalTestState_QuerySRV
)
13411 ASPrintF( &subtest
->queryName
, "_http._tcp.srv-%u-%u-%u.%s%s.local.",
13412 kDotLocalTestSRV_Priority
, kDotLocalTestSRV_Weight
, kDotLocalTestSRV_Port
, kDotLocalTestSRV_TargetStr
,
13413 inContext
->labelStr
);
13414 require_action_quiet( subtest
->queryName
, exit
, err
= kNoMemoryErr
);
13416 subtest
->needSRV
= true;
13417 subtest
->testDesc
= kDotLocalTestSubtestDesc_QuerySRV
;
13426 // Start new operation.
13428 flags
= kDNSServiceFlagsShareConnection
| kDNSServiceFlagsReturnIntermediates
;
13429 #if( TARGET_OS_WATCH )
13430 flags
|= kDNSServiceFlagsPathEvaluationDone
;
13433 subtest
->startTime
= NanoTimeGetCurrent();
13434 subtest
->endTime
= kNanoTime_Invalid
;
13436 if( inContext
->state
== kDotLocalTestState_QuerySRV
)
13438 op
= inContext
->connection
;
13439 err
= DNSServiceQueryRecord( &op
, flags
, kDNSServiceInterfaceIndexAny
, subtest
->queryName
,
13440 kDNSServiceType_SRV
, kDNSServiceClass_IN
, _DotLocalTestQueryRecordCallback
, inContext
);
13441 require_noerr( err
, exit
);
13445 op
= inContext
->connection
;
13446 err
= DNSServiceGetAddrInfo( &op
, flags
, kDNSServiceInterfaceIndexAny
,
13447 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, subtest
->queryName
, _DotLocalTestGAICallback
, inContext
);
13448 require_noerr( err
, exit
);
13453 check( !inContext
->timer
);
13454 err
= DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestSubTestDurationSecs
),
13455 INT64_C_safe( kDotLocalTestSubTestDurationSecs
) * kNanosecondsPerSecond
/ 10, dispatch_get_main_queue(),
13456 _DotLocalTestTimerHandler
, inContext
, &inContext
->timer
);
13457 require_noerr( err
, exit
);
13458 dispatch_resume( inContext
->timer
);
13460 check( !inContext
->op
);
13461 inContext
->op
= op
;
13464 check( !inContext
->subtest
);
13465 inContext
->subtest
= subtest
;
13469 if( subtest
) _DotLocalSubtestFree( subtest
);
13470 if( op
) DNSServiceRefDeallocate( op
);
13474 //===========================================================================================================================
13475 // _DotLocalTestFinalizeSubtest
13476 //===========================================================================================================================
13478 #define kDotLocalTestReportKey_StartTime CFSTR( "startTime" ) // String.
13479 #define kDotLocalTestReportKey_EndTime CFSTR( "endTime" ) // String.
13480 #define kDotLocalTestReportKey_Success CFSTR( "success" ) // Boolean.
13481 #define kDotLocalTestReportKey_MDNSReplierCmd CFSTR( "replierCmd" ) // String.
13482 #define kDotLocalTestReportKey_DNSServerCmd CFSTR( "serverCmd" ) // String.
13483 #define kDotLocalTestReportKey_GetAddrInfoTests CFSTR( "testsGAI" ) // Array of Dictionaries.
13484 #define kDotLocalTestReportKey_QuerySRVTests CFSTR( "testsQuerySRV" ) // Array of Dictionaries.
13485 #define kDotLocalTestReportKey_Description CFSTR( "description" ) // String.
13486 #define kDotLocalTestReportKey_QueryName CFSTR( "queryName" ) // String.
13487 #define kDotLocalTestReportKey_Error CFSTR( "error" ) // Integer.
13488 #define kDotLocalTestReportKey_Results CFSTR( "results" ) // Dictionary of Arrays.
13489 #define kDotLocalTestReportKey_CorrectResults CFSTR( "correct" ) // Array of Strings
13490 #define kDotLocalTestReportKey_DuplicateResults CFSTR( "duplicates" ) // Array of Strings.
13491 #define kDotLocalTestReportKey_UnexpectedResults CFSTR( "unexpected" ) // Array of Strings.
13492 #define kDotLocalTestReportKey_MissingResults CFSTR( "missing" ) // Array of Strings.
13494 static OSStatus
_DotLocalTestFinalizeSubtest( DotLocalTestContext
*inContext
)
13497 DotLocalSubtest
* subtest
;
13498 CFMutableDictionaryRef reportDict
;
13499 CFMutableDictionaryRef resultsDict
;
13500 CFMutableArrayRef missingResults
, reportArray
;
13501 char startTimeStr
[ 32 ];
13502 char endTimeStr
[ 32 ];
13504 subtest
= inContext
->subtest
;
13505 inContext
->subtest
= NULL
;
13507 subtest
->endTime
= NanoTimeGetCurrent();
13508 _NanoTime64ToDateString( subtest
->startTime
, startTimeStr
, sizeof( startTimeStr
) );
13509 _NanoTime64ToDateString( subtest
->endTime
, endTimeStr
, sizeof( endTimeStr
) );
13512 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &reportDict
,
13514 "%kO=%s" // startTime
13515 "%kO=%s" // endTime
13516 "%kO=%s" // queryName
13517 "%kO=%s" // description
13518 "%kO={%@}" // results
13520 kDotLocalTestReportKey_StartTime
, startTimeStr
,
13521 kDotLocalTestReportKey_EndTime
, endTimeStr
,
13522 kDotLocalTestReportKey_QueryName
, subtest
->queryName
,
13523 kDotLocalTestReportKey_Description
, subtest
->testDesc
,
13524 kDotLocalTestReportKey_Results
, &resultsDict
);
13525 require_noerr( err
, exit
);
13527 missingResults
= NULL
;
13528 switch( inContext
->state
)
13530 case kDotLocalTestState_GAIMDNSOnly
:
13531 case kDotLocalTestState_GAIDNSOnly
:
13532 case kDotLocalTestState_GAIBoth
:
13533 case kDotLocalTestState_GAINeither
:
13534 if( subtest
->needDNSv4
|| subtest
->needDNSv6
|| subtest
->needMDNSv4
|| subtest
->needMDNSv6
)
13536 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &missingResults
,
13538 "%.4a" // Expected DNS IPv4 address
13539 "%.16a" // Expected DNS IPv6 address
13540 "%.4a" // Expected MDNS IPv4 address
13541 "%.16a" // Expected MDNS IPv6 address
13543 subtest
->needDNSv4
? &subtest
->addrDNSv4
: NULL
,
13544 subtest
->needDNSv6
? subtest
->addrDNSv6
: NULL
,
13545 subtest
->needMDNSv4
? &subtest
->addrMDNSv4
: NULL
,
13546 subtest
->needMDNSv6
? subtest
->addrMDNSv6
: NULL
);
13547 require_noerr( err
, exit
);
13551 case kDotLocalTestState_QuerySRV
:
13552 if( subtest
->needSRV
)
13554 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &missingResults
,
13556 "%s" // Expected SRV record data as a string.
13558 kDotLocalTestSRV_ResultStr
);
13559 require_noerr( err
, exit
);
13563 case kDotLocalTestState_GAINoSuchRecord
:
13564 if( subtest
->needDNSv4
|| subtest
->needDNSv6
)
13566 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &missingResults
,
13568 "%s" // No Such Record (A)
13569 "%s" // No Such Record (AAAA)
13571 subtest
->needDNSv4
? kNoSuchRecordAStr
: NULL
,
13572 subtest
->needDNSv6
? kNoSuchRecordAAAAStr
: NULL
);
13573 require_noerr( err
, exit
);
13582 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_CorrectResults
, subtest
->correctResults
);
13584 if( missingResults
)
13586 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_MissingResults
, missingResults
);
13587 ForgetCF( &missingResults
);
13588 if( !subtest
->error
) subtest
->error
= kNotFoundErr
;
13591 if( CFArrayGetCount( subtest
->unexpectedResults
) > 0 )
13593 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_UnexpectedResults
, subtest
->unexpectedResults
);
13594 if( !subtest
->error
) subtest
->error
= kUnexpectedErr
;
13597 if( CFArrayGetCount( subtest
->duplicateResults
) > 0 )
13599 CFDictionarySetValue( resultsDict
, kDotLocalTestReportKey_DuplicateResults
, subtest
->duplicateResults
);
13600 if( !subtest
->error
) subtest
->error
= kDuplicateErr
;
13603 if( subtest
->error
) inContext
->testFailed
= true;
13604 err
= CFDictionarySetInt64( reportDict
, kDotLocalTestReportKey_Error
, subtest
->error
);
13605 require_noerr( err
, exit
);
13607 reportArray
= ( inContext
->state
== kDotLocalTestState_QuerySRV
) ? inContext
->reportsQuerySRV
: inContext
->reportsGAI
;
13608 CFArrayAppendValue( reportArray
, reportDict
);
13611 _DotLocalSubtestFree( subtest
);
13612 CFReleaseNullSafe( reportDict
);
13616 //===========================================================================================================================
13617 // _DotLocalTestFinalizeAndExit
13618 //===========================================================================================================================
13620 static void _DotLocalTestFinalizeAndExit( DotLocalTestContext
*inContext
)
13623 CFPropertyListRef plist
;
13624 char startTimeStr
[ 32 ];
13625 char endTimeStr
[ 32 ];
13627 check( !inContext
->subtest
);
13628 inContext
->endTime
= NanoTimeGetCurrent();
13630 if( inContext
->replierPID
!= -1 )
13632 kill( inContext
->replierPID
, SIGTERM
);
13633 inContext
->replierPID
= -1;
13635 if( inContext
->serverPID
!= -1 )
13637 kill( inContext
->serverPID
, SIGTERM
);
13638 inContext
->serverPID
= -1;
13640 err
= DNSServiceRemoveRecord( inContext
->connection
, inContext
->localSOARef
, 0 );
13641 require_noerr( err
, exit
);
13643 _NanoTime64ToDateString( inContext
->startTime
, startTimeStr
, sizeof( startTimeStr
) );
13644 _NanoTime64ToDateString( inContext
->endTime
, endTimeStr
, sizeof( endTimeStr
) );
13646 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
13648 "%kO=%s" // startTime
13649 "%kO=%s" // endTime
13650 "%kO=%O" // testsGAI
13651 "%kO=%O" // testsQuerySRV
13652 "%kO=%b" // success
13653 "%kO=%s" // replierCmd
13654 "%kO=%s" // serverCmd
13656 kDotLocalTestReportKey_StartTime
, startTimeStr
,
13657 kDotLocalTestReportKey_EndTime
, endTimeStr
,
13658 kDotLocalTestReportKey_GetAddrInfoTests
, inContext
->reportsGAI
,
13659 kDotLocalTestReportKey_QuerySRVTests
, inContext
->reportsQuerySRV
,
13660 kDotLocalTestReportKey_Success
, inContext
->testFailed
? false : true,
13661 kDotLocalTestReportKey_MDNSReplierCmd
, inContext
->replierCmd
,
13662 kDotLocalTestReportKey_DNSServerCmd
, inContext
->serverCmd
);
13663 require_noerr( err
, exit
);
13665 ForgetCF( &inContext
->reportsGAI
);
13666 ForgetCF( &inContext
->reportsQuerySRV
);
13668 err
= OutputPropertyList( plist
, inContext
->outputFormat
, inContext
->appendNewline
, inContext
->outputFilePath
);
13669 CFRelease( plist
);
13670 require_noerr( err
, exit
);
13672 exit( inContext
->testFailed
? 2 : 0 );
13675 ErrQuit( 1, "error: %#m\n", err
);
13678 //===========================================================================================================================
13679 // _DotLocalTestProbeQueryRecordCallback
13680 //===========================================================================================================================
13682 static void DNSSD_API
13683 _DotLocalTestProbeQueryRecordCallback(
13684 DNSServiceRef inSDRef
,
13685 DNSServiceFlags inFlags
,
13686 uint32_t inInterfaceIndex
,
13687 DNSServiceErrorType inError
,
13688 const char * inFullName
,
13691 uint16_t inRDataLen
,
13692 const void * inRDataPtr
,
13696 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13698 Unused( inInterfaceIndex
);
13699 Unused( inFullName
);
13702 Unused( inRDataLen
);
13703 Unused( inRDataPtr
);
13706 check( context
->state
== kDotLocalTestState_Preparing
);
13708 require_quiet( ( inFlags
& kDNSServiceFlagsAdd
) && !inError
, exit
);
13710 if( inSDRef
== context
->op
)
13712 DNSServiceForget( &context
->op
);
13713 context
->serverIsReady
= true;
13715 else if( inSDRef
== context
->op2
)
13717 DNSServiceForget( &context
->op2
);
13718 context
->replierIsReady
= true;
13721 if( context
->registeredSOA
&& context
->serverIsReady
&& context
->replierIsReady
)
13723 _DotLocalTestStateMachine( context
);
13730 //===========================================================================================================================
13731 // _DotLocalTestRegisterRecordCallback
13732 //===========================================================================================================================
13734 static void DNSSD_API
13735 _DotLocalTestRegisterRecordCallback(
13736 DNSServiceRef inSDRef
,
13737 DNSRecordRef inRecordRef
,
13738 DNSServiceFlags inFlags
,
13739 DNSServiceErrorType inError
,
13742 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13745 Unused( inRecordRef
);
13748 if( inError
) ErrQuit( 1, "error: local. SOA record registration failed: %#m\n", inError
);
13750 if( !context
->registeredSOA
)
13752 context
->registeredSOA
= true;
13753 if( context
->serverIsReady
&& context
->replierIsReady
) _DotLocalTestStateMachine( context
);
13757 //===========================================================================================================================
13758 // _DotLocalTestTimerHandler
13759 //===========================================================================================================================
13761 static void _DotLocalTestTimerHandler( void *inContext
)
13763 _DotLocalTestStateMachine( (DotLocalTestContext
*) inContext
);
13766 //===========================================================================================================================
13767 // _DotLocalTestGAICallback
13768 //===========================================================================================================================
13770 static void DNSSD_API
13771 _DotLocalTestGAICallback(
13772 DNSServiceRef inSDRef
,
13773 DNSServiceFlags inFlags
,
13774 uint32_t inInterfaceIndex
,
13775 DNSServiceErrorType inError
,
13776 const char * inHostname
,
13777 const struct sockaddr
* inSockAddr
,
13782 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13783 DotLocalSubtest
* const subtest
= context
->subtest
;
13784 const sockaddr_ip
* const sip
= (const sockaddr_ip
*) inSockAddr
;
13787 Unused( inInterfaceIndex
);
13788 Unused( inHostname
);
13791 require_action_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
, err
= kFlagErr
);
13792 require_action_quiet( ( sip
->sa
.sa_family
== AF_INET
) || ( sip
->sa
.sa_family
== AF_INET6
), exit
, err
= kTypeErr
);
13794 if( context
->state
== kDotLocalTestState_GAINoSuchRecord
)
13796 if( inError
== kDNSServiceErr_NoSuchRecord
)
13798 CFMutableArrayRef array
= NULL
;
13799 const char * noSuchRecordStr
;
13801 if( sip
->sa
.sa_family
== AF_INET
)
13803 array
= subtest
->needDNSv4
? subtest
->correctResults
: subtest
->duplicateResults
;
13804 subtest
->needDNSv4
= false;
13806 noSuchRecordStr
= kNoSuchRecordAStr
;
13810 array
= subtest
->needDNSv6
? subtest
->correctResults
: subtest
->duplicateResults
;
13811 subtest
->needDNSv6
= false;
13813 noSuchRecordStr
= kNoSuchRecordAAAAStr
;
13815 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, array
, "%s", noSuchRecordStr
);
13816 require_noerr( err
, fatal
);
13818 else if( !inError
)
13820 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, subtest
->unexpectedResults
, "%##a", sip
);
13821 require_noerr( err
, fatal
);
13833 CFMutableArrayRef array
= NULL
;
13835 if( sip
->sa
.sa_family
== AF_INET
)
13837 const uint32_t addrV4
= sip
->v4
.sin_addr
.s_addr
;
13839 if( subtest
->hasDNSv4
&& ( addrV4
== subtest
->addrDNSv4
) )
13841 array
= subtest
->needDNSv4
? subtest
->correctResults
: subtest
->duplicateResults
;
13842 subtest
->needDNSv4
= false;
13844 else if( subtest
->hasMDNSv4
&& ( addrV4
== subtest
->addrMDNSv4
) )
13846 array
= subtest
->needMDNSv4
? subtest
->correctResults
: subtest
->duplicateResults
;
13847 subtest
->needMDNSv4
= false;
13852 const uint8_t * const addrV6
= sip
->v6
.sin6_addr
.s6_addr
;
13854 if( subtest
->hasDNSv6
&& ( memcmp( addrV6
, subtest
->addrDNSv6
, 16 ) == 0 ) )
13856 array
= subtest
->needDNSv6
? subtest
->correctResults
: subtest
->duplicateResults
;
13857 subtest
->needDNSv6
= false;
13859 else if( subtest
->hasMDNSv6
&& ( memcmp( addrV6
, subtest
->addrMDNSv6
, 16 ) == 0 ) )
13861 array
= subtest
->needMDNSv6
? subtest
->correctResults
: subtest
->duplicateResults
;
13862 subtest
->needMDNSv6
= false;
13865 if( !array
) array
= subtest
->unexpectedResults
;
13866 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, array
, "%##a", sip
);
13867 require_noerr( err
, fatal
);
13869 else if( inError
== kDNSServiceErr_NoSuchRecord
)
13871 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, subtest
->unexpectedResults
, "%s",
13872 ( sip
->sa
.sa_family
== AF_INET
) ? kNoSuchRecordAStr
: kNoSuchRecordAAAAStr
);
13873 require_noerr( err
, fatal
);
13885 subtest
->error
= err
;
13886 _DotLocalTestStateMachine( context
);
13891 ErrQuit( 1, "error: %#m\n", err
);
13894 //===========================================================================================================================
13895 // _DotLocalTestQueryRecordCallback
13896 //===========================================================================================================================
13898 static void DNSSD_API
13899 _DotLocalTestQueryRecordCallback(
13900 DNSServiceRef inSDRef
,
13901 DNSServiceFlags inFlags
,
13902 uint32_t inInterfaceIndex
,
13903 DNSServiceErrorType inError
,
13904 const char * inFullName
,
13907 uint16_t inRDataLen
,
13908 const void * inRDataPtr
,
13913 DotLocalTestContext
* const context
= (DotLocalTestContext
*) inContext
;
13914 DotLocalSubtest
* const subtest
= context
->subtest
;
13915 const SRVRecordDataFixedFields
* fields
;
13916 const uint8_t * target
;
13917 const uint8_t * ptr
;
13918 const uint8_t * end
;
13920 unsigned int priority
, weight
, port
;
13921 CFMutableArrayRef array
;
13924 Unused( inInterfaceIndex
);
13925 Unused( inFullName
);
13928 check( context
->state
== kDotLocalTestState_QuerySRV
);
13931 require_noerr_quiet( err
, exit
);
13932 require_action_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
, err
= kFlagErr
);
13933 require_action_quiet( ( inType
== kDNSServiceType_SRV
) && ( inClass
== kDNSServiceClass_IN
), exit
, err
= kTypeErr
);
13934 require_action_quiet( inRDataLen
> sizeof( SRVRecordDataFixedFields
), exit
, err
= kSizeErr
);
13936 fields
= (const SRVRecordDataFixedFields
*) inRDataPtr
;
13937 SRVRecordDataFixedFieldsGet( fields
, &priority
, &weight
, &port
);
13938 target
= (const uint8_t *) &fields
[ 1 ];
13939 end
= ( (const uint8_t *) inRDataPtr
) + inRDataLen
;
13940 for( ptr
= target
; ( ptr
< end
) && ( *ptr
!= 0 ); ptr
+= ( 1 + *ptr
) ) {}
13942 if( ( priority
== kDotLocalTestSRV_Priority
) &&
13943 ( weight
== kDotLocalTestSRV_Weight
) &&
13944 ( port
== kDotLocalTestSRV_Port
) &&
13945 ( ptr
< end
) && DomainNameEqual( target
, kDotLocalTestSRV_TargetName
) )
13947 array
= subtest
->needSRV
? subtest
->correctResults
: subtest
->duplicateResults
;
13948 subtest
->needSRV
= false;
13952 array
= subtest
->unexpectedResults
;
13956 DNSRecordDataToString( inRDataPtr
, inRDataLen
, kDNSServiceType_SRV
, NULL
, 0, &rdataStr
);
13959 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, inRDataLen
);
13960 require_action( rdataStr
, fatal
, err
= kNoMemoryErr
);
13963 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, array
, "%s", rdataStr
);
13965 require_noerr( err
, fatal
);
13970 subtest
->error
= err
;
13971 _DotLocalTestStateMachine( context
);
13976 ErrQuit( 1, "error: %#m\n", err
);
13979 //===========================================================================================================================
13980 // ProbeConflictTestCmd
13981 //===========================================================================================================================
13983 #define kProbeConflictTestService_DefaultName "name"
13984 #define kProbeConflictTestService_Port 60000
13986 #define kProbeConflictTestTXTPtr "\x13" "PROBE-CONFLICT-TEST"
13987 #define kProbeConflictTestTXTLen sizeof_string( kProbeConflictTestTXTPtr )
13991 const char * description
;
13992 const char * program
;
13993 Boolean expectsRename
;
13995 } ProbeConflictTestCase
;
13997 #define kPCTProgPreWait "wait 1000;" // Wait 1 second before sending gratuitous response.
13998 #define kPCTProgPostWait "wait 8000;" // Wait 8 seconds after sending gratuitous response.
13999 // This allows ~2.75 seconds for probing and ~5 seconds for a rename.
14001 static const ProbeConflictTestCase kProbeConflictTestCases
[] =
14005 { "No probe conflicts.", kPCTProgPreWait
"probes n-n-n;" "send;" kPCTProgPostWait
, false },
14007 // One multicast probe conflict
14009 { "One multicast probe conflict (1).", kPCTProgPreWait
"probes m;" "send;" kPCTProgPostWait
, false },
14010 { "One multicast probe conflict (2).", kPCTProgPreWait
"probes n-m;" "send;" kPCTProgPostWait
, false },
14011 { "One multicast probe conflict (3).", kPCTProgPreWait
"probes n-n-m;" "send;" kPCTProgPostWait
, false },
14013 // One unicast probe conflict
14015 { "One unicast probe conflict (1).", kPCTProgPreWait
"probes u;" "send;" kPCTProgPostWait
, true },
14016 { "One unicast probe conflict (2).", kPCTProgPreWait
"probes n-u;" "send;" kPCTProgPostWait
, true },
14017 { "One unicast probe conflict (3).", kPCTProgPreWait
"probes n-n-u;" "send;" kPCTProgPostWait
, true },
14019 // One multicast and one unicast probe conflict
14021 { "Multicast and unicast probe conflict (1).", kPCTProgPreWait
"probes m-u;" "send;" kPCTProgPostWait
, true },
14022 { "Multicast and unicast probe conflict (2).", kPCTProgPreWait
"probes m-n-u;" "send;" kPCTProgPostWait
, true },
14023 { "Multicast and unicast probe conflict (3).", kPCTProgPreWait
"probes m-n-n-u;" "send;" kPCTProgPostWait
, true },
14024 { "Multicast and unicast probe conflict (4).", kPCTProgPreWait
"probes n-m-u;" "send;" kPCTProgPostWait
, true },
14025 { "Multicast and unicast probe conflict (5).", kPCTProgPreWait
"probes n-m-n-u;" "send;" kPCTProgPostWait
, true },
14026 { "Multicast and unicast probe conflict (6).", kPCTProgPreWait
"probes n-m-n-n-u;" "send;" kPCTProgPostWait
, true },
14027 { "Multicast and unicast probe conflict (7).", kPCTProgPreWait
"probes n-n-m-u;" "send;" kPCTProgPostWait
, true },
14028 { "Multicast and unicast probe conflict (8).", kPCTProgPreWait
"probes n-n-m-n-u;" "send;" kPCTProgPostWait
, true },
14029 { "Multicast and unicast probe conflict (9).", kPCTProgPreWait
"probes n-n-m-n-n-u;" "send;" kPCTProgPostWait
, true },
14031 // Two multicast probe conflicts
14033 { "Two multicast probe conflicts (1).", kPCTProgPreWait
"probes m-m;" "send;" kPCTProgPostWait
, true },
14034 { "Two multicast probe conflicts (2).", kPCTProgPreWait
"probes m-n-m;" "send;" kPCTProgPostWait
, true },
14035 { "Two multicast probe conflicts (3).", kPCTProgPreWait
"probes m-n-n-m;" "send;" kPCTProgPostWait
, true },
14036 { "Two multicast probe conflicts (4).", kPCTProgPreWait
"probes n-m-m;" "send;" kPCTProgPostWait
, true },
14037 { "Two multicast probe conflicts (5).", kPCTProgPreWait
"probes n-m-n-m-n;" "send;" kPCTProgPostWait
, true },
14038 { "Two multicast probe conflicts (6).", kPCTProgPreWait
"probes n-m-n-n-m;" "send;" kPCTProgPostWait
, true },
14039 { "Two multicast probe conflicts (7).", kPCTProgPreWait
"probes n-n-m-m;" "send;" kPCTProgPostWait
, true },
14040 { "Two multicast probe conflicts (8).", kPCTProgPreWait
"probes n-n-m-n-m;" "send;" kPCTProgPostWait
, true },
14041 { "Two multicast probe conflicts (9).", kPCTProgPreWait
"probes n-n-m-n-n-m;" "send;" kPCTProgPostWait
, true },
14044 #define kProbeConflictTestCaseCount countof( kProbeConflictTestCases )
14048 DNSServiceRef registration
; // Test service registration.
14049 NanoTime64 testStartTime
; // Test's start time.
14050 NanoTime64 startTime
; // Current test case's start time.
14051 MDNSColliderRef collider
; // mDNS collider object.
14052 CFMutableArrayRef results
; // Array of test case results.
14053 char * serviceName
; // Test service's instance name as a string. (malloced)
14054 char * serviceType
; // Test service's service type as a string. (malloced)
14055 uint8_t * recordName
; // FQDN of collider's record (same as test service's SRV+TXT records). (malloced)
14056 unsigned int testCaseIndex
; // Index of the current test case.
14057 uint32_t ifIndex
; // Index of the interface that the collider is to operate on.
14058 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout. (malloced)
14059 OutputFormatType outputFormat
; // Format of test report output.
14060 Boolean appendNewline
; // True if a newline character should be appended to JSON output.
14061 Boolean registered
; // True if the test service instance is currently registered.
14062 Boolean testFailed
; // True if at least one test case failed.
14064 } ProbeConflictTestContext
;
14066 static void DNSSD_API
14067 _ProbeConflictTestRegisterCallback(
14068 DNSServiceRef inSDRef
,
14069 DNSServiceFlags inFlags
,
14070 DNSServiceErrorType inError
,
14071 const char * inName
,
14072 const char * inType
,
14073 const char * inDomain
,
14074 void * inContext
);
14075 static void _ProbeConflictTestColliderStopHandler( void *inContext
, OSStatus inError
);
14076 static OSStatus
_ProbeConflictTestStartNextTest( ProbeConflictTestContext
*inContext
);
14077 static OSStatus
_ProbeConflictTestStopCurrentTest( ProbeConflictTestContext
*inContext
, Boolean inRenamed
);
14078 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext
*inContext
) ATTRIBUTE_NORETURN
;
14080 static void ProbeConflictTestCmd( void )
14083 ProbeConflictTestContext
* context
;
14084 const char * serviceName
;
14087 context
= (ProbeConflictTestContext
*) calloc( 1, sizeof( *context
) );
14088 require_action( context
, exit
, err
= kNoMemoryErr
);
14090 if( gProbeConflictTest_Interface
)
14092 err
= InterfaceIndexFromArgString( gProbeConflictTest_Interface
, &context
->ifIndex
);
14093 require_noerr_quiet( err
, exit
);
14097 err
= GetAnyMDNSInterface( NULL
, &context
->ifIndex
);
14098 require_noerr_quiet( err
, exit
);
14101 if( gProbeConflictTest_OutputFilePath
)
14103 context
->outputFilePath
= strdup( gProbeConflictTest_OutputFilePath
);
14104 require_action( context
->outputFilePath
, exit
, err
= kNoMemoryErr
);
14107 context
->appendNewline
= gProbeConflictTest_OutputAppendNewline
? true : false;
14108 context
->outputFormat
= (OutputFormatType
) CLIArgToValue( "format", gProbeConflictTest_OutputFormat
, &err
,
14109 kOutputFormatStr_JSON
, kOutputFormatType_JSON
,
14110 kOutputFormatStr_XML
, kOutputFormatType_XML
,
14111 kOutputFormatStr_Binary
, kOutputFormatType_Binary
,
14113 require_noerr_quiet( err
, exit
);
14115 context
->results
= CFArrayCreateMutable( NULL
, kProbeConflictTestCaseCount
, &kCFTypeArrayCallBacks
);
14116 require_action( context
->results
, exit
, err
= kNoMemoryErr
);
14118 serviceName
= gProbeConflictTest_UseComputerName
? NULL
: kProbeConflictTestService_DefaultName
;
14120 ASPrintF( &context
->serviceType
, "_pctest-%s._udp",
14121 _RandomStringExact( kLowerAlphaNumericCharSet
, kLowerAlphaNumericCharSetSize
, sizeof( tag
) - 1, tag
) );
14122 require_action( context
->serviceType
, exit
, err
= kNoMemoryErr
);
14124 context
->testStartTime
= NanoTimeGetCurrent();
14125 err
= DNSServiceRegister( &context
->registration
, 0, context
->ifIndex
, serviceName
, context
->serviceType
, "local.",
14126 NULL
, htons( kProbeConflictTestService_Port
), 0, NULL
, _ProbeConflictTestRegisterCallback
, context
);
14127 require_noerr( err
, exit
);
14129 err
= DNSServiceSetDispatchQueue( context
->registration
, dispatch_get_main_queue() );
14130 require_noerr( err
, exit
);
14138 //===========================================================================================================================
14139 // _ProbeConflictTestRegisterCallback
14140 //===========================================================================================================================
14142 static void DNSSD_API
14143 _ProbeConflictTestRegisterCallback(
14144 DNSServiceRef inSDRef
,
14145 DNSServiceFlags inFlags
,
14146 DNSServiceErrorType inError
,
14147 const char * inName
,
14148 const char * inType
,
14149 const char * inDomain
,
14153 ProbeConflictTestContext
* const context
= (ProbeConflictTestContext
*) inContext
;
14157 Unused( inDomain
);
14160 require_noerr( err
, exit
);
14162 if( !context
->registered
)
14164 if( inFlags
& kDNSServiceFlagsAdd
)
14167 size_t recordNameLen
;
14169 uint8_t name
[ kDomainNameLengthMax
];
14171 context
->registered
= true;
14173 FreeNullSafe( context
->serviceName
);
14174 context
->serviceName
= strdup( inName
);
14175 require_action( context
->serviceName
, exit
, err
= kNoMemoryErr
);
14177 err
= DomainNameFromString( name
, context
->serviceName
, NULL
);
14178 require_noerr( err
, exit
);
14180 err
= DomainNameAppendString( name
, context
->serviceType
, NULL
);
14181 require_noerr( err
, exit
);
14183 err
= DomainNameAppendString( name
, "local", NULL
);
14184 require_noerr( err
, exit
);
14186 ForgetMem( &context
->recordName
);
14187 err
= DomainNameDup( name
, &context
->recordName
, &recordNameLen
);
14188 require_noerr( err
, exit
);
14189 require_fatal( recordNameLen
> 0, "Record name length is zero." ); // Prevents dubious static analyzer warning.
14191 // Make the first label all caps so that it's easier to spot in system logs.
14193 ptr
= context
->recordName
;
14194 for( len
= *ptr
++; len
> 0; --len
, ++ptr
) *ptr
= (uint8_t) toupper_safe( *ptr
);
14196 err
= _ProbeConflictTestStartNextTest( context
);
14197 require_noerr( err
, exit
);
14202 if( !( inFlags
& kDNSServiceFlagsAdd
) )
14204 context
->registered
= false;
14205 err
= _ProbeConflictTestStopCurrentTest( context
, true );
14206 require_noerr( err
, exit
);
14212 if( err
) exit( 1 );
14215 //===========================================================================================================================
14216 // _ProbeConflictTestColliderStopHandler
14217 //===========================================================================================================================
14219 static void _ProbeConflictTestColliderStopHandler( void *inContext
, OSStatus inError
)
14222 ProbeConflictTestContext
* const context
= (ProbeConflictTestContext
*) inContext
;
14225 require_noerr_quiet( err
, exit
);
14227 ForgetCF( &context
->collider
);
14229 err
= _ProbeConflictTestStopCurrentTest( context
, false );
14230 require_noerr( err
, exit
);
14232 err
= _ProbeConflictTestStartNextTest( context
);
14233 require_noerr( err
, exit
);
14236 if( err
) exit( 1 );
14239 //===========================================================================================================================
14240 // _ProbeConflictTestStartNextTest
14241 //===========================================================================================================================
14243 static OSStatus
_ProbeConflictTestStartNextTest( ProbeConflictTestContext
*inContext
)
14246 const ProbeConflictTestCase
* testCase
;
14248 check( !inContext
->collider
);
14250 if( inContext
->testCaseIndex
< kProbeConflictTestCaseCount
)
14252 testCase
= &kProbeConflictTestCases
[ inContext
->testCaseIndex
];
14256 _ProbeConflictTestFinalizeAndExit( inContext
);
14259 err
= MDNSColliderCreate( dispatch_get_main_queue(), &inContext
->collider
);
14260 require_noerr( err
, exit
);
14262 err
= MDNSColliderSetProgram( inContext
->collider
, testCase
->program
);
14263 require_noerr( err
, exit
);
14265 err
= MDNSColliderSetRecord( inContext
->collider
, inContext
->recordName
, kDNSServiceType_TXT
,
14266 kProbeConflictTestTXTPtr
, kProbeConflictTestTXTLen
);
14267 require_noerr( err
, exit
);
14269 MDNSColliderSetProtocols( inContext
->collider
, kMDNSColliderProtocol_IPv4
);
14270 MDNSColliderSetInterfaceIndex( inContext
->collider
, inContext
->ifIndex
);
14271 MDNSColliderSetStopHandler( inContext
->collider
, _ProbeConflictTestColliderStopHandler
, inContext
);
14273 inContext
->startTime
= NanoTimeGetCurrent();
14274 err
= MDNSColliderStart( inContext
->collider
);
14275 require_noerr( err
, exit
);
14281 //===========================================================================================================================
14282 // _ProbeConflictTestStopCurrentTest
14283 //===========================================================================================================================
14285 #define kProbeConflictTestCaseResultKey_Description CFSTR( "description" )
14286 #define kProbeConflictTestCaseResultKey_StartTime CFSTR( "startTime" )
14287 #define kProbeConflictTestCaseResultKey_EndTime CFSTR( "endTime" )
14288 #define kProbeConflictTestCaseResultKey_ExpectedRename CFSTR( "expectedRename" )
14289 #define kProbeConflictTestCaseResultKey_ServiceName CFSTR( "serviceName" )
14290 #define kProbeConflictTestCaseResultKey_Passed CFSTR( "passed" )
14292 static OSStatus
_ProbeConflictTestStopCurrentTest( ProbeConflictTestContext
*inContext
, Boolean inRenamed
)
14295 const ProbeConflictTestCase
* testCase
;
14296 NanoTime64 endTime
;
14298 char startTimeStr
[ 32 ];
14299 char endTimeStr
[ 32 ];
14301 endTime
= NanoTimeGetCurrent();
14303 if( inContext
->collider
)
14305 MDNSColliderSetStopHandler( inContext
->collider
, NULL
, NULL
);
14306 MDNSColliderStop( inContext
->collider
);
14307 CFRelease( inContext
->collider
);
14308 inContext
->collider
= NULL
;
14311 testCase
= &kProbeConflictTestCases
[ inContext
->testCaseIndex
];
14312 passed
= ( ( testCase
->expectsRename
&& inRenamed
) || ( !testCase
->expectsRename
&& !inRenamed
) ) ? true : false;
14313 if( !passed
) inContext
->testFailed
= true;
14315 _NanoTime64ToDateString( inContext
->startTime
, startTimeStr
, sizeof( startTimeStr
) );
14316 _NanoTime64ToDateString( endTime
, endTimeStr
, sizeof( endTimeStr
) );
14318 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, inContext
->results
,
14320 "%kO=%s" // description
14321 "%kO=%b" // expectedRename
14322 "%kO=%s" // startTime
14323 "%kO=%s" // endTime
14324 "%kO=%s" // serviceName
14327 kProbeConflictTestCaseResultKey_Description
, testCase
->description
,
14328 kProbeConflictTestCaseResultKey_ExpectedRename
, testCase
->expectsRename
,
14329 kProbeConflictTestCaseResultKey_StartTime
, startTimeStr
,
14330 kProbeConflictTestCaseResultKey_EndTime
, endTimeStr
,
14331 kProbeConflictTestCaseResultKey_ServiceName
, inContext
->serviceName
,
14332 kProbeConflictTestCaseResultKey_Passed
, passed
);
14333 require_noerr( err
, exit
);
14335 ++inContext
->testCaseIndex
;
14341 //===========================================================================================================================
14342 // _ProbeConflictTestFinalizeAndExit
14343 //===========================================================================================================================
14345 #define kProbeConflictTestReportKey_StartTime CFSTR( "startTime" )
14346 #define kProbeConflictTestReportKey_EndTime CFSTR( "endTime" )
14347 #define kProbeConflictTestReportKey_ServiceType CFSTR( "serviceType" )
14348 #define kProbeConflictTestReportKey_Results CFSTR( "results" )
14349 #define kProbeConflictTestReportKey_Passed CFSTR( "passed" )
14351 static void _ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext
*inContext
)
14354 CFPropertyListRef plist
;
14355 NanoTime64 endTime
;
14356 char startTimeStr
[ 32 ];
14357 char endTimeStr
[ 32 ];
14359 endTime
= NanoTimeGetCurrent();
14361 check( !inContext
->collider
);
14363 _NanoTime64ToDateString( inContext
->testStartTime
, startTimeStr
, sizeof( startTimeStr
) );
14364 _NanoTime64ToDateString( endTime
, endTimeStr
, sizeof( endTimeStr
) );
14366 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
14368 "%kO=%s" // startTime
14369 "%kO=%s" // endTime
14370 "%kO=%s" // serviceType
14371 "%kO=%O" // results
14374 kProbeConflictTestReportKey_StartTime
, startTimeStr
,
14375 kProbeConflictTestReportKey_EndTime
, endTimeStr
,
14376 kProbeConflictTestReportKey_ServiceType
, inContext
->serviceType
,
14377 kProbeConflictTestReportKey_Results
, inContext
->results
,
14378 kProbeConflictTestReportKey_Passed
, inContext
->testFailed
? false : true );
14379 require_noerr( err
, exit
);
14380 ForgetCF( &inContext
->results
);
14382 err
= OutputPropertyList( plist
, inContext
->outputFormat
, inContext
->appendNewline
, inContext
->outputFilePath
);
14383 CFRelease( plist
);
14384 require_noerr( err
, exit
);
14386 exit( inContext
->testFailed
? 2 : 0 );
14389 ErrQuit( 1, "error: %#m\n", err
);
14392 //===========================================================================================================================
14394 //===========================================================================================================================
14396 #define kSSDPPort 1900
14400 HTTPHeader header
; // HTTP header object for sending and receiving.
14401 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
14402 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
14403 int receiveSecs
; // After send, the amount of time to spend receiving.
14404 uint32_t ifindex
; // Index of the interface over which to send the query.
14405 Boolean useIPv4
; // True if the query should be sent via IPv4 multicast.
14406 Boolean useIPv6
; // True if the query should be sent via IPv6 multicast.
14408 } SSDPDiscoverContext
;
14410 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext
*inContext
);
14411 static void SSDPDiscoverReadHandler( void *inContext
);
14412 static int SocketToPortNumber( SocketRef inSock
);
14413 static OSStatus
WriteSSDPSearchRequest( HTTPHeader
*inHeader
, const void *inHostSA
, int inMX
, const char *inST
);
14415 static void SSDPDiscoverCmd( void )
14418 struct timeval now
;
14419 SSDPDiscoverContext
* context
;
14420 dispatch_source_t signalSource
= NULL
;
14421 SocketRef sockV4
= kInvalidSocketRef
;
14422 SocketRef sockV6
= kInvalidSocketRef
;
14426 // Set up SIGINT handler.
14428 signal( SIGINT
, SIG_IGN
);
14429 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
14430 require_noerr( err
, exit
);
14431 dispatch_resume( signalSource
);
14433 // Check command parameters.
14435 if( gSSDPDiscover_ReceiveSecs
< -1 )
14437 FPrintF( stdout
, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs
);
14444 context
= (SSDPDiscoverContext
*) calloc( 1, sizeof( *context
) );
14445 require_action( context
, exit
, err
= kNoMemoryErr
);
14447 context
->receiveSecs
= gSSDPDiscover_ReceiveSecs
;
14448 context
->useIPv4
= ( gSSDPDiscover_UseIPv4
|| !gSSDPDiscover_UseIPv6
) ? true : false;
14449 context
->useIPv6
= ( gSSDPDiscover_UseIPv6
|| !gSSDPDiscover_UseIPv4
) ? true : false;
14451 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifindex
);
14452 require_noerr_quiet( err
, exit
);
14454 // Set up IPv4 socket.
14456 if( context
->useIPv4
)
14459 err
= UDPClientSocketOpen( AF_INET
, NULL
, 0, -1, &port
, &sockV4
);
14460 require_noerr( err
, exit
);
14462 err
= SocketSetMulticastInterface( sockV4
, NULL
, context
->ifindex
);
14463 require_noerr( err
, exit
);
14465 err
= setsockopt( sockV4
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *) &(uint8_t){ 1 }, (socklen_t
) sizeof( uint8_t ) );
14466 err
= map_socket_noerr_errno( sockV4
, err
);
14467 require_noerr( err
, exit
);
14470 // Set up IPv6 socket.
14472 if( context
->useIPv6
)
14474 err
= UDPClientSocketOpen( AF_INET6
, NULL
, 0, -1, NULL
, &sockV6
);
14475 require_noerr( err
, exit
);
14477 err
= SocketSetMulticastInterface( sockV6
, NULL
, context
->ifindex
);
14478 require_noerr( err
, exit
);
14480 err
= setsockopt( sockV6
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *) &(int){ 1 }, (socklen_t
) sizeof( int ) );
14481 err
= map_socket_noerr_errno( sockV6
, err
);
14482 require_noerr( err
, exit
);
14487 SSDPDiscoverPrintPrologue( context
);
14489 // Send mDNS query message.
14492 if( IsValidSocket( sockV4
) )
14494 struct sockaddr_in mcastAddr4
;
14496 memset( &mcastAddr4
, 0, sizeof( mcastAddr4
) );
14497 SIN_LEN_SET( &mcastAddr4
);
14498 mcastAddr4
.sin_family
= AF_INET
;
14499 mcastAddr4
.sin_port
= htons( kSSDPPort
);
14500 mcastAddr4
.sin_addr
.s_addr
= htonl( 0xEFFFFFFA ); // 239.255.255.250
14502 err
= WriteSSDPSearchRequest( &context
->header
, &mcastAddr4
, gSSDPDiscover_MX
, gSSDPDiscover_ST
);
14503 require_noerr( err
, exit
);
14505 n
= sendto( sockV4
, context
->header
.buf
, context
->header
.len
, 0, (const struct sockaddr
*) &mcastAddr4
,
14506 (socklen_t
) sizeof( mcastAddr4
) );
14507 err
= map_socket_value_errno( sockV4
, n
== (ssize_t
) context
->header
.len
, n
);
14510 FPrintF( stderr
, "*** Failed to send query on IPv4 socket with error %#m\n", err
);
14511 ForgetSocket( &sockV4
);
14515 if( gSSDPDiscover_Verbose
)
14517 gettimeofday( &now
, NULL
);
14518 FPrintF( stdout
, "---\n" );
14519 FPrintF( stdout
, "Send time: %{du:time}\n", &now
);
14520 FPrintF( stdout
, "Source Port: %d\n", SocketToPortNumber( sockV4
) );
14521 FPrintF( stdout
, "Destination: %##a\n", &mcastAddr4
);
14522 FPrintF( stdout
, "Message size: %zu\n", context
->header
.len
);
14523 FPrintF( stdout
, "HTTP header:\n%1{text}", context
->header
.buf
, context
->header
.len
);
14529 if( IsValidSocket( sockV6
) )
14531 struct sockaddr_in6 mcastAddr6
;
14533 memset( &mcastAddr6
, 0, sizeof( mcastAddr6
) );
14534 SIN6_LEN_SET( &mcastAddr6
);
14535 mcastAddr6
.sin6_family
= AF_INET6
;
14536 mcastAddr6
.sin6_port
= htons( kSSDPPort
);
14537 mcastAddr6
.sin6_addr
.s6_addr
[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C
14538 mcastAddr6
.sin6_addr
.s6_addr
[ 1 ] = 0x02;
14539 mcastAddr6
.sin6_addr
.s6_addr
[ 15 ] = 0x0C;
14541 err
= WriteSSDPSearchRequest( &context
->header
, &mcastAddr6
, gSSDPDiscover_MX
, gSSDPDiscover_ST
);
14542 require_noerr( err
, exit
);
14544 n
= sendto( sockV6
, context
->header
.buf
, context
->header
.len
, 0, (const struct sockaddr
*) &mcastAddr6
,
14545 (socklen_t
) sizeof( mcastAddr6
) );
14546 err
= map_socket_value_errno( sockV6
, n
== (ssize_t
) context
->header
.len
, n
);
14549 FPrintF( stderr
, "*** Failed to send query on IPv6 socket with error %#m\n", err
);
14550 ForgetSocket( &sockV6
);
14554 if( gSSDPDiscover_Verbose
)
14556 gettimeofday( &now
, NULL
);
14557 FPrintF( stdout
, "---\n" );
14558 FPrintF( stdout
, "Send time: %{du:time}\n", &now
);
14559 FPrintF( stdout
, "Source Port: %d\n", SocketToPortNumber( sockV6
) );
14560 FPrintF( stdout
, "Destination: %##a\n", &mcastAddr6
);
14561 FPrintF( stdout
, "Message size: %zu\n", context
->header
.len
);
14562 FPrintF( stdout
, "HTTP header:\n%1{text}", context
->header
.buf
, context
->header
.len
);
14567 require_action_quiet( sendCount
> 0, exit
, err
= kUnexpectedErr
);
14569 // If there's no wait period after the send, then exit.
14571 if( context
->receiveSecs
== 0 ) goto exit
;
14573 // Create dispatch read sources for socket(s).
14575 if( IsValidSocket( sockV4
) )
14577 SocketContext
* sockCtx
;
14579 err
= SocketContextCreate( sockV4
, context
, &sockCtx
);
14580 require_noerr( err
, exit
);
14581 sockV4
= kInvalidSocketRef
;
14583 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, SSDPDiscoverReadHandler
, SocketContextCancelHandler
, sockCtx
,
14584 &context
->readSourceV4
);
14585 if( err
) ForgetSocketContext( &sockCtx
);
14586 require_noerr( err
, exit
);
14588 dispatch_resume( context
->readSourceV4
);
14591 if( IsValidSocket( sockV6
) )
14593 SocketContext
* sockCtx
;
14595 err
= SocketContextCreate( sockV6
, context
, &sockCtx
);
14596 require_noerr( err
, exit
);
14597 sockV6
= kInvalidSocketRef
;
14599 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, SSDPDiscoverReadHandler
, SocketContextCancelHandler
, sockCtx
,
14600 &context
->readSourceV6
);
14601 if( err
) ForgetSocketContext( &sockCtx
);
14602 require_noerr( err
, exit
);
14604 dispatch_resume( context
->readSourceV6
);
14607 if( context
->receiveSecs
> 0 )
14609 dispatch_after_f( dispatch_time_seconds( context
->receiveSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
14615 ForgetSocket( &sockV4
);
14616 ForgetSocket( &sockV6
);
14617 dispatch_source_forget( &signalSource
);
14618 if( err
) exit( 1 );
14621 static int SocketToPortNumber( SocketRef inSock
)
14627 len
= (socklen_t
) sizeof( sip
);
14628 err
= getsockname( inSock
, &sip
.sa
, &len
);
14629 err
= map_socket_noerr_errno( inSock
, err
);
14630 check_noerr( err
);
14631 return( err
? -1 : SockAddrGetPort( &sip
) );
14634 static OSStatus
WriteSSDPSearchRequest( HTTPHeader
*inHeader
, const void *inHostSA
, int inMX
, const char *inST
)
14638 err
= HTTPHeader_InitRequest( inHeader
, "M-SEARCH", "*", "HTTP/1.1" );
14639 require_noerr( err
, exit
);
14641 err
= HTTPHeader_SetField( inHeader
, "Host", "%##a", inHostSA
);
14642 require_noerr( err
, exit
);
14644 err
= HTTPHeader_SetField( inHeader
, "ST", "%s", inST
? inST
: "ssdp:all" );
14645 require_noerr( err
, exit
);
14647 err
= HTTPHeader_SetField( inHeader
, "Man", "\"ssdp:discover\"" );
14648 require_noerr( err
, exit
);
14650 err
= HTTPHeader_SetField( inHeader
, "MX", "%d", inMX
);
14651 require_noerr( err
, exit
);
14653 err
= HTTPHeader_Commit( inHeader
);
14654 require_noerr( err
, exit
);
14660 //===========================================================================================================================
14661 // SSDPDiscoverPrintPrologue
14662 //===========================================================================================================================
14664 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext
*inContext
)
14666 const int receiveSecs
= inContext
->receiveSecs
;
14667 const char * ifName
;
14668 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
14669 NetTransportType ifType
;
14671 ifName
= if_indextoname( inContext
->ifindex
, ifNameBuf
);
14673 ifType
= kNetTransportType_Undefined
;
14674 if( ifName
) SocketGetInterfaceInfo( kInvalidSocketRef
, ifName
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &ifType
);
14676 FPrintF( stdout
, "Interface: %s/%d/%s\n",
14677 ifName
? ifName
: "?", inContext
->ifindex
, NetTransportTypeToString( ifType
) );
14678 FPrintF( stdout
, "IP protocols: %?s%?s%?s\n",
14679 inContext
->useIPv4
, "IPv4", ( inContext
->useIPv4
&& inContext
->useIPv6
), ", ", inContext
->useIPv6
, "IPv6" );
14680 FPrintF( stdout
, "Receive duration: " );
14681 if( receiveSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", receiveSecs
, receiveSecs
!= 1, 's' );
14682 else FPrintF( stdout
, "∞\n" );
14683 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
14686 //===========================================================================================================================
14687 // SSDPDiscoverReadHandler
14688 //===========================================================================================================================
14690 static void SSDPDiscoverReadHandler( void *inContext
)
14693 struct timeval now
;
14694 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
14695 SSDPDiscoverContext
* const context
= (SSDPDiscoverContext
*) sockCtx
->userContext
;
14696 HTTPHeader
* const header
= &context
->header
;
14697 sockaddr_ip fromAddr
;
14700 gettimeofday( &now
, NULL
);
14702 err
= SocketRecvFrom( sockCtx
->sock
, header
->buf
, sizeof( header
->buf
), &msgLen
, &fromAddr
, sizeof( fromAddr
),
14703 NULL
, NULL
, NULL
, NULL
);
14704 require_noerr( err
, exit
);
14706 FPrintF( stdout
, "---\n" );
14707 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
14708 FPrintF( stdout
, "Source: %##a\n", &fromAddr
);
14709 FPrintF( stdout
, "Message size: %zu\n", msgLen
);
14710 header
->len
= msgLen
;
14711 if( HTTPHeader_Validate( header
) )
14713 FPrintF( stdout
, "HTTP header:\n%1{text}", header
->buf
, header
->len
);
14714 if( header
->extraDataLen
> 0 )
14716 FPrintF( stdout
, "HTTP body: %1.1H", header
->extraDataPtr
, (int) header
->extraDataLen
, INT_MAX
);
14721 FPrintF( stdout
, "Invalid HTTP message:\n%1.1H", header
->buf
, (int) msgLen
, INT_MAX
);
14726 if( err
) exit( 1 );
14729 //===========================================================================================================================
14730 // HTTPHeader_Validate
14732 // Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid.
14733 // This assumes the "buf" and "len" fields are set. The other fields are set by this function.
14735 // Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework.
14736 //===========================================================================================================================
14738 Boolean
HTTPHeader_Validate( HTTPHeader
*inHeader
)
14743 // Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
14745 require( inHeader
->len
< sizeof( inHeader
->buf
), exit
);
14746 src
= inHeader
->buf
;
14747 end
= src
+ inHeader
->len
;
14748 if( ( ( end
- src
) >= 4 ) && ( src
[ 0 ] == '$' ) )
14754 // Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted.
14755 // $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over.
14759 while( ( src
< end
) && ( src
[ 0 ] != '\n' ) ) ++src
;
14760 if( src
>= end
) goto exit
;
14762 if( ( ( end
- src
) >= 2 ) && ( src
[ 0 ] == '\r' ) && ( src
[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF
14767 else if( ( ( end
- src
) >= 1 ) && ( src
[ 0 ] == '\n' ) ) // LFLF
14774 inHeader
->extraDataPtr
= src
;
14775 inHeader
->extraDataLen
= (size_t)( end
- src
);
14776 inHeader
->len
= (size_t)( src
- inHeader
->buf
);
14783 #if( TARGET_OS_DARWIN )
14784 //===========================================================================================================================
14786 //===========================================================================================================================
14788 // res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h).
14790 SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv
);
14791 SOFT_LINK_FUNCTION_EX( resolv
, res_9_query
,
14793 ( const char *dname
, int class, int type
, u_char
*answer
, int anslen
),
14794 ( dname
, class, type
, answer
, anslen
) );
14796 // res_query() from libinfo
14798 SOFT_LINK_LIBRARY_EX( "/usr/lib", info
);
14799 SOFT_LINK_FUNCTION_EX( info
, res_query
,
14801 ( const char *dname
, int class, int type
, u_char
*answer
, int anslen
),
14802 ( dname
, class, type
, answer
, anslen
) );
14804 typedef int ( *res_query_f
)( const char *dname
, int class, int type
, u_char
*answer
, int anslen
);
14806 static void ResQueryCmd( void )
14809 res_query_f res_query_ptr
;
14811 uint16_t type
, class;
14812 uint8_t answer
[ 1024 ];
14814 // Get pointer to one of the res_query() functions.
14816 if( gResQuery_UseLibInfo
)
14818 if( !SOFT_LINK_HAS_FUNCTION( info
, res_query
) )
14820 FPrintF( stderr
, "Failed to soft link res_query from libinfo.\n" );
14821 err
= kNotFoundErr
;
14824 res_query_ptr
= soft_res_query
;
14828 if( !SOFT_LINK_HAS_FUNCTION( resolv
, res_9_query
) )
14830 FPrintF( stderr
, "Failed to soft link res_query from libresolv.\n" );
14831 err
= kNotFoundErr
;
14834 res_query_ptr
= soft_res_9_query
;
14837 // Get record type.
14839 err
= RecordTypeFromArgString( gResQuery_Type
, &type
);
14840 require_noerr( err
, exit
);
14842 // Get record class.
14844 if( gResQuery_Class
)
14846 err
= RecordClassFromArgString( gResQuery_Class
, &class );
14847 require_noerr( err
, exit
);
14851 class = kDNSServiceClass_IN
;
14856 FPrintF( stdout
, "Name: %s\n", gResQuery_Name
);
14857 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
14858 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
14859 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
14860 FPrintF( stdout
, "---\n" );
14862 // Call res_query().
14864 n
= res_query_ptr( gResQuery_Name
, class, type
, (u_char
*) answer
, (int) sizeof( answer
) );
14867 FPrintF( stderr
, "res_query() failed with error: %d (%s).\n", h_errno
, hstrerror( h_errno
) );
14874 FPrintF( stdout
, "Message size: %d\n\n%{du:dnsmsg}", n
, answer
, (size_t) n
);
14877 if( err
) exit( 1 );
14880 //===========================================================================================================================
14881 // ResolvDNSQueryCmd
14882 //===========================================================================================================================
14884 // 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
14885 // avoid including the header file.
14887 typedef void * dns_handle_t
;
14889 SOFT_LINK_FUNCTION_EX( resolv
, dns_open
, dns_handle_t
, ( const char *path
), ( path
) );
14890 SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv
, dns_free
, ( dns_handle_t
*dns
), ( dns
) );
14891 SOFT_LINK_FUNCTION_EX( resolv
, dns_query
,
14899 struct sockaddr
* from
,
14900 uint32_t * fromlen
),
14901 ( dns
, name
, dnsclass
, dnstype
, buf
, len
, from
, fromlen
) );
14903 static void ResolvDNSQueryCmd( void )
14907 dns_handle_t dns
= NULL
;
14908 uint16_t type
, class;
14911 uint8_t answer
[ 1024 ];
14913 // Make sure that the required symbols are available.
14915 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_open
) )
14917 FPrintF( stderr
, "Failed to soft link dns_open from libresolv.\n" );
14918 err
= kNotFoundErr
;
14922 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_free
) )
14924 FPrintF( stderr
, "Failed to soft link dns_free from libresolv.\n" );
14925 err
= kNotFoundErr
;
14929 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_query
) )
14931 FPrintF( stderr
, "Failed to soft link dns_query from libresolv.\n" );
14932 err
= kNotFoundErr
;
14936 // Get record type.
14938 err
= RecordTypeFromArgString( gResolvDNSQuery_Type
, &type
);
14939 require_noerr( err
, exit
);
14941 // Get record class.
14943 if( gResolvDNSQuery_Class
)
14945 err
= RecordClassFromArgString( gResolvDNSQuery_Class
, &class );
14946 require_noerr( err
, exit
);
14950 class = kDNSServiceClass_IN
;
14955 dns
= soft_dns_open( gResolvDNSQuery_Path
);
14958 FPrintF( stderr
, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path
);
14965 FPrintF( stdout
, "Name: %s\n", gResolvDNSQuery_Name
);
14966 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
14967 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
14968 FPrintF( stdout
, "Path: %s\n", gResolvDNSQuery_Path
? gResolvDNSQuery_Name
: "<NULL>" );
14969 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
14970 FPrintF( stdout
, "---\n" );
14972 // Call dns_query().
14974 memset( &from
, 0, sizeof( from
) );
14975 fromLen
= (uint32_t) sizeof( from
);
14976 n
= soft_dns_query( dns
, gResolvDNSQuery_Name
, class, type
, (char *) answer
, (uint32_t) sizeof( answer
), &from
.sa
,
14980 FPrintF( stderr
, "dns_query() failed with error: %d (%s).\n", h_errno
, hstrerror( h_errno
) );
14987 FPrintF( stdout
, "From: %##a\n", &from
);
14988 FPrintF( stdout
, "Message size: %d\n\n%{du:dnsmsg}", n
, answer
, (size_t) n
);
14991 if( dns
) soft_dns_free( dns
);
14992 if( err
) exit( 1 );
14995 //===========================================================================================================================
14997 //===========================================================================================================================
15000 _CFHostResolveCallback(
15002 CFHostInfoType inInfoType
,
15003 const CFStreamError
* inError
,
15006 static void CFHostCmd( void )
15011 CFHostRef host
= NULL
;
15012 CFHostClientContext context
;
15013 CFStreamError streamErr
;
15015 name
= CFStringCreateWithCString( kCFAllocatorDefault
, gCFHost_Name
, kCFStringEncodingUTF8
);
15016 require_action( name
, exit
, err
= kUnknownErr
);
15018 host
= CFHostCreateWithName( kCFAllocatorDefault
, name
);
15020 require_action( host
, exit
, err
= kUnknownErr
);
15022 memset( &context
, 0, sizeof( context
) );
15023 success
= CFHostSetClient( host
, _CFHostResolveCallback
, &context
);
15024 require_action( success
, exit
, err
= kUnknownErr
);
15026 CFHostScheduleWithRunLoop( host
, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode
);
15030 FPrintF( stdout
, "Hostname: %s\n", gCFHost_Name
);
15031 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
15032 FPrintF( stdout
, "---\n" );
15034 success
= CFHostStartInfoResolution( host
, kCFHostAddresses
, &streamErr
);
15035 require_action( success
, exit
, err
= kUnknownErr
);
15041 CFReleaseNullSafe( host
);
15042 if( err
) exit( 1 );
15045 static void _CFHostResolveCallback( CFHostRef inHost
, CFHostInfoType inInfoType
, const CFStreamError
*inError
, void *inInfo
)
15048 struct timeval now
;
15050 gettimeofday( &now
, NULL
);
15052 Unused( inInfoType
);
15055 if( inError
&& ( inError
->domain
!= 0 ) && ( inError
->error
) )
15057 err
= inError
->error
;
15058 if( inError
->domain
== kCFStreamErrorDomainNetDB
)
15060 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
15064 FPrintF( stderr
, "Error %#m\n", err
);
15069 CFArrayRef addresses
;
15071 CFDataRef addrData
;
15072 const struct sockaddr
* sockAddr
;
15073 Boolean wasResolved
= false;
15075 addresses
= CFHostGetAddressing( inHost
, &wasResolved
);
15076 check( wasResolved
);
15080 count
= CFArrayGetCount( addresses
);
15081 for( i
= 0; i
< count
; ++i
)
15083 addrData
= CFArrayGetCFDataAtIndex( addresses
, i
, &err
);
15084 require_noerr( err
, exit
);
15086 sockAddr
= (const struct sockaddr
*) CFDataGetBytePtr( addrData
);
15087 FPrintF( stdout
, "%##a\n", sockAddr
);
15093 FPrintF( stdout
, "---\n" );
15094 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
15096 if( gCFHost_WaitSecs
> 0 ) sleep( (unsigned int) gCFHost_WaitSecs
);
15099 exit( err
? 1 : 0 );
15102 //===========================================================================================================================
15105 // Note: Based on ajn's supplemental test tool.
15106 //===========================================================================================================================
15108 static void DNSConfigAddCmd( void )
15111 CFMutableDictionaryRef dict
= NULL
;
15112 CFMutableArrayRef array
= NULL
;
15114 SCDynamicStoreRef store
= NULL
;
15115 CFStringRef key
= NULL
;
15118 // Create dictionary.
15120 dict
= CFDictionaryCreateMutable( NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
15121 require_action( dict
, exit
, err
= kNoMemoryErr
);
15123 // Add DNS server IP addresses.
15125 array
= CFArrayCreateMutable( NULL
, (CFIndex
) gDNSConfigAdd_IPAddrCount
, &kCFTypeArrayCallBacks
);
15126 require_action( array
, exit
, err
= kNoMemoryErr
);
15128 for( i
= 0; i
< gDNSConfigAdd_IPAddrCount
; ++i
)
15130 CFStringRef addrStr
;
15132 addrStr
= CFStringCreateWithCString( NULL
, gDNSConfigAdd_IPAddrArray
[ i
], kCFStringEncodingUTF8
);
15133 require_action( addrStr
, exit
, err
= kUnknownErr
);
15135 CFArrayAppendValue( array
, addrStr
);
15136 CFRelease( addrStr
);
15139 CFDictionarySetValue( dict
, kSCPropNetDNSServerAddresses
, array
);
15140 ForgetCF( &array
);
15142 // Add domains, if any.
15144 array
= CFArrayCreateMutable( NULL
, (CFIndex
) Min( gDNSConfigAdd_DomainCount
, 1 ), &kCFTypeArrayCallBacks
);
15145 require_action( array
, exit
, err
= kNoMemoryErr
);
15147 if( gDNSConfigAdd_DomainCount
> 0 )
15149 for( i
= 0; i
< gDNSConfigAdd_DomainCount
; ++i
)
15151 CFStringRef domainStr
;
15153 domainStr
= CFStringCreateWithCString( NULL
, gDNSConfigAdd_DomainArray
[ i
], kCFStringEncodingUTF8
);
15154 require_action( domainStr
, exit
, err
= kUnknownErr
);
15156 CFArrayAppendValue( array
, domainStr
);
15157 CFRelease( domainStr
);
15162 // There are no domains, but the domain array needs to be non-empty, so add a zero-length string to the array.
15164 CFArrayAppendValue( array
, CFSTR( "" ) );
15167 CFDictionarySetValue( dict
, kSCPropNetDNSSupplementalMatchDomains
, array
);
15168 ForgetCF( &array
);
15170 // Add interface, if any.
15172 if( gDNSConfigAdd_Interface
)
15174 err
= CFDictionarySetCString( dict
, kSCPropInterfaceName
, gDNSConfigAdd_Interface
, kSizeCString
);
15175 require_noerr( err
, exit
);
15177 CFDictionarySetValue( dict
, kSCPropNetDNSConfirmedServiceID
, gDNSConfigAdd_ID
);
15180 // Set dictionary in dynamic store.
15182 store
= SCDynamicStoreCreate( NULL
, CFSTR( "com.apple.dnssdutil" ), NULL
, NULL
);
15183 err
= map_scerror( store
);
15184 require_noerr( err
, exit
);
15186 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
, gDNSConfigAdd_ID
, kSCEntNetDNS
);
15187 require_action( key
, exit
, err
= kUnknownErr
);
15189 success
= SCDynamicStoreSetValue( store
, key
, dict
);
15190 require_action( success
, exit
, err
= kUnknownErr
);
15193 CFReleaseNullSafe( dict
);
15194 CFReleaseNullSafe( array
);
15195 CFReleaseNullSafe( store
);
15196 CFReleaseNullSafe( key
);
15197 gExitCode
= err
? 1 : 0;
15200 //===========================================================================================================================
15201 // DNSConfigRemoveCmd
15202 //===========================================================================================================================
15204 static void DNSConfigRemoveCmd( void )
15207 SCDynamicStoreRef store
= NULL
;
15208 CFStringRef key
= NULL
;
15211 store
= SCDynamicStoreCreate( NULL
, CFSTR( "com.apple.dnssdutil" ), NULL
, NULL
);
15212 err
= map_scerror( store
);
15213 require_noerr( err
, exit
);
15215 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
, gDNSConfigRemove_ID
, kSCEntNetDNS
);
15216 require_action( key
, exit
, err
= kUnknownErr
);
15218 success
= SCDynamicStoreRemoveValue( store
, key
);
15219 require_action( success
, exit
, err
= kUnknownErr
);
15222 CFReleaseNullSafe( store
);
15223 CFReleaseNullSafe( key
);
15224 gExitCode
= err
? 1 : 0;
15226 #endif // TARGET_OS_DARWIN
15228 //===========================================================================================================================
15229 // DaemonVersionCmd
15230 //===========================================================================================================================
15232 static void DaemonVersionCmd( void )
15235 uint32_t size
, version
;
15238 size
= (uint32_t) sizeof( version
);
15239 err
= DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion
, &version
, &size
);
15240 require_noerr( err
, exit
);
15242 FPrintF( stdout
, "Daemon version: %s\n", SourceVersionToCString( version
, strBuf
) );
15245 if( err
) exit( 1 );
15248 //===========================================================================================================================
15250 //===========================================================================================================================
15252 static void Exit( void *inContext
)
15254 const char * const reason
= (const char *) inContext
;
15256 FPrintF( stdout
, "---\n" );
15257 FPrintF( stdout
, "End time: %{du:time}\n", NULL
);
15258 if( reason
) FPrintF( stdout
, "End reason: %s\n", reason
);
15262 //===========================================================================================================================
15263 // PrintFTimestampHandler
15264 //===========================================================================================================================
15267 PrintFTimestampHandler(
15268 PrintFContext
* inContext
,
15269 PrintFFormat
* inFormat
,
15270 PrintFVAList
* inArgs
,
15271 void * inUserContext
)
15273 struct timeval now
;
15274 const struct timeval
* tv
;
15275 struct tm
* localTime
;
15278 char dateTimeStr
[ 32 ];
15280 Unused( inUserContext
);
15282 tv
= va_arg( inArgs
->args
, const struct timeval
* );
15283 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
15287 gettimeofday( &now
, NULL
);
15290 localTime
= localtime( &tv
->tv_sec
);
15291 len
= strftime( dateTimeStr
, sizeof( dateTimeStr
), "%Y-%m-%d %H:%M:%S", localTime
);
15292 if( len
== 0 ) dateTimeStr
[ 0 ] = '\0';
15294 n
= PrintFCore( inContext
, "%s.%06u", dateTimeStr
, (unsigned int) tv
->tv_usec
);
15300 //===========================================================================================================================
15301 // PrintFDNSMessageHandler
15302 //===========================================================================================================================
15305 PrintFDNSMessageHandler(
15306 PrintFContext
* inContext
,
15307 PrintFFormat
* inFormat
,
15308 PrintFVAList
* inArgs
,
15309 void * inUserContext
)
15312 const void * msgPtr
;
15317 Boolean printRawRData
;
15319 Unused( inUserContext
);
15321 msgPtr
= va_arg( inArgs
->args
, const void * );
15322 msgLen
= va_arg( inArgs
->args
, size_t );
15323 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
15325 isMDNS
= ( inFormat
->altForm
> 0 ) ? true : false;
15326 if( inFormat
->precision
== 0 ) printRawRData
= false;
15327 else if( inFormat
->precision
== 1 ) printRawRData
= true;
15330 n
= PrintFCore( inContext
, "<< BAD %%{du:dnsmsg} PRECISION >>" );
15334 err
= DNSMessageToText( msgPtr
, msgLen
, isMDNS
, printRawRData
, &text
);
15337 n
= PrintFCore( inContext
, "%*{text}", inFormat
->fieldWidth
, text
, kSizeCString
);
15342 n
= PrintFCore( inContext
, "%*.1H", inFormat
->fieldWidth
, msgPtr
, (int) msgLen
, (int) msgLen
);
15349 //===========================================================================================================================
15350 // PrintFAddRmvFlagsHandler
15351 //===========================================================================================================================
15354 PrintFAddRmvFlagsHandler(
15355 PrintFContext
* inContext
,
15356 PrintFFormat
* inFormat
,
15357 PrintFVAList
* inArgs
,
15358 void * inUserContext
)
15360 DNSServiceFlags flags
;
15363 Unused( inUserContext
);
15365 flags
= va_arg( inArgs
->args
, DNSServiceFlags
);
15366 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
15368 n
= PrintFCore( inContext
, "%08X %s%c%c", flags
,
15369 ( flags
& kDNSServiceFlagsAdd
) ? "Add" : "Rmv",
15370 ( flags
& kDNSServiceFlagsMoreComing
) ? '+' : ' ',
15371 ( flags
& kDNSServiceFlagsExpiredAnswer
) ? '!' : ' ' );
15377 //===========================================================================================================================
15378 // GetDNSSDFlagsFromOpts
15379 //===========================================================================================================================
15381 static DNSServiceFlags
GetDNSSDFlagsFromOpts( void )
15383 DNSServiceFlags flags
;
15385 flags
= (DNSServiceFlags
) gDNSSDFlags
;
15386 if( flags
& kDNSServiceFlagsShareConnection
)
15388 FPrintF( stderr
, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
15389 kDNSServiceFlagsShareConnection
);
15392 if( gDNSSDFlag_AllowExpiredAnswers
) flags
|= kDNSServiceFlagsAllowExpiredAnswers
;
15393 if( gDNSSDFlag_BrowseDomains
) flags
|= kDNSServiceFlagsBrowseDomains
;
15394 if( gDNSSDFlag_DenyCellular
) flags
|= kDNSServiceFlagsDenyCellular
;
15395 if( gDNSSDFlag_DenyExpensive
) flags
|= kDNSServiceFlagsDenyExpensive
;
15396 if( gDNSSDFlag_ForceMulticast
) flags
|= kDNSServiceFlagsForceMulticast
;
15397 if( gDNSSDFlag_IncludeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
15398 if( gDNSSDFlag_NoAutoRename
) flags
|= kDNSServiceFlagsNoAutoRename
;
15399 if( gDNSSDFlag_PathEvaluationDone
) flags
|= kDNSServiceFlagsPathEvaluationDone
;
15400 if( gDNSSDFlag_RegistrationDomains
) flags
|= kDNSServiceFlagsRegistrationDomains
;
15401 if( gDNSSDFlag_ReturnIntermediates
) flags
|= kDNSServiceFlagsReturnIntermediates
;
15402 if( gDNSSDFlag_Shared
) flags
|= kDNSServiceFlagsShared
;
15403 if( gDNSSDFlag_SuppressUnusable
) flags
|= kDNSServiceFlagsSuppressUnusable
;
15404 if( gDNSSDFlag_Timeout
) flags
|= kDNSServiceFlagsTimeout
;
15405 if( gDNSSDFlag_UnicastResponse
) flags
|= kDNSServiceFlagsUnicastResponse
;
15406 if( gDNSSDFlag_Unique
) flags
|= kDNSServiceFlagsUnique
;
15407 if( gDNSSDFlag_WakeOnResolve
) flags
|= kDNSServiceFlagsWakeOnResolve
;
15412 //===========================================================================================================================
15413 // CreateConnectionFromArgString
15414 //===========================================================================================================================
15417 CreateConnectionFromArgString(
15418 const char * inString
,
15419 dispatch_queue_t inQueue
,
15420 DNSServiceRef
* outSDRef
,
15421 ConnectionDesc
* outDesc
)
15424 DNSServiceRef sdRef
= NULL
;
15425 ConnectionType type
;
15426 int32_t pid
= -1; // Initializing because the analyzer claims pid may be used uninitialized.
15427 uint8_t uuid
[ 16 ];
15429 if( strcasecmp( inString
, kConnectionArg_Normal
) == 0 )
15431 err
= DNSServiceCreateConnection( &sdRef
);
15432 require_noerr( err
, exit
);
15433 type
= kConnectionType_Normal
;
15435 else if( stricmp_prefix( inString
, kConnectionArgPrefix_PID
) == 0 )
15437 const char * const pidStr
= inString
+ sizeof_string( kConnectionArgPrefix_PID
);
15439 err
= StringToInt32( pidStr
, &pid
);
15442 FPrintF( stderr
, "Invalid delegate connection PID value: %s\n", pidStr
);
15447 memset( uuid
, 0, sizeof( uuid
) );
15448 err
= DNSServiceCreateDelegateConnection( &sdRef
, pid
, uuid
);
15451 FPrintF( stderr
, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err
, pid
);
15454 type
= kConnectionType_DelegatePID
;
15456 else if( stricmp_prefix( inString
, kConnectionArgPrefix_UUID
) == 0 )
15458 const char * const uuidStr
= inString
+ sizeof_string( kConnectionArgPrefix_UUID
);
15460 check_compile_time_code( sizeof( uuid
) == sizeof( uuid_t
) );
15462 err
= StringToUUID( uuidStr
, kSizeCString
, false, uuid
);
15465 FPrintF( stderr
, "Invalid delegate connection UUID value: %s\n", uuidStr
);
15470 err
= DNSServiceCreateDelegateConnection( &sdRef
, 0, uuid
);
15473 FPrintF( stderr
, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err
, uuid
);
15476 type
= kConnectionType_DelegateUUID
;
15480 FPrintF( stderr
, "Unrecognized connection string \"%s\".\n", inString
);
15485 err
= DNSServiceSetDispatchQueue( sdRef
, inQueue
);
15486 require_noerr( err
, exit
);
15491 outDesc
->type
= type
;
15492 if( type
== kConnectionType_DelegatePID
) outDesc
->delegate
.pid
= pid
;
15493 else if( type
== kConnectionType_DelegateUUID
) memcpy( outDesc
->delegate
.uuid
, uuid
, 16 );
15498 if( sdRef
) DNSServiceRefDeallocate( sdRef
);
15502 //===========================================================================================================================
15503 // InterfaceIndexFromArgString
15504 //===========================================================================================================================
15506 static OSStatus
InterfaceIndexFromArgString( const char *inString
, uint32_t *outIndex
)
15513 ifIndex
= if_nametoindex( inString
);
15516 err
= StringToUInt32( inString
, &ifIndex
);
15519 FPrintF( stderr
, "error: Invalid interface value: %s\n", inString
);
15530 *outIndex
= ifIndex
;
15537 //===========================================================================================================================
15538 // RecordDataFromArgString
15539 //===========================================================================================================================
15541 static OSStatus
RecordDataFromArgString( const char *inString
, uint8_t **outDataPtr
, size_t *outDataLen
)
15544 uint8_t * dataPtr
= NULL
;
15551 else if( stricmp_prefix( inString
, kRDataArgPrefix_Domain
) == 0 )
15553 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_Domain
);
15555 err
= StringToDomainName( str
, &dataPtr
, &dataLen
);
15556 require_noerr_quiet( err
, exit
);
15561 else if( stricmp_prefix( inString
, kRDataArgPrefix_File
) == 0 )
15563 const char * const path
= inString
+ sizeof_string( kRDataArgPrefix_File
);
15565 err
= CopyFileDataByPath( path
, (char **) &dataPtr
, &dataLen
);
15566 require_noerr( err
, exit
);
15567 require_action( dataLen
<= kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
15570 // Hexadecimal string
15572 else if( stricmp_prefix( inString
, kRDataArgPrefix_HexString
) == 0 )
15574 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_HexString
);
15576 err
= HexToDataCopy( str
, kSizeCString
, kHexToData_DefaultFlags
, &dataPtr
, &dataLen
, NULL
);
15577 require_noerr( err
, exit
);
15578 require_action( dataLen
<= kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
15581 // IPv4 address string
15583 else if( stricmp_prefix( inString
, kRDataArgPrefix_IPv4
) == 0 )
15585 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_IPv4
);
15587 err
= StringToARecordData( str
, &dataPtr
, &dataLen
);
15588 require_noerr_quiet( err
, exit
);
15591 // IPv6 address string
15593 else if( stricmp_prefix( inString
, kRDataArgPrefix_IPv6
) == 0 )
15595 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_IPv6
);
15597 err
= StringToAAAARecordData( str
, &dataPtr
, &dataLen
);
15598 require_noerr_quiet( err
, exit
);
15603 else if( stricmp_prefix( inString
, kRDataArgPrefix_SRV
) == 0 )
15605 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_SRV
);
15607 err
= CreateSRVRecordDataFromString( str
, &dataPtr
, &dataLen
);
15608 require_noerr( err
, exit
);
15611 // String with escaped hex and octal bytes
15613 else if( stricmp_prefix( inString
, kRDataArgPrefix_String
) == 0 )
15615 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_String
);
15616 const char * const end
= str
+ strlen( str
);
15623 success
= ParseQuotedEscapedString( str
, end
, "", NULL
, 0, NULL
, &totalLen
, NULL
);
15624 require_action( success
, exit
, err
= kParamErr
);
15625 require_action( totalLen
<= kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
15627 dataLen
= totalLen
;
15628 dataPtr
= (uint8_t *) malloc( dataLen
);
15629 require_action( dataPtr
, exit
, err
= kNoMemoryErr
);
15631 success
= ParseQuotedEscapedString( str
, end
, "", (char *) dataPtr
, dataLen
, &copiedLen
, NULL
, NULL
);
15632 require_action( success
, exit
, err
= kParamErr
);
15633 check( copiedLen
== dataLen
);
15644 else if( stricmp_prefix( inString
, kRDataArgPrefix_TXT
) == 0 )
15646 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_TXT
);
15648 err
= CreateTXTRecordDataFromString( str
, ',', &dataPtr
, &dataLen
);
15649 require_noerr( err
, exit
);
15652 // Unrecognized format
15656 FPrintF( stderr
, "Unrecognized record data string \"%s\".\n", inString
);
15662 *outDataLen
= dataLen
;
15663 *outDataPtr
= dataPtr
;
15667 FreeNullSafe( dataPtr
);
15671 //===========================================================================================================================
15672 // RecordTypeFromArgString
15673 //===========================================================================================================================
15677 uint16_t value
; // Record type's numeric value.
15678 const char * name
; // Record type's name as a string (e.g., "A", "PTR", "SRV").
15682 static const RecordType kRecordTypes
[] =
15686 { kDNSServiceType_A
, "A" },
15687 { kDNSServiceType_AAAA
, "AAAA" },
15688 { kDNSServiceType_PTR
, "PTR" },
15689 { kDNSServiceType_SRV
, "SRV" },
15690 { kDNSServiceType_TXT
, "TXT" },
15691 { kDNSServiceType_CNAME
, "CNAME" },
15692 { kDNSServiceType_SOA
, "SOA" },
15693 { kDNSServiceType_NSEC
, "NSEC" },
15694 { kDNSServiceType_NS
, "NS" },
15695 { kDNSServiceType_MX
, "MX" },
15696 { kDNSServiceType_ANY
, "ANY" },
15697 { kDNSServiceType_OPT
, "OPT" },
15699 // Less common types.
15701 { kDNSServiceType_MD
, "MD" },
15702 { kDNSServiceType_NS
, "NS" },
15703 { kDNSServiceType_MD
, "MD" },
15704 { kDNSServiceType_MF
, "MF" },
15705 { kDNSServiceType_MB
, "MB" },
15706 { kDNSServiceType_MG
, "MG" },
15707 { kDNSServiceType_MR
, "MR" },
15708 { kDNSServiceType_NULL
, "NULL" },
15709 { kDNSServiceType_WKS
, "WKS" },
15710 { kDNSServiceType_HINFO
, "HINFO" },
15711 { kDNSServiceType_MINFO
, "MINFO" },
15712 { kDNSServiceType_RP
, "RP" },
15713 { kDNSServiceType_AFSDB
, "AFSDB" },
15714 { kDNSServiceType_X25
, "X25" },
15715 { kDNSServiceType_ISDN
, "ISDN" },
15716 { kDNSServiceType_RT
, "RT" },
15717 { kDNSServiceType_NSAP
, "NSAP" },
15718 { kDNSServiceType_NSAP_PTR
, "NSAP_PTR" },
15719 { kDNSServiceType_SIG
, "SIG" },
15720 { kDNSServiceType_KEY
, "KEY" },
15721 { kDNSServiceType_PX
, "PX" },
15722 { kDNSServiceType_GPOS
, "GPOS" },
15723 { kDNSServiceType_LOC
, "LOC" },
15724 { kDNSServiceType_NXT
, "NXT" },
15725 { kDNSServiceType_EID
, "EID" },
15726 { kDNSServiceType_NIMLOC
, "NIMLOC" },
15727 { kDNSServiceType_ATMA
, "ATMA" },
15728 { kDNSServiceType_NAPTR
, "NAPTR" },
15729 { kDNSServiceType_KX
, "KX" },
15730 { kDNSServiceType_CERT
, "CERT" },
15731 { kDNSServiceType_A6
, "A6" },
15732 { kDNSServiceType_DNAME
, "DNAME" },
15733 { kDNSServiceType_SINK
, "SINK" },
15734 { kDNSServiceType_APL
, "APL" },
15735 { kDNSServiceType_DS
, "DS" },
15736 { kDNSServiceType_SSHFP
, "SSHFP" },
15737 { kDNSServiceType_IPSECKEY
, "IPSECKEY" },
15738 { kDNSServiceType_RRSIG
, "RRSIG" },
15739 { kDNSServiceType_DNSKEY
, "DNSKEY" },
15740 { kDNSServiceType_DHCID
, "DHCID" },
15741 { kDNSServiceType_NSEC3
, "NSEC3" },
15742 { kDNSServiceType_NSEC3PARAM
, "NSEC3PARAM" },
15743 { kDNSServiceType_HIP
, "HIP" },
15744 { kDNSServiceType_SPF
, "SPF" },
15745 { kDNSServiceType_UINFO
, "UINFO" },
15746 { kDNSServiceType_UID
, "UID" },
15747 { kDNSServiceType_GID
, "GID" },
15748 { kDNSServiceType_UNSPEC
, "UNSPEC" },
15749 { kDNSServiceType_TKEY
, "TKEY" },
15750 { kDNSServiceType_TSIG
, "TSIG" },
15751 { kDNSServiceType_IXFR
, "IXFR" },
15752 { kDNSServiceType_AXFR
, "AXFR" },
15753 { kDNSServiceType_MAILB
, "MAILB" },
15754 { kDNSServiceType_MAILA
, "MAILA" }
15757 static OSStatus
RecordTypeFromArgString( const char *inString
, uint16_t *outValue
)
15761 const RecordType
* type
;
15762 const RecordType
* const end
= kRecordTypes
+ countof( kRecordTypes
);
15764 for( type
= kRecordTypes
; type
< end
; ++type
)
15766 if( strcasecmp( type
->name
, inString
) == 0 )
15768 *outValue
= type
->value
;
15773 err
= StringToInt32( inString
, &i32
);
15774 require_noerr_quiet( err
, exit
);
15775 require_action_quiet( ( i32
>= 0 ) && ( i32
<= UINT16_MAX
), exit
, err
= kParamErr
);
15777 *outValue
= (uint16_t) i32
;
15783 //===========================================================================================================================
15784 // RecordClassFromArgString
15785 //===========================================================================================================================
15787 static OSStatus
RecordClassFromArgString( const char *inString
, uint16_t *outValue
)
15792 if( strcasecmp( inString
, "IN" ) == 0 )
15794 *outValue
= kDNSServiceClass_IN
;
15799 err
= StringToInt32( inString
, &i32
);
15800 require_noerr_quiet( err
, exit
);
15801 require_action_quiet( ( i32
>= 0 ) && ( i32
<= UINT16_MAX
), exit
, err
= kParamErr
);
15803 *outValue
= (uint16_t) i32
;
15809 //===========================================================================================================================
15810 // InterfaceIndexToName
15811 //===========================================================================================================================
15813 static char * InterfaceIndexToName( uint32_t inIfIndex
, char inNameBuf
[ kInterfaceNameBufLen
] )
15815 switch( inIfIndex
)
15817 case kDNSServiceInterfaceIndexAny
:
15818 strlcpy( inNameBuf
, "Any", kInterfaceNameBufLen
);
15821 case kDNSServiceInterfaceIndexLocalOnly
:
15822 strlcpy( inNameBuf
, "LocalOnly", kInterfaceNameBufLen
);
15825 case kDNSServiceInterfaceIndexUnicast
:
15826 strlcpy( inNameBuf
, "Unicast", kInterfaceNameBufLen
);
15829 case kDNSServiceInterfaceIndexP2P
:
15830 strlcpy( inNameBuf
, "P2P", kInterfaceNameBufLen
);
15833 #if( defined( kDNSServiceInterfaceIndexBLE ) )
15834 case kDNSServiceInterfaceIndexBLE
:
15835 strlcpy( inNameBuf
, "BLE", kInterfaceNameBufLen
);
15843 name
= if_indextoname( inIfIndex
, inNameBuf
);
15844 if( !name
) strlcpy( inNameBuf
, "NO NAME", kInterfaceNameBufLen
);
15849 return( inNameBuf
);
15852 //===========================================================================================================================
15853 // RecordTypeToString
15854 //===========================================================================================================================
15856 static const char * RecordTypeToString( unsigned int inValue
)
15858 const RecordType
* type
;
15859 const RecordType
* const end
= kRecordTypes
+ countof( kRecordTypes
);
15861 for( type
= kRecordTypes
; type
< end
; ++type
)
15863 if( type
->value
== inValue
) return( type
->name
);
15868 //===========================================================================================================================
15869 // DNSMessageExtractDomainName
15870 //===========================================================================================================================
15873 DNSMessageExtractDomainName(
15874 const uint8_t * inMsgPtr
,
15876 const uint8_t * inNamePtr
,
15877 uint8_t inBuf
[ kDomainNameLengthMax
],
15878 const uint8_t ** outNextPtr
)
15881 const uint8_t * label
;
15883 const uint8_t * nextLabel
;
15884 const uint8_t * const msgEnd
= inMsgPtr
+ inMsgLen
;
15885 uint8_t * dst
= inBuf
;
15886 const uint8_t * const dstLim
= inBuf
? ( inBuf
+ kDomainNameLengthMax
) : NULL
;
15887 const uint8_t * nameEnd
= NULL
;
15889 require_action( ( inNamePtr
>= inMsgPtr
) && ( inNamePtr
< msgEnd
), exit
, err
= kRangeErr
);
15891 for( label
= inNamePtr
; ( labelLen
= label
[ 0 ] ) != 0; label
= nextLabel
)
15893 if( labelLen
<= kDomainLabelLengthMax
)
15895 nextLabel
= label
+ 1 + labelLen
;
15896 require_action( nextLabel
< msgEnd
, exit
, err
= kUnderrunErr
);
15899 require_action( ( dstLim
- dst
) > ( 1 + labelLen
), exit
, err
= kOverrunErr
);
15900 memcpy( dst
, label
, 1 + labelLen
);
15901 dst
+= ( 1 + labelLen
);
15904 else if( IsCompressionByte( labelLen
) )
15908 require_action( ( msgEnd
- label
) >= 2, exit
, err
= kUnderrunErr
);
15911 nameEnd
= label
+ 2;
15914 offset
= (uint16_t)( ( ( label
[ 0 ] & 0x3F ) << 8 ) | label
[ 1 ] );
15915 nextLabel
= inMsgPtr
+ offset
;
15916 require_action( nextLabel
< msgEnd
, exit
, err
= kUnderrunErr
);
15917 require_action( !IsCompressionByte( nextLabel
[ 0 ] ), exit
, err
= kMalformedErr
);
15921 dlogassert( "Unhandled label length 0x%02X\n", labelLen
);
15922 err
= kMalformedErr
;
15927 if( dst
) *dst
= 0;
15928 if( !nameEnd
) nameEnd
= label
+ 1;
15930 if( outNextPtr
) *outNextPtr
= nameEnd
;
15937 //===========================================================================================================================
15938 // DNSMessageExtractDomainNameString
15939 //===========================================================================================================================
15942 DNSMessageExtractDomainNameString(
15943 const void * inMsgPtr
,
15945 const void * inNamePtr
,
15946 char inBuf
[ kDNSServiceMaxDomainName
],
15947 const uint8_t ** outNextPtr
)
15950 const uint8_t * nextPtr
;
15951 uint8_t domainName
[ kDomainNameLengthMax
];
15953 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, inNamePtr
, domainName
, &nextPtr
);
15954 require_noerr( err
, exit
);
15956 err
= DomainNameToString( domainName
, NULL
, inBuf
, NULL
);
15957 require_noerr( err
, exit
);
15959 if( outNextPtr
) *outNextPtr
= nextPtr
;
15965 //===========================================================================================================================
15966 // DNSMessageExtractQuestion
15967 //===========================================================================================================================
15970 DNSMessageExtractQuestion(
15971 const uint8_t * inMsgPtr
,
15973 const uint8_t * inPtr
,
15974 uint8_t inNameBuf
[ kDomainNameLengthMax
],
15975 uint16_t * outType
,
15976 uint16_t * outClass
,
15977 const uint8_t ** outPtr
)
15980 const uint8_t * const msgEnd
= &inMsgPtr
[ inMsgLen
];
15981 const uint8_t * ptr
;
15982 const DNSQuestionFixedFields
* fields
;
15984 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, inPtr
, inNameBuf
, &ptr
);
15985 require_noerr_quiet( err
, exit
);
15986 require_action_quiet( (size_t)( msgEnd
- ptr
) >= sizeof( DNSQuestionFixedFields
), exit
, err
= kUnderrunErr
);
15988 fields
= (const DNSQuestionFixedFields
*) ptr
;
15989 if( outType
) *outType
= DNSQuestionFixedFieldsGetType( fields
);
15990 if( outClass
) *outClass
= DNSQuestionFixedFieldsGetClass( fields
);
15991 if( outPtr
) *outPtr
= (const uint8_t *) &fields
[ 1 ];
15997 //===========================================================================================================================
15998 // DNSMessageExtractRecord
15999 //===========================================================================================================================
16004 uint8_t class[ 2 ];
16006 uint8_t rdLength
[ 2 ];
16007 uint8_t rdata
[ 1 ];
16011 check_compile_time( offsetof( DNSRecordFields
, rdata
) == 10 );
16014 DNSMessageExtractRecord(
16015 const uint8_t * inMsgPtr
,
16017 const uint8_t * inPtr
,
16018 uint8_t inNameBuf
[ kDomainNameLengthMax
],
16019 uint16_t * outType
,
16020 uint16_t * outClass
,
16022 const uint8_t ** outRDataPtr
,
16023 size_t * outRDataLen
,
16024 const uint8_t ** outPtr
)
16027 const uint8_t * const msgEnd
= inMsgPtr
+ inMsgLen
;
16028 const uint8_t * ptr
;
16029 const DNSRecordFields
* record
;
16032 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, inPtr
, inNameBuf
, &ptr
);
16033 require_noerr_quiet( err
, exit
);
16034 require_action_quiet( (size_t)( msgEnd
- ptr
) >= offsetof( DNSRecordFields
, rdata
), exit
, err
= kUnderrunErr
);
16036 record
= (DNSRecordFields
*) ptr
;
16037 rdLength
= ReadBig16( record
->rdLength
);
16038 require_action_quiet( (size_t)( msgEnd
- record
->rdata
) >= rdLength
, exit
, err
= kUnderrunErr
);
16040 if( outType
) *outType
= ReadBig16( record
->type
);
16041 if( outClass
) *outClass
= ReadBig16( record
->class );
16042 if( outTTL
) *outTTL
= ReadBig32( record
->ttl
);
16043 if( outRDataPtr
) *outRDataPtr
= record
->rdata
;
16044 if( outRDataLen
) *outRDataLen
= rdLength
;
16045 if( outPtr
) *outPtr
= record
->rdata
+ rdLength
;
16051 //===========================================================================================================================
16052 // DNSMessageGetAnswerSection
16053 //===========================================================================================================================
16055 static OSStatus
DNSMessageGetAnswerSection( const uint8_t *inMsgPtr
, size_t inMsgLen
, const uint8_t **outPtr
)
16058 unsigned int questionCount
, i
;
16059 const DNSHeader
* hdr
;
16060 const uint8_t * ptr
;
16062 require_action_quiet( inMsgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
16064 hdr
= (DNSHeader
*) inMsgPtr
;
16065 questionCount
= DNSHeaderGetQuestionCount( hdr
);
16067 ptr
= (const uint8_t *) &hdr
[ 1 ];
16068 for( i
= 0; i
< questionCount
; ++i
)
16070 err
= DNSMessageExtractQuestion( inMsgPtr
, inMsgLen
, ptr
, NULL
, NULL
, NULL
, &ptr
);
16071 require_noerr( err
, exit
);
16074 if( outPtr
) *outPtr
= ptr
;
16081 //===========================================================================================================================
16082 // DNSRecordDataToString
16083 //===========================================================================================================================
16086 DNSRecordDataToString(
16087 const void * inRDataPtr
,
16089 unsigned int inRDataType
,
16090 const void * inMsgPtr
,
16092 char ** outString
)
16095 const uint8_t * const rdataPtr
= (uint8_t *) inRDataPtr
;
16096 const uint8_t * const rdataEnd
= rdataPtr
+ inRDataLen
;
16098 const uint8_t * ptr
;
16100 char domainNameStr
[ kDNSServiceMaxDomainName
];
16103 if( inRDataType
== kDNSServiceType_A
)
16105 require_action_quiet( inRDataLen
== 4, exit
, err
= kMalformedErr
);
16107 ASPrintF( &rdataStr
, "%.4a", rdataPtr
);
16108 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
16110 else if( inRDataType
== kDNSServiceType_AAAA
)
16112 require_action_quiet( inRDataLen
== 16, exit
, err
= kMalformedErr
);
16114 ASPrintF( &rdataStr
, "%.16a", rdataPtr
);
16115 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
16117 else if( ( inRDataType
== kDNSServiceType_PTR
) || ( inRDataType
== kDNSServiceType_CNAME
) ||
16118 ( inRDataType
== kDNSServiceType_NS
) )
16122 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, NULL
);
16123 require_noerr( err
, exit
);
16127 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, NULL
);
16128 require_noerr( err
, exit
);
16131 rdataStr
= strdup( domainNameStr
);
16132 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
16134 else if( inRDataType
== kDNSServiceType_SRV
)
16136 const SRVRecordDataFixedFields
* fields
;
16137 const uint8_t * target
;
16138 unsigned int priority
, weight
, port
;
16140 require_action_quiet( inRDataLen
> sizeof( SRVRecordDataFixedFields
), exit
, err
= kMalformedErr
);
16142 fields
= (const SRVRecordDataFixedFields
*) rdataPtr
;
16143 SRVRecordDataFixedFieldsGet( fields
, &priority
, &weight
, &port
);
16144 target
= (const uint8_t *) &fields
[ 1 ];
16148 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, target
, domainNameStr
, NULL
);
16149 require_noerr( err
, exit
);
16153 err
= DomainNameToString( target
, rdataEnd
, domainNameStr
, NULL
);
16154 require_noerr( err
, exit
);
16157 ASPrintF( &rdataStr
, "%u %u %u %s", priority
, weight
, port
, domainNameStr
);
16158 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
16160 else if( inRDataType
== kDNSServiceType_TXT
)
16162 require_action_quiet( inRDataLen
> 0, exit
, err
= kMalformedErr
);
16164 if( inRDataLen
== 1 )
16166 ASPrintF( &rdataStr
, "%#H", rdataPtr
, (int) inRDataLen
, INT_MAX
);
16167 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
16171 ASPrintF( &rdataStr
, "%#{txt}", rdataPtr
, inRDataLen
);
16172 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
16175 else if( inRDataType
== kDNSServiceType_SOA
)
16177 uint32_t serial
, refresh
, retry
, expire
, minimum
;
16181 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
16182 require_noerr( err
, exit
);
16184 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
16186 rdataStr
= strdup( domainNameStr
);
16187 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
16189 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, ptr
, domainNameStr
, &ptr
);
16190 require_noerr( err
, exit
);
16194 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
16195 require_noerr( err
, exit
);
16197 rdataStr
= strdup( domainNameStr
);
16198 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
16200 err
= DomainNameToString( ptr
, rdataEnd
, domainNameStr
, &ptr
);
16201 require_noerr( err
, exit
);
16204 require_action_quiet( ( rdataEnd
- ptr
) == sizeof( SOARecordDataFixedFields
), exit
, err
= kMalformedErr
);
16206 SOARecordDataFixedFieldsGet( (const SOARecordDataFixedFields
*) ptr
, &serial
, &refresh
, &retry
, &expire
, &minimum
);
16208 n
= AppendPrintF( &rdataStr
, " %s %u %u %u %u %u\n", domainNameStr
, serial
, refresh
, retry
, expire
, minimum
);
16209 require_action( n
> 0, exit
, err
= kUnknownErr
);
16211 else if( inRDataType
== kDNSServiceType_NSEC
)
16213 unsigned int windowBlock
, bitmapLen
, i
, recordType
;
16214 const uint8_t * bitmapPtr
;
16218 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
16219 require_noerr( err
, exit
);
16223 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
16224 require_noerr( err
, exit
);
16227 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
16229 rdataStr
= strdup( domainNameStr
);
16230 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
16232 for( ; ptr
< rdataEnd
; ptr
+= ( 2 + bitmapLen
) )
16234 require_action_quiet( ( ptr
+ 2 ) < rdataEnd
, exit
, err
= kMalformedErr
);
16236 windowBlock
= ptr
[ 0 ];
16237 bitmapLen
= ptr
[ 1 ];
16238 bitmapPtr
= &ptr
[ 2 ];
16240 require_action_quiet( ( bitmapLen
>= 1 ) && ( bitmapLen
<= 32 ) , exit
, err
= kMalformedErr
);
16241 require_action_quiet( ( bitmapPtr
+ bitmapLen
) <= rdataEnd
, exit
, err
= kMalformedErr
);
16243 for( i
= 0; i
< BitArray_MaxBits( bitmapLen
); ++i
)
16245 if( BitArray_GetBit( bitmapPtr
, bitmapLen
, i
) )
16247 recordType
= ( windowBlock
* 256 ) + i
;
16248 n
= AppendPrintF( &rdataStr
, " %s", RecordTypeToString( recordType
) );
16249 require_action( n
> 0, exit
, err
= kUnknownErr
);
16254 else if( inRDataType
== kDNSServiceType_MX
)
16256 uint16_t preference
;
16257 const uint8_t * exchange
;
16259 require_action_quiet( ( rdataPtr
+ 2 ) < rdataEnd
, exit
, err
= kMalformedErr
);
16261 preference
= ReadBig16( rdataPtr
);
16262 exchange
= &rdataPtr
[ 2 ];
16266 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, exchange
, domainNameStr
, NULL
);
16267 require_noerr( err
, exit
);
16271 err
= DomainNameToString( exchange
, rdataEnd
, domainNameStr
, NULL
);
16272 require_noerr( err
, exit
);
16275 n
= ASPrintF( &rdataStr
, "%u %s", preference
, domainNameStr
);
16276 require_action( n
> 0, exit
, err
= kUnknownErr
);
16280 err
= kNotHandledErr
;
16285 *outString
= rdataStr
;
16290 FreeNullSafe( rdataStr
);
16294 //===========================================================================================================================
16295 // DomainNameAppendString
16296 //===========================================================================================================================
16299 DomainNameAppendString(
16300 uint8_t inDomainName
[ kDomainNameLengthMax
],
16301 const char * inString
,
16302 uint8_t ** outEndPtr
)
16307 const uint8_t * const nameLim
= inDomainName
+ kDomainNameLengthMax
;
16309 for( root
= inDomainName
; ( root
< nameLim
) && *root
; root
+= ( 1 + *root
) ) {}
16310 require_action_quiet( root
< nameLim
, exit
, err
= kMalformedErr
);
16312 // If the string is a single dot, denoting the root domain, then there are no non-empty labels.
16315 if( ( src
[ 0 ] == '.' ) && ( src
[ 1 ] == '\0' ) ) ++src
;
16318 uint8_t * const label
= root
;
16319 const uint8_t * const labelLim
= Min( &label
[ 1 + kDomainLabelLengthMax
], nameLim
- 1 );
16325 while( *src
&& ( ( c
= *src
++ ) != '.' ) )
16329 require_action_quiet( *src
!= '\0', exit
, err
= kUnderrunErr
);
16331 if( isdigit_safe( c
) && isdigit_safe( src
[ 0 ] ) && isdigit_safe( src
[ 1 ] ) )
16333 const int decimal
= ( ( c
- '0' ) * 100 ) + ( ( src
[ 0 ] - '0' ) * 10 ) + ( src
[ 1 ] - '0' );
16335 if( decimal
<= 255 )
16342 require_action_quiet( dst
< labelLim
, exit
, err
= kOverrunErr
);
16343 *dst
++ = (uint8_t) c
;
16346 labelLen
= (size_t)( dst
- &label
[ 1 ] );
16347 require_action_quiet( labelLen
> 0, exit
, err
= kMalformedErr
);
16349 label
[ 0 ] = (uint8_t) labelLen
;
16354 if( outEndPtr
) *outEndPtr
= root
+ 1;
16361 //===========================================================================================================================
16363 //===========================================================================================================================
16365 static Boolean
DomainNameEqual( const uint8_t *inName1
, const uint8_t *inName2
)
16367 const uint8_t * p1
= inName1
;
16368 const uint8_t * p2
= inName2
;
16373 if( ( len
= *p1
++ ) != *p2
++ ) return( false );
16374 if( len
== 0 ) break;
16375 for( ; len
> 0; ++p1
, ++p2
, --len
)
16377 if( tolower_safe( *p1
) != tolower_safe( *p2
) ) return( false );
16383 //===========================================================================================================================
16384 // DomainNameLength
16385 //===========================================================================================================================
16387 static size_t DomainNameLength( const uint8_t * const inName
)
16389 const uint8_t * ptr
;
16391 for( ptr
= inName
; *ptr
!= 0; ptr
+= ( 1 + *ptr
) ) {}
16392 return( (size_t)( ptr
- inName
) + 1 );
16395 //===========================================================================================================================
16397 //===========================================================================================================================
16399 static OSStatus
DomainNameDupEx( const uint8_t *inName
, Boolean inLower
, uint8_t **outNamePtr
, size_t *outNameLen
)
16403 const size_t nameLen
= DomainNameLength( inName
);
16407 const uint8_t * src
;
16411 namePtr
= (uint8_t *) malloc( nameLen
);
16412 require_action( namePtr
, exit
, err
= kNoMemoryErr
);
16416 while( ( len
= *src
) != 0 )
16421 *dst
++ = (uint8_t) tolower_safe( *src
);
16429 namePtr
= (uint8_t *) memdup( inName
, nameLen
);
16430 require_action( namePtr
, exit
, err
= kNoMemoryErr
);
16433 *outNamePtr
= namePtr
;
16434 if( outNameLen
) *outNameLen
= nameLen
;
16441 //===========================================================================================================================
16442 // DomainNameFromString
16443 //===========================================================================================================================
16446 DomainNameFromString(
16447 uint8_t inDomainName
[ kDomainNameLengthMax
],
16448 const char * inString
,
16449 uint8_t ** outEndPtr
)
16451 inDomainName
[ 0 ] = 0;
16452 return( DomainNameAppendString( inDomainName
, inString
, outEndPtr
) );
16455 //===========================================================================================================================
16456 // DomainNameToString
16457 //===========================================================================================================================
16460 DomainNameToString(
16461 const uint8_t * inDomainName
,
16462 const uint8_t * inEnd
,
16463 char inBuf
[ kDNSServiceMaxDomainName
],
16464 const uint8_t ** outNextPtr
)
16467 const uint8_t * label
;
16469 const uint8_t * nextLabel
;
16471 const uint8_t * src
;
16473 require_action( !inEnd
|| ( inDomainName
< inEnd
), exit
, err
= kUnderrunErr
);
16475 // Convert each label up until the root label, i.e., the zero-length label.
16478 for( label
= inDomainName
; ( labelLen
= label
[ 0 ] ) != 0; label
= nextLabel
)
16480 require_action( labelLen
<= kDomainLabelLengthMax
, exit
, err
= kMalformedErr
);
16482 nextLabel
= &label
[ 1 ] + labelLen
;
16483 require_action( ( nextLabel
- inDomainName
) < kDomainNameLengthMax
, exit
, err
= kMalformedErr
);
16484 require_action( !inEnd
|| ( nextLabel
< inEnd
), exit
, err
= kUnderrunErr
);
16486 for( src
= &label
[ 1 ]; src
< nextLabel
; ++src
)
16488 if( isprint_safe( *src
) )
16490 if( ( *src
== '.' ) || ( *src
== '\\' ) || ( *src
== ' ' ) ) *dst
++ = '\\';
16491 *dst
++ = (char) *src
;
16496 *dst
++ = '0' + ( *src
/ 100 );
16497 *dst
++ = '0' + ( ( *src
/ 10 ) % 10 );
16498 *dst
++ = '0' + ( *src
% 10 );
16504 // At this point, label points to the root label.
16505 // If the root label was the only label, then write a dot for it.
16507 if( label
== inDomainName
) *dst
++ = '.';
16509 if( outNextPtr
) *outNextPtr
= label
+ 1;
16516 //===========================================================================================================================
16517 // DNSMessageToText
16518 //===========================================================================================================================
16520 #define DNSFlagsOpCodeToString( X ) ( \
16521 ( (X) == kDNSOpCode_Query ) ? "Query" : \
16522 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \
16523 ( (X) == kDNSOpCode_Status ) ? "Status" : \
16524 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \
16525 ( (X) == kDNSOpCode_Update ) ? "Update" : \
16528 #define DNSFlagsRCodeToString( X ) ( \
16529 ( (X) == kDNSRCode_NoError ) ? "NoError" : \
16530 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \
16531 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \
16532 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \
16533 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \
16534 ( (X) == kDNSRCode_Refused ) ? "Refused" : \
16539 const uint8_t * inMsgPtr
,
16541 const Boolean inMDNS
,
16542 const Boolean inPrintRaw
,
16546 DataBuffer dataBuf
;
16548 const DNSHeader
* hdr
;
16549 const uint8_t * ptr
;
16550 unsigned int id
, flags
, opcode
, rcode
;
16551 unsigned int questionCount
, answerCount
, authorityCount
, additionalCount
, i
, totalRRCount
;
16552 uint8_t name
[ kDomainNameLengthMax
];
16553 char nameStr
[ kDNSServiceMaxDomainName
];
16555 DataBuffer_Init( &dataBuf
, NULL
, 0, SIZE_MAX
);
16556 #define _Append( ... ) do { err = DataBuffer_AppendF( &dataBuf, __VA_ARGS__ ); require_noerr( err, exit ); } while( 0 )
16558 require_action_quiet( inMsgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
16560 hdr
= (DNSHeader
*) inMsgPtr
;
16561 id
= DNSHeaderGetID( hdr
);
16562 flags
= DNSHeaderGetFlags( hdr
);
16563 questionCount
= DNSHeaderGetQuestionCount( hdr
);
16564 answerCount
= DNSHeaderGetAnswerCount( hdr
);
16565 authorityCount
= DNSHeaderGetAuthorityCount( hdr
);
16566 additionalCount
= DNSHeaderGetAdditionalCount( hdr
);
16567 opcode
= DNSFlagsGetOpCode( flags
);
16568 rcode
= DNSFlagsGetRCode( flags
);
16570 _Append( "ID: 0x%04X (%u)\n", id
, id
);
16571 _Append( "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA%?s%?s %s\n",
16573 ( flags
& kDNSHeaderFlag_Response
) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode
),
16574 ( flags
& kDNSHeaderFlag_AuthAnswer
) ? ' ' : '!',
16575 ( flags
& kDNSHeaderFlag_Truncation
) ? ' ' : '!',
16576 ( flags
& kDNSHeaderFlag_RecursionDesired
) ? ' ' : '!',
16577 ( flags
& kDNSHeaderFlag_RecursionAvailable
) ? ' ' : '!',
16578 !inMDNS
, ( flags
& kDNSHeaderFlag_AuthenticData
) ? " AD" : "!AD",
16579 !inMDNS
, ( flags
& kDNSHeaderFlag_CheckingDisabled
) ? " CD" : "!CD",
16580 DNSFlagsRCodeToString( rcode
) );
16581 _Append( "Question count: %u\n", questionCount
);
16582 _Append( "Answer count: %u\n", answerCount
);
16583 _Append( "Authority count: %u\n", authorityCount
);
16584 _Append( "Additional count: %u\n", additionalCount
);
16586 ptr
= (const uint8_t *) &hdr
[ 1 ];
16587 for( i
= 0; i
< questionCount
; ++i
)
16589 uint16_t qtype
, qclass
;
16592 err
= DNSMessageExtractQuestion( inMsgPtr
, inMsgLen
, ptr
, name
, &qtype
, &qclass
, &ptr
);
16593 require_noerr( err
, exit
);
16595 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
16596 require_noerr( err
, exit
);
16598 isQU
= ( inMDNS
&& ( qclass
& kQClassUnicastResponseBit
) ) ? true : false;
16599 if( inMDNS
) qclass
&= ~kQClassUnicastResponseBit
;
16601 if( i
== 0 ) _Append( "\nQUESTION SECTION\n" );
16603 _Append( "%-30s %2s %?2s%?2u %-5s\n",
16604 nameStr
, inMDNS
? ( isQU
? "QU" : "QM" ) : "",
16605 ( qclass
== kDNSServiceClass_IN
), "IN", ( qclass
!= kDNSServiceClass_IN
), qclass
, RecordTypeToString( qtype
) );
16608 totalRRCount
= answerCount
+ authorityCount
+ additionalCount
;
16609 for( i
= 0; i
< totalRRCount
; ++i
)
16614 const uint8_t * rdataPtr
;
16617 Boolean cacheFlush
;
16619 err
= DNSMessageExtractRecord( inMsgPtr
, inMsgLen
, ptr
, name
, &type
, &class, &ttl
, &rdataPtr
, &rdataLen
, &ptr
);
16620 require_noerr( err
, exit
);
16622 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
16623 require_noerr( err
, exit
);
16625 cacheFlush
= ( inMDNS
&& ( class & kRRClassCacheFlushBit
) ) ? true : false;
16626 if( inMDNS
) class &= ~kRRClassCacheFlushBit
;
16629 if( !inPrintRaw
) DNSRecordDataToString( rdataPtr
, rdataLen
, type
, inMsgPtr
, inMsgLen
, &rdataStr
);
16632 ASPrintF( &rdataStr
, "%#H", rdataPtr
, (int) rdataLen
, INT_MAX
);
16633 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
16636 if( answerCount
&& ( i
== 0 ) ) _Append( "\nANSWER SECTION\n" );
16637 else if( authorityCount
&& ( i
== answerCount
) ) _Append( "\nAUTHORITY SECTION\n" );
16638 else if( additionalCount
&& ( i
== ( answerCount
+ authorityCount
) ) ) _Append( "\nADDITIONAL SECTION\n" );
16640 _Append( "%-42s %6u %2s %?2s%?2u %-5s %s\n",
16641 nameStr
, ttl
, cacheFlush
? "CF" : "",
16642 ( class == kDNSServiceClass_IN
), "IN", ( class != kDNSServiceClass_IN
), class,
16643 RecordTypeToString( type
), rdataStr
);
16648 err
= DataBuffer_Append( &dataBuf
, "", 1 );
16649 require_noerr( err
, exit
);
16651 err
= DataBuffer_Detach( &dataBuf
, (uint8_t **) outText
, &len
);
16652 require_noerr( err
, exit
);
16655 DataBuffer_Free( &dataBuf
);
16659 //===========================================================================================================================
16660 // WriteDNSQueryMessage
16661 //===========================================================================================================================
16664 WriteDNSQueryMessage(
16665 uint8_t inMsg
[ kDNSQueryMessageMaxLen
],
16668 const char * inQName
,
16671 size_t * outMsgLen
)
16674 DNSHeader
* const hdr
= (DNSHeader
*) inMsg
;
16678 memset( hdr
, 0, sizeof( *hdr
) );
16679 DNSHeaderSetID( hdr
, inMsgID
);
16680 DNSHeaderSetFlags( hdr
, inFlags
);
16681 DNSHeaderSetQuestionCount( hdr
, 1 );
16683 ptr
= (uint8_t *)( hdr
+ 1 );
16684 err
= DomainNameFromString( ptr
, inQName
, &ptr
);
16685 require_noerr_quiet( err
, exit
);
16687 DNSQuestionFixedFieldsInit( (DNSQuestionFixedFields
*) ptr
, inQType
, inQClass
);
16690 msgLen
= (size_t)( ptr
- inMsg
);
16691 check( msgLen
<= kDNSQueryMessageMaxLen
);
16693 if( outMsgLen
) *outMsgLen
= msgLen
;
16699 //===========================================================================================================================
16700 // DispatchSignalSourceCreate
16701 //===========================================================================================================================
16704 DispatchSignalSourceCreate(
16706 DispatchHandler inEventHandler
,
16708 dispatch_source_t
* outSource
)
16711 dispatch_source_t source
;
16713 source
= dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL
, (uintptr_t) inSignal
, 0, dispatch_get_main_queue() );
16714 require_action( source
, exit
, err
= kUnknownErr
);
16716 dispatch_set_context( source
, inContext
);
16717 dispatch_source_set_event_handler_f( source
, inEventHandler
);
16719 *outSource
= source
;
16726 //===========================================================================================================================
16727 // DispatchSocketSourceCreate
16728 //===========================================================================================================================
16731 DispatchSocketSourceCreate(
16733 dispatch_source_type_t inType
,
16734 dispatch_queue_t inQueue
,
16735 DispatchHandler inEventHandler
,
16736 DispatchHandler inCancelHandler
,
16738 dispatch_source_t
* outSource
)
16741 dispatch_source_t source
;
16743 source
= dispatch_source_create( inType
, (uintptr_t) inSock
, 0, inQueue
? inQueue
: dispatch_get_main_queue() );
16744 require_action( source
, exit
, err
= kUnknownErr
);
16746 dispatch_set_context( source
, inContext
);
16747 dispatch_source_set_event_handler_f( source
, inEventHandler
);
16748 dispatch_source_set_cancel_handler_f( source
, inCancelHandler
);
16750 *outSource
= source
;
16757 //===========================================================================================================================
16758 // DispatchTimerCreate
16759 //===========================================================================================================================
16762 DispatchTimerCreate(
16763 dispatch_time_t inStart
,
16764 uint64_t inIntervalNs
,
16765 uint64_t inLeewayNs
,
16766 dispatch_queue_t inQueue
,
16767 DispatchHandler inEventHandler
,
16768 DispatchHandler inCancelHandler
,
16770 dispatch_source_t
* outTimer
)
16773 dispatch_source_t timer
;
16775 timer
= dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, inQueue
? inQueue
: dispatch_get_main_queue() );
16776 require_action( timer
, exit
, err
= kUnknownErr
);
16778 dispatch_source_set_timer( timer
, inStart
, inIntervalNs
, inLeewayNs
);
16779 dispatch_set_context( timer
, inContext
);
16780 dispatch_source_set_event_handler_f( timer
, inEventHandler
);
16781 dispatch_source_set_cancel_handler_f( timer
, inCancelHandler
);
16790 //===========================================================================================================================
16791 // DispatchProcessMonitorCreate
16792 //===========================================================================================================================
16795 DispatchProcessMonitorCreate(
16797 unsigned long inFlags
,
16798 dispatch_queue_t inQueue
,
16799 DispatchHandler inEventHandler
,
16800 DispatchHandler inCancelHandler
,
16802 dispatch_source_t
* outMonitor
)
16805 dispatch_source_t monitor
;
16807 monitor
= dispatch_source_create( DISPATCH_SOURCE_TYPE_PROC
, (uintptr_t) inPID
, inFlags
,
16808 inQueue
? inQueue
: dispatch_get_main_queue() );
16809 require_action( monitor
, exit
, err
= kUnknownErr
);
16811 dispatch_set_context( monitor
, inContext
);
16812 dispatch_source_set_event_handler_f( monitor
, inEventHandler
);
16813 dispatch_source_set_cancel_handler_f( monitor
, inCancelHandler
);
16815 *outMonitor
= monitor
;
16822 //===========================================================================================================================
16823 // ServiceTypeDescription
16824 //===========================================================================================================================
16828 const char * name
; // Name of the service type in two-label "_service._proto" format.
16829 const char * description
; // Description of the service type.
16833 // A Non-comprehensive table of DNS-SD service types
16835 static const ServiceType kServiceTypes
[] =
16837 { "_acp-sync._tcp", "AirPort Base Station Sync" },
16838 { "_adisk._tcp", "Automatic Disk Discovery" },
16839 { "_afpovertcp._tcp", "Apple File Sharing" },
16840 { "_airdrop._tcp", "AirDrop" },
16841 { "_airplay._tcp", "AirPlay" },
16842 { "_airport._tcp", "AirPort Base Station" },
16843 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
16844 { "_eppc._tcp", "Remote AppleEvents" },
16845 { "_ftp._tcp", "File Transfer Protocol" },
16846 { "_home-sharing._tcp", "Home Sharing" },
16847 { "_homekit._tcp", "HomeKit" },
16848 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
16849 { "_https._tcp", "HTTP over SSL/TLS" },
16850 { "_ipp._tcp", "Internet Printing Protocol" },
16851 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
16852 { "_mediaremotetv._tcp", "Media Remote" },
16853 { "_net-assistant._tcp", "Apple Remote Desktop" },
16854 { "_od-master._tcp", "OpenDirectory Master" },
16855 { "_nfs._tcp", "Network File System" },
16856 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
16857 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
16858 { "_raop._tcp", "Remote Audio Output Protocol" },
16859 { "_rfb._tcp", "Remote Frame Buffer" },
16860 { "_scanner._tcp", "Bonjour Scanning" },
16861 { "_smb._tcp", "Server Message Block over TCP/IP" },
16862 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
16863 { "_sleep-proxy._udp", "Sleep Proxy Server" },
16864 { "_ssh._tcp", "SSH Remote Login Protocol" },
16865 { "_teleport._tcp", "teleport" },
16866 { "_tftp._tcp", "Trivial File Transfer Protocol" },
16867 { "_workstation._tcp", "Workgroup Manager" },
16868 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
16869 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
16872 static const char * ServiceTypeDescription( const char *inName
)
16874 const ServiceType
* serviceType
;
16875 const ServiceType
* const end
= kServiceTypes
+ countof( kServiceTypes
);
16877 for( serviceType
= kServiceTypes
; serviceType
< end
; ++serviceType
)
16879 if( ( stricmp_prefix( inName
, serviceType
->name
) == 0 ) )
16881 const size_t len
= strlen( serviceType
->name
);
16883 if( ( inName
[ len
] == '\0' ) || ( strcmp( &inName
[ len
], "." ) == 0 ) )
16885 return( serviceType
->description
);
16892 //===========================================================================================================================
16893 // SocketContextCreate
16894 //===========================================================================================================================
16896 static OSStatus
SocketContextCreate( SocketRef inSock
, void * inUserContext
, SocketContext
**outContext
)
16899 SocketContext
* context
;
16901 context
= (SocketContext
*) calloc( 1, sizeof( *context
) );
16902 require_action( context
, exit
, err
= kNoMemoryErr
);
16904 context
->refCount
= 1;
16905 context
->sock
= inSock
;
16906 context
->userContext
= inUserContext
;
16908 *outContext
= context
;
16915 //===========================================================================================================================
16916 // SocketContextRetain
16917 //===========================================================================================================================
16919 static SocketContext
* SocketContextRetain( SocketContext
*inContext
)
16921 ++inContext
->refCount
;
16922 return( inContext
);
16925 //===========================================================================================================================
16926 // SocketContextRelease
16927 //===========================================================================================================================
16929 static void SocketContextRelease( SocketContext
*inContext
)
16931 if( --inContext
->refCount
== 0 )
16933 ForgetSocket( &inContext
->sock
);
16938 //===========================================================================================================================
16939 // SocketContextCancelHandler
16940 //===========================================================================================================================
16942 static void SocketContextCancelHandler( void *inContext
)
16944 SocketContextRelease( (SocketContext
*) inContext
);
16947 //===========================================================================================================================
16949 //===========================================================================================================================
16951 static OSStatus
StringToInt32( const char *inString
, int32_t *outValue
)
16957 value
= strtol( inString
, &endPtr
, 0 );
16958 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
16959 require_action_quiet( ( value
>= INT32_MIN
) && ( value
<= INT32_MAX
), exit
, err
= kRangeErr
);
16961 *outValue
= (int32_t) value
;
16968 //===========================================================================================================================
16970 //===========================================================================================================================
16972 static OSStatus
StringToUInt32( const char *inString
, uint32_t *outValue
)
16978 value
= (uint32_t) strtol( inString
, &endPtr
, 0 );
16979 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
16988 #if( TARGET_OS_DARWIN )
16989 //===========================================================================================================================
16991 //===========================================================================================================================
16993 static OSStatus
StringToPID( const char *inString
, pid_t
*outPID
)
16999 set_errno_compat( 0 );
17000 value
= strtoll( inString
, &endPtr
, 0 );
17001 err
= errno_compat();
17002 require_noerr_quiet( err
, exit
);
17003 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kMalformedErr
);
17004 require_action_quiet( value
== (pid_t
) value
, exit
, err
= kRangeErr
);
17006 *outPID
= (pid_t
) value
;
17014 //===========================================================================================================================
17015 // StringToARecordData
17016 //===========================================================================================================================
17018 static OSStatus
StringToARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
17021 uint32_t * addrPtr
;
17022 const size_t addrLen
= sizeof( *addrPtr
);
17025 addrPtr
= (uint32_t *) malloc( addrLen
);
17026 require_action( addrPtr
, exit
, err
= kNoMemoryErr
);
17028 err
= StringToIPv4Address( inString
, kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
, addrPtr
,
17029 NULL
, NULL
, NULL
, &end
);
17030 if( !err
&& ( *end
!= '\0' ) ) err
= kMalformedErr
;
17031 require_noerr_quiet( err
, exit
);
17033 *addrPtr
= HostToBig32( *addrPtr
);
17035 *outPtr
= (uint8_t *) addrPtr
;
17040 FreeNullSafe( addrPtr
);
17044 //===========================================================================================================================
17045 // StringToAAAARecordData
17046 //===========================================================================================================================
17048 static OSStatus
StringToAAAARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
17052 const size_t addrLen
= 16;
17055 addrPtr
= (uint8_t *) malloc( addrLen
);
17056 require_action( addrPtr
, exit
, err
= kNoMemoryErr
);
17058 err
= StringToIPv6Address( inString
,
17059 kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
| kStringToIPAddressFlagsNoScope
,
17060 addrPtr
, NULL
, NULL
, NULL
, &end
);
17061 if( !err
&& ( *end
!= '\0' ) ) err
= kMalformedErr
;
17062 require_noerr_quiet( err
, exit
);
17069 FreeNullSafe( addrPtr
);
17073 //===========================================================================================================================
17074 // StringToDomainName
17075 //===========================================================================================================================
17077 static OSStatus
StringToDomainName( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
17083 uint8_t nameBuf
[ kDomainNameLengthMax
];
17085 err
= DomainNameFromString( nameBuf
, inString
, &end
);
17086 require_noerr_quiet( err
, exit
);
17088 nameLen
= (size_t)( end
- nameBuf
);
17089 namePtr
= memdup( nameBuf
, nameLen
);
17090 require_action( namePtr
, exit
, err
= kNoMemoryErr
);
17094 if( outLen
) *outLen
= nameLen
;
17100 #if( TARGET_OS_DARWIN )
17101 //===========================================================================================================================
17102 // GetDefaultDNSServer
17103 //===========================================================================================================================
17105 static OSStatus
GetDefaultDNSServer( sockaddr_ip
*outAddr
)
17108 dns_config_t
* config
;
17109 struct sockaddr
* addr
;
17112 config
= dns_configuration_copy();
17113 require_action( config
, exit
, err
= kUnknownErr
);
17116 for( i
= 0; i
< config
->n_resolver
; ++i
)
17118 const dns_resolver_t
* const resolver
= config
->resolver
[ i
];
17120 if( !resolver
->domain
&& ( resolver
->n_nameserver
> 0 ) )
17122 addr
= resolver
->nameserver
[ 0 ];
17126 require_action_quiet( addr
, exit
, err
= kNotFoundErr
);
17128 SockAddrCopy( addr
, outAddr
);
17132 if( config
) dns_configuration_free( config
);
17137 //===========================================================================================================================
17138 // GetMDNSMulticastAddrV4
17139 //===========================================================================================================================
17141 static void _MDNSMulticastAddrV4Init( void *inContext
);
17143 static const struct sockaddr
* GetMDNSMulticastAddrV4( void )
17145 static struct sockaddr_in sMDNSMulticastAddrV4
;
17146 static dispatch_once_t sMDNSMulticastAddrV4InitOnce
= 0;
17148 dispatch_once_f( &sMDNSMulticastAddrV4InitOnce
, &sMDNSMulticastAddrV4
, _MDNSMulticastAddrV4Init
);
17149 return( (const struct sockaddr
*) &sMDNSMulticastAddrV4
);
17152 static void _MDNSMulticastAddrV4Init( void *inContext
)
17154 struct sockaddr_in
* const addr
= (struct sockaddr_in
*) inContext
;
17156 memset( addr
, 0, sizeof( *addr
) );
17157 SIN_LEN_SET( addr
);
17158 addr
->sin_family
= AF_INET
;
17159 addr
->sin_port
= htons( kMDNSPort
);
17160 addr
->sin_addr
.s_addr
= htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251
17163 //===========================================================================================================================
17164 // GetMDNSMulticastAddrV6
17165 //===========================================================================================================================
17167 static void _MDNSMulticastAddrV6Init( void *inContext
);
17169 static const struct sockaddr
* GetMDNSMulticastAddrV6( void )
17171 static struct sockaddr_in6 sMDNSMulticastAddrV6
;
17172 static dispatch_once_t sMDNSMulticastAddrV6InitOnce
= 0;
17174 dispatch_once_f( &sMDNSMulticastAddrV6InitOnce
, &sMDNSMulticastAddrV6
, _MDNSMulticastAddrV6Init
);
17175 return( (const struct sockaddr
*) &sMDNSMulticastAddrV6
);
17178 static void _MDNSMulticastAddrV6Init( void *inContext
)
17180 struct sockaddr_in6
* const addr
= (struct sockaddr_in6
*) inContext
;
17182 memset( addr
, 0, sizeof( *addr
) );
17183 SIN6_LEN_SET( addr
);
17184 addr
->sin6_family
= AF_INET6
;
17185 addr
->sin6_port
= htons( kMDNSPort
);
17186 addr
->sin6_addr
.s6_addr
[ 0 ] = 0xFF; // The mDNS IPv6 multicast address is FF02::FB.
17187 addr
->sin6_addr
.s6_addr
[ 1 ] = 0x02;
17188 addr
->sin6_addr
.s6_addr
[ 15 ] = 0xFB;
17191 //===========================================================================================================================
17192 // GetAnyMDNSInterface
17193 //===========================================================================================================================
17195 static OSStatus
GetAnyMDNSInterface( char inNameBuf
[ IF_NAMESIZE
+ 1 ], uint32_t *outIndex
)
17198 struct ifaddrs
* ifaList
;
17199 const struct ifaddrs
* ifa
;
17200 const struct ifaddrs
* ifa2
;
17201 const char * ifname
= NULL
;
17202 const unsigned int checkFlags
= IFF_UP
| IFF_MULTICAST
| IFF_LOOPBACK
| IFF_POINTOPOINT
;
17203 const unsigned int wantFlags
= IFF_UP
| IFF_MULTICAST
;
17205 NetTransportType type
;
17208 err
= getifaddrs( &ifaList
);
17209 err
= map_global_noerr_errno( err
);
17210 require_noerr( err
, exit
);
17212 for( ifa
= ifaList
; ifa
; ifa
= ifa
->ifa_next
)
17214 if( ( ifa
->ifa_flags
& checkFlags
) != wantFlags
) continue;
17215 if( !ifa
->ifa_addr
|| !ifa
->ifa_name
) continue;
17216 if( ( ifa
->ifa_addr
->sa_family
!= AF_INET
) &&
17217 ( ifa
->ifa_addr
->sa_family
!= AF_INET6
) ) continue;
17219 err
= SocketGetInterfaceInfo( kInvalidSocketRef
, ifa
->ifa_name
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &type
);
17220 check_noerr( err
);
17221 if( err
|| ( type
== kNetTransportType_AWDL
) ) continue;
17223 if( !ifname
) ifname
= ifa
->ifa_name
;
17224 wantFamily
= ( ifa
->ifa_addr
->sa_family
== AF_INET
) ? AF_INET6
: AF_INET
;
17226 for( ifa2
= ifa
->ifa_next
; ifa2
; ifa2
= ifa2
->ifa_next
)
17228 if( ( ifa2
->ifa_flags
& checkFlags
) != wantFlags
) continue;
17229 if( !ifa2
->ifa_addr
|| !ifa2
->ifa_name
) continue;
17230 if( ifa2
->ifa_addr
->sa_family
!= wantFamily
) continue;
17231 if( strcmp( ifa2
->ifa_name
, ifa
->ifa_name
) == 0 ) break;
17235 ifname
= ifa
->ifa_name
;
17239 require_action_quiet( ifname
, exit
, err
= kNotFoundErr
);
17241 if( inNameBuf
) strlcpy( inNameBuf
, ifname
, IF_NAMESIZE
+ 1 );
17242 if( outIndex
) *outIndex
= if_nametoindex( ifname
);
17245 if( ifaList
) freeifaddrs( ifaList
);
17249 //===========================================================================================================================
17250 // CreateMulticastSocket
17251 //===========================================================================================================================
17254 CreateMulticastSocket(
17255 const struct sockaddr
* inAddr
,
17257 const char * inIfName
,
17258 uint32_t inIfIndex
,
17261 SocketRef
* outSock
)
17264 SocketRef sock
= kInvalidSocketRef
;
17265 const int family
= inAddr
->sa_family
;
17268 require_action_quiet( ( family
== AF_INET
) ||( family
== AF_INET6
), exit
, err
= kUnsupportedErr
);
17270 err
= ServerSocketOpen( family
, SOCK_DGRAM
, IPPROTO_UDP
, inPort
, &port
, kSocketBufferSize_DontSet
, &sock
);
17271 require_noerr_quiet( err
, exit
);
17273 err
= SocketSetMulticastInterface( sock
, inIfName
, inIfIndex
);
17274 require_noerr_quiet( err
, exit
);
17276 if( family
== AF_INET
)
17278 err
= setsockopt( sock
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *) &(uint8_t){ 1 }, (socklen_t
) sizeof( uint8_t ) );
17279 err
= map_socket_noerr_errno( sock
, err
);
17280 require_noerr_quiet( err
, exit
);
17284 err
= setsockopt( sock
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *) &(int){ 1 }, (socklen_t
) sizeof( int ) );
17285 err
= map_socket_noerr_errno( sock
, err
);
17286 require_noerr_quiet( err
, exit
);
17291 err
= SocketJoinMulticast( sock
, inAddr
, inIfName
, inIfIndex
);
17292 require_noerr_quiet( err
, exit
);
17295 if( outPort
) *outPort
= port
;
17297 sock
= kInvalidSocketRef
;
17300 ForgetSocket( &sock
);
17304 //===========================================================================================================================
17305 // DecimalTextToUInt32
17306 //===========================================================================================================================
17308 static OSStatus
DecimalTextToUInt32( const char *inSrc
, const char *inEnd
, uint32_t *outValue
, const char **outPtr
)
17312 const char * ptr
= inSrc
;
17314 require_action_quiet( ( ptr
< inEnd
) && isdigit_safe( *ptr
), exit
, err
= kMalformedErr
);
17316 value
= (uint64_t)( *ptr
++ - '0' );
17319 if( ( ptr
< inEnd
) && isdigit_safe( *ptr
) )
17321 err
= kMalformedErr
;
17327 while( ( ptr
< inEnd
) && isdigit_safe( *ptr
) )
17329 value
= ( value
* 10 ) + (uint64_t)( *ptr
++ - '0' );
17330 require_action_quiet( value
<= UINT32_MAX
, exit
, err
= kRangeErr
);
17334 *outValue
= (uint32_t) value
;
17335 if( outPtr
) *outPtr
= ptr
;
17342 //===========================================================================================================================
17343 // CheckIntegerArgument
17344 //===========================================================================================================================
17346 static OSStatus
CheckIntegerArgument( int inArgValue
, const char *inArgName
, int inMin
, int inMax
)
17348 if( ( inArgValue
>= inMin
) && ( inArgValue
<= inMax
) ) return( kNoErr
);
17350 FPrintF( stderr
, "error: Invalid %s: %d. Valid range is [%d, %d].\n", inArgName
, inArgValue
, inMin
, inMax
);
17351 return( kRangeErr
);
17354 //===========================================================================================================================
17355 // CheckDoubleArgument
17356 //===========================================================================================================================
17358 static OSStatus
CheckDoubleArgument( double inArgValue
, const char *inArgName
, double inMin
, double inMax
)
17360 if( ( inArgValue
>= inMin
) && ( inArgValue
<= inMax
) ) return( kNoErr
);
17362 FPrintF( stderr
, "error: Invalid %s: %.1f. Valid range is [%.1f, %.1f].\n", inArgName
, inArgValue
, inMin
, inMax
);
17363 return( kRangeErr
);
17366 //===========================================================================================================================
17368 //===========================================================================================================================
17370 static OSStatus
CheckRootUser( void )
17372 if( geteuid() == 0 ) return( kNoErr
);
17374 FPrintF( stderr
, "error: This command must to be run as root.\n" );
17375 return( kPermissionErr
);
17378 //===========================================================================================================================
17381 // Note: Based on systemf() from CoreUtils framework.
17382 //===========================================================================================================================
17384 extern char ** environ
;
17386 static OSStatus
SpawnCommand( pid_t
*outPID
, const char *inFormat
, ... )
17395 va_start( args
, inFormat
);
17396 VASPrintF( &command
, inFormat
, args
);
17398 require_action( command
, exit
, err
= kUnknownErr
);
17400 argv
[ 0 ] = "/bin/sh";
17402 argv
[ 2 ] = command
;
17404 err
= posix_spawn( &pid
, argv
[ 0 ], NULL
, NULL
, argv
, environ
);
17406 require_noerr_quiet( err
, exit
);
17408 if( outPID
) *outPID
= pid
;
17414 //===========================================================================================================================
17415 // OutputPropertyList
17416 //===========================================================================================================================
17419 OutputPropertyList(
17420 CFPropertyListRef inPList
,
17421 OutputFormatType inType
,
17422 Boolean inAppendNewline
,
17423 const char * inOutputFilePath
)
17426 CFDataRef results
= NULL
;
17427 FILE * file
= NULL
;
17429 // Convert plist to a specific format.
17433 case kOutputFormatType_JSON
:
17434 results
= CFCreateJSONData( inPList
, kJSONFlags_None
, NULL
);
17435 require_action( results
, exit
, err
= kUnknownErr
);
17438 case kOutputFormatType_XML
:
17439 results
= CFPropertyListCreateData( NULL
, inPList
, kCFPropertyListXMLFormat_v1_0
, 0, NULL
);
17440 require_action( results
, exit
, err
= kUnknownErr
);
17443 case kOutputFormatType_Binary
:
17444 results
= CFPropertyListCreateData( NULL
, inPList
, kCFPropertyListBinaryFormat_v1_0
, 0, NULL
);
17445 require_action( results
, exit
, err
= kUnknownErr
);
17453 // Write formatted results to file or stdout.
17455 if( inOutputFilePath
)
17457 file
= fopen( inOutputFilePath
, "wb" );
17458 err
= map_global_value_errno( file
, file
);
17459 require_noerr( err
, exit
);
17466 err
= WriteANSIFile( file
, CFDataGetBytePtr( results
), (size_t) CFDataGetLength( results
) );
17467 require_noerr_quiet( err
, exit
);
17469 // Write a trailing newline for JSON-formatted results if requested.
17471 if( ( inType
== kOutputFormatType_JSON
) && inAppendNewline
)
17473 err
= WriteANSIFile( file
, "\n", 1 );
17474 require_noerr_quiet( err
, exit
);
17478 if( file
&& ( file
!= stdout
) ) fclose( file
);
17479 CFReleaseNullSafe( results
);
17483 //===========================================================================================================================
17484 // DNSRecordFixedFieldsSet
17485 //===========================================================================================================================
17488 DNSRecordFixedFieldsSet(
17489 DNSRecordFixedFields
* inFields
,
17493 uint16_t inRDLength
)
17495 WriteBig16( inFields
->type
, inType
);
17496 WriteBig16( inFields
->class, inClass
);
17497 WriteBig32( inFields
->ttl
, inTTL
);
17498 WriteBig16( inFields
->rdlength
, inRDLength
);
17501 //===========================================================================================================================
17502 // SRVRecordDataFixedFieldsGet
17503 //===========================================================================================================================
17506 SRVRecordDataFixedFieldsGet(
17507 const SRVRecordDataFixedFields
* inFields
,
17508 unsigned int * outPriority
,
17509 unsigned int * outWeight
,
17510 unsigned int * outPort
)
17512 if( outPriority
) *outPriority
= ReadBig16( inFields
->priority
);
17513 if( outWeight
) *outWeight
= ReadBig16( inFields
->weight
);
17514 if( outPort
) *outPort
= ReadBig16( inFields
->port
);
17517 //===========================================================================================================================
17518 // SRVRecordDataFixedFieldsSet
17519 //===========================================================================================================================
17522 SRVRecordDataFixedFieldsSet(
17523 SRVRecordDataFixedFields
* inFields
,
17524 uint16_t inPriority
,
17528 WriteBig16( inFields
->priority
, inPriority
);
17529 WriteBig16( inFields
->weight
, inWeight
);
17530 WriteBig16( inFields
->port
, inPort
);
17533 //===========================================================================================================================
17534 // SOARecordDataFixedFieldsGet
17535 //===========================================================================================================================
17538 SOARecordDataFixedFieldsGet(
17539 const SOARecordDataFixedFields
* inFields
,
17540 uint32_t * outSerial
,
17541 uint32_t * outRefresh
,
17542 uint32_t * outRetry
,
17543 uint32_t * outExpire
,
17544 uint32_t * outMinimum
)
17546 if( outSerial
) *outSerial
= ReadBig32( inFields
->serial
);
17547 if( outRefresh
) *outRefresh
= ReadBig32( inFields
->refresh
);
17548 if( outRetry
) *outRetry
= ReadBig32( inFields
->retry
);
17549 if( outExpire
) *outExpire
= ReadBig32( inFields
->expire
);
17550 if( outMinimum
) *outMinimum
= ReadBig32( inFields
->minimum
);
17553 //===========================================================================================================================
17554 // SOARecordDataFixedFieldsSet
17555 //===========================================================================================================================
17558 SOARecordDataFixedFieldsSet(
17559 SOARecordDataFixedFields
* inFields
,
17561 uint32_t inRefresh
,
17564 uint32_t inMinimum
)
17566 WriteBig32( inFields
->serial
, inSerial
);
17567 WriteBig32( inFields
->refresh
, inRefresh
);
17568 WriteBig32( inFields
->retry
, inRetry
);
17569 WriteBig32( inFields
->expire
, inExpire
);
17570 WriteBig32( inFields
->minimum
, inMinimum
);
17573 //===========================================================================================================================
17574 // CreateSRVRecordDataFromString
17575 //===========================================================================================================================
17577 static OSStatus
CreateSRVRecordDataFromString( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
17580 DataBuffer dataBuf
;
17584 uint8_t target
[ kDomainNameLengthMax
];
17586 DataBuffer_Init( &dataBuf
, NULL
, 0, ( 3 * 2 ) + kDomainNameLengthMax
);
17588 // Parse and set the priority, weight, and port values (all three are unsigned 16-bit values).
17591 for( i
= 0; i
< 3; ++i
)
17597 value
= strtol( ptr
, &next
, 0 );
17598 require_action_quiet( ( next
!= ptr
) && ( *next
== ',' ), exit
, err
= kMalformedErr
);
17599 require_action_quiet( ( value
>= 0 ) && ( value
<= UINT16_MAX
), exit
, err
= kRangeErr
);
17602 WriteBig16( buf
, value
);
17604 err
= DataBuffer_Append( &dataBuf
, buf
, sizeof( buf
) );
17605 require_noerr( err
, exit
);
17608 // Set the target domain name.
17610 err
= DomainNameFromString( target
, ptr
, &end
);
17611 require_noerr_quiet( err
, exit
);
17613 err
= DataBuffer_Append( &dataBuf
, target
, (size_t)( end
- target
) );
17614 require_noerr( err
, exit
);
17616 err
= DataBuffer_Detach( &dataBuf
, outPtr
, outLen
);
17617 require_noerr( err
, exit
);
17620 DataBuffer_Free( &dataBuf
);
17624 //===========================================================================================================================
17625 // CreateTXTRecordDataFromString
17626 //===========================================================================================================================
17628 static OSStatus
CreateTXTRecordDataFromString(const char *inString
, int inDelimiter
, uint8_t **outPtr
, size_t *outLen
)
17631 DataBuffer dataBuf
;
17633 uint8_t txtStr
[ 256 ]; // Buffer for single TXT string: 1 length byte + up to 255 bytes of data.
17635 DataBuffer_Init( &dataBuf
, NULL
, 0, kDNSRecordDataLengthMax
);
17640 uint8_t * dst
= &txtStr
[ 1 ];
17641 const uint8_t * const lim
= &txtStr
[ 256 ];
17644 while( *src
&& ( *src
!= inDelimiter
) )
17646 if( ( c
= *src
++ ) == '\\' )
17648 require_action_quiet( *src
!= '\0', exit
, err
= kUnderrunErr
);
17651 require_action_quiet( dst
< lim
, exit
, err
= kOverrunErr
);
17652 *dst
++ = (uint8_t) c
;
17654 txtStr
[ 0 ] = (uint8_t)( dst
- &txtStr
[ 1 ] );
17655 err
= DataBuffer_Append( &dataBuf
, txtStr
, 1 + txtStr
[ 0 ] );
17656 require_noerr( err
, exit
);
17658 if( *src
== '\0' ) break;
17662 err
= DataBuffer_Detach( &dataBuf
, outPtr
, outLen
);
17663 require_noerr( err
, exit
);
17666 DataBuffer_Free( &dataBuf
);
17670 //===========================================================================================================================
17671 // CreateNSECRecordData
17672 //===========================================================================================================================
17674 DECLARE_QSORT_NUMERIC_COMPARATOR( _QSortCmpUnsigned
);
17675 DEFINE_QSORT_NUMERIC_COMPARATOR( unsigned int, _QSortCmpUnsigned
)
17677 #define kNSECBitmapMaxLength 32 // 32 bytes (256 bits). See <https://tools.ietf.org/html/rfc4034#section-4.1.2>.
17680 CreateNSECRecordData(
17681 const uint8_t * inNextDomainName
,
17684 unsigned int inTypeCount
,
17689 DataBuffer rdataDB
;
17690 unsigned int * array
= NULL
;
17691 unsigned int i
, type
, maxBit
, currBlock
, bitmapLen
;
17692 uint8_t fields
[ 2 + kNSECBitmapMaxLength
];
17693 uint8_t * const bitmap
= &fields
[ 2 ];
17695 va_start( args
, inTypeCount
);
17696 DataBuffer_Init( &rdataDB
, NULL
, 0, kDNSRecordDataLengthMax
);
17698 // Append Next Domain Name.
17700 err
= DataBuffer_Append( &rdataDB
, inNextDomainName
, DomainNameLength( inNextDomainName
) );
17701 require_noerr( err
, exit
);
17703 // Append Type Bit Maps.
17706 memset( bitmap
, 0, kNSECBitmapMaxLength
);
17707 if( inTypeCount
> 0 )
17709 array
= (unsigned int *) malloc( inTypeCount
* sizeof_element( array
) );
17710 require_action( array
, exit
, err
= kNoMemoryErr
);
17712 for( i
= 0; i
< inTypeCount
; ++i
)
17714 type
= va_arg( args
, unsigned int );
17715 require_action_quiet( type
<= UINT16_MAX
, exit
, err
= kRangeErr
);
17718 qsort( array
, inTypeCount
, sizeof_element( array
), _QSortCmpUnsigned
);
17720 currBlock
= array
[ 0 ] / 256;
17721 for( i
= 0; i
< inTypeCount
; ++i
)
17723 const unsigned int block
= array
[ i
] / 256;
17724 const unsigned int bit
= array
[ i
] % 256;
17726 if( block
!= currBlock
)
17728 bitmapLen
= BitArray_MaxBytes( maxBit
+ 1 );
17729 fields
[ 0 ] = (uint8_t) currBlock
;
17730 fields
[ 1 ] = (uint8_t) bitmapLen
;
17732 err
= DataBuffer_Append( &rdataDB
, fields
, 2 + bitmapLen
);
17733 require_noerr( err
, exit
);
17737 memset( bitmap
, 0, bitmapLen
);
17739 BitArray_SetBit( bitmap
, bit
);
17740 if( bit
> maxBit
) maxBit
= bit
;
17748 bitmapLen
= BitArray_MaxBytes( maxBit
+ 1 );
17749 fields
[ 0 ] = (uint8_t) currBlock
;
17750 fields
[ 1 ] = (uint8_t) bitmapLen
;
17752 err
= DataBuffer_Append( &rdataDB
, fields
, 2 + bitmapLen
);
17753 require_noerr( err
, exit
);
17755 err
= DataBuffer_Detach( &rdataDB
, outPtr
, outLen
);
17756 require_noerr( err
, exit
);
17760 DataBuffer_Free( &rdataDB
);
17761 FreeNullSafe( array
);
17765 //===========================================================================================================================
17767 //===========================================================================================================================
17770 _AppendSOARecordData(
17772 const uint8_t * inMName
,
17773 const uint8_t * inRName
,
17775 uint32_t inRefresh
,
17778 uint32_t inMinimumTTL
,
17784 const uint8_t * inNamePtr
,
17789 const uint8_t * inMName
,
17790 const uint8_t * inRName
,
17792 uint32_t inRefresh
,
17795 uint32_t inMinimumTTL
,
17800 size_t rdlengthOffset
= 0;
17801 uint8_t * rdlengthPtr
;
17805 err
= _DataBuffer_AppendDNSRecord( inDB
, inNamePtr
, inNameLen
, inType
, inClass
, inTTL
, NULL
, 0 );
17806 require_noerr( err
, exit
);
17808 rdlengthOffset
= DataBuffer_GetLen( inDB
) - 2;
17811 err
= _AppendSOARecordData( inDB
, inMName
, inRName
, inSerial
, inRefresh
, inRetry
, inExpire
, inMinimumTTL
, &rdataLen
);
17812 require_noerr( err
, exit
);
17816 rdlengthPtr
= DataBuffer_GetPtr( inDB
) + rdlengthOffset
;
17817 WriteBig16( rdlengthPtr
, rdataLen
);
17820 if( outLen
) *outLen
= inNameLen
+ sizeof( DNSRecordFixedFields
) + rdataLen
;
17828 _AppendSOARecordData(
17830 const uint8_t * inMName
,
17831 const uint8_t * inRName
,
17833 uint32_t inRefresh
,
17836 uint32_t inMinimumTTL
,
17840 SOARecordDataFixedFields fields
;
17841 const size_t mnameLen
= DomainNameLength( inMName
);
17842 const size_t rnameLen
= DomainNameLength( inRName
);
17846 err
= DataBuffer_Append( inDB
, inMName
, mnameLen
);
17847 require_noerr( err
, exit
);
17849 err
= DataBuffer_Append( inDB
, inRName
, rnameLen
);
17850 require_noerr( err
, exit
);
17852 SOARecordDataFixedFieldsSet( &fields
, inSerial
, inRefresh
, inRetry
, inExpire
, inMinimumTTL
);
17853 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
17854 require_noerr( err
, exit
);
17856 if( outLen
) *outLen
= mnameLen
+ rnameLen
+ sizeof( fields
);
17863 //===========================================================================================================================
17864 // CreateSOARecordData
17865 //===========================================================================================================================
17868 CreateSOARecordData(
17869 const uint8_t * inMName
,
17870 const uint8_t * inRName
,
17872 uint32_t inRefresh
,
17875 uint32_t inMinimumTTL
,
17880 DataBuffer rdataDB
;
17882 DataBuffer_Init( &rdataDB
, NULL
, 0, kDNSRecordDataLengthMax
);
17884 err
= _AppendSOARecordData( &rdataDB
, inMName
, inRName
, inSerial
, inRefresh
, inRetry
, inExpire
, inMinimumTTL
, NULL
);
17885 require_noerr( err
, exit
);
17887 err
= DataBuffer_Detach( &rdataDB
, outPtr
, outLen
);
17888 require_noerr( err
, exit
);
17891 DataBuffer_Free( &rdataDB
);
17895 //===========================================================================================================================
17896 // _DataBuffer_AppendDNSQuestion
17897 //===========================================================================================================================
17900 _DataBuffer_AppendDNSQuestion(
17902 const uint8_t * inNamePtr
,
17908 DNSQuestionFixedFields fields
;
17910 err
= DataBuffer_Append( inDB
, inNamePtr
, inNameLen
);
17911 require_noerr( err
, exit
);
17913 DNSQuestionFixedFieldsInit( &fields
, inType
, inClass
);
17914 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
17915 require_noerr( err
, exit
);
17921 //===========================================================================================================================
17922 // _DataBuffer_AppendDNSRecord
17923 //===========================================================================================================================
17926 _DataBuffer_AppendDNSRecord(
17928 const uint8_t * inNamePtr
,
17933 const uint8_t * inRDataPtr
,
17934 size_t inRDataLen
)
17937 DNSRecordFixedFields fields
;
17939 require_action_quiet( inRDataLen
< kDNSRecordDataLengthMax
, exit
, err
= kSizeErr
);
17941 err
= DataBuffer_Append( inDB
, inNamePtr
, inNameLen
);
17942 require_noerr( err
, exit
);
17944 DNSRecordFixedFieldsSet( &fields
, inType
, inClass
, inTTL
, (uint16_t) inRDataLen
);
17945 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
17946 require_noerr( err
, exit
);
17950 err
= DataBuffer_Append( inDB
, inRDataPtr
, inRDataLen
);
17951 require_noerr( err
, exit
);
17958 //===========================================================================================================================
17959 // _NanoTime64ToDateString
17960 //===========================================================================================================================
17962 static char * _NanoTime64ToDateString( NanoTime64 inTime
, char *inBuf
, size_t inMaxLen
)
17966 NanoTimeToTimeVal( inTime
, &tv
);
17967 return( MakeFractionalDateString( &tv
, inBuf
, inMaxLen
) );
17970 //===========================================================================================================================
17971 // MDNSColliderCreate
17972 //===========================================================================================================================
17976 kMDNSColliderOpCode_Invalid
= 0,
17977 kMDNSColliderOpCode_Send
= 1,
17978 kMDNSColliderOpCode_Wait
= 2,
17979 kMDNSColliderOpCode_SetProbeActions
= 3,
17980 kMDNSColliderOpCode_LoopPush
= 4,
17981 kMDNSColliderOpCode_LoopPop
= 5,
17982 kMDNSColliderOpCode_Exit
= 6
17984 } MDNSColliderOpCode
;
17988 MDNSColliderOpCode opcode
;
17991 } MDNSCInstruction
;
17993 #define kMaxLoopDepth 16
17995 struct MDNSColliderPrivate
17997 CFRuntimeBase base
; // CF object base.
17998 dispatch_queue_t queue
; // Queue for collider's events.
17999 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
18000 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
18001 SocketRef sockV4
; // IPv4 UDP socket for mDNS.
18002 SocketRef sockV6
; // IPv6 UDP socket for mDNS.
18003 uint8_t * target
; // Record name being targeted. (malloced)
18004 uint8_t * responsePtr
; // Response message pointer. (malloced)
18005 size_t responseLen
; // Response message length.
18006 uint8_t * probePtr
; // Probe query message pointer. (malloced)
18007 size_t probeLen
; // Probe query message length.
18008 unsigned int probeCount
; // Count of probe queries received for collider's record.
18009 uint32_t probeActionMap
; // Bitmap of actions to take for
18010 MDNSCInstruction
* program
; // Program to execute.
18011 uint32_t pc
; // Program's program counter.
18012 uint32_t loopCounts
[ kMaxLoopDepth
]; // Stack of loop counters.
18013 uint32_t loopDepth
; // Current loop depth.
18014 dispatch_source_t waitTimer
; // Timer for program's wait commands.
18015 uint32_t interfaceIndex
; // Interface over which to send and receive mDNS msgs.
18016 MDNSColliderStopHandler_f stopHandler
; // User's stop handler.
18017 void * stopContext
; // User's stop handler context.
18018 MDNSColliderProtocols protocols
; // Protocols to use, i.e., IPv4, IPv6.
18019 Boolean stopped
; // True if the collider has been stopped.
18020 uint8_t msgBuf
[ kMDNSMessageSizeMax
]; // mDNS message buffer.
18023 static void _MDNSColliderStop( MDNSColliderRef inCollider
, OSStatus inError
);
18024 static void _MDNSColliderReadHandler( void *inContext
);
18025 static void _MDNSColliderExecuteProgram( void *inContext
);
18026 static OSStatus
_MDNSColliderSendResponse( MDNSColliderRef inCollider
, SocketRef inSock
, const struct sockaddr
*inDest
);
18027 static OSStatus
_MDNSColliderSendProbe( MDNSColliderRef inCollider
, SocketRef inSock
, const struct sockaddr
*inDest
);
18029 CF_CLASS_DEFINE( MDNSCollider
);
18031 ulog_define_ex( "com.apple.dnssdutil", MDNSCollider
, kLogLevelInfo
, kLogFlags_None
, "MDNSCollider", NULL
);
18032 #define mc_ulog( LEVEL, ... ) ulog( &log_category_from_name( MDNSCollider ), (LEVEL), __VA_ARGS__ )
18034 static OSStatus
MDNSColliderCreate( dispatch_queue_t inQueue
, MDNSColliderRef
*outCollider
)
18037 MDNSColliderRef obj
= NULL
;
18039 CF_OBJECT_CREATE( MDNSCollider
, obj
, err
, exit
);
18041 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
18042 obj
->sockV4
= kInvalidSocketRef
;
18043 obj
->sockV6
= kInvalidSocketRef
;
18045 *outCollider
= obj
;
18052 //===========================================================================================================================
18053 // _MDNSColliderFinalize
18054 //===========================================================================================================================
18056 static void _MDNSColliderFinalize( CFTypeRef inObj
)
18058 MDNSColliderRef
const me
= (MDNSColliderRef
) inObj
;
18060 check( !me
->waitTimer
);
18061 check( !me
->readSourceV4
);
18062 check( !me
->readSourceV6
);
18063 check( !IsValidSocket( me
->sockV4
) );
18064 check( !IsValidSocket( me
->sockV6
) );
18065 ForgetMem( &me
->target
);
18066 ForgetMem( &me
->responsePtr
);
18067 ForgetMem( &me
->probePtr
);
18068 ForgetMem( &me
->program
);
18069 dispatch_forget( &me
->queue
);
18072 //===========================================================================================================================
18073 // MDNSColliderStart
18074 //===========================================================================================================================
18076 static void _MDNSColliderStart( void *inContext
);
18078 static OSStatus
MDNSColliderStart( MDNSColliderRef me
)
18082 require_action_quiet( me
->target
, exit
, err
= kNotPreparedErr
);
18083 require_action_quiet( me
->responsePtr
, exit
, err
= kNotPreparedErr
);
18084 require_action_quiet( me
->probePtr
, exit
, err
= kNotPreparedErr
);
18085 require_action_quiet( me
->program
, exit
, err
= kNotPreparedErr
);
18086 require_action_quiet( me
->interfaceIndex
, exit
, err
= kNotPreparedErr
);
18087 require_action_quiet( me
->protocols
, exit
, err
= kNotPreparedErr
);
18090 dispatch_async_f( me
->queue
, me
, _MDNSColliderStart
);
18097 static void _MDNSColliderStart( void *inContext
)
18100 MDNSColliderRef
const me
= (MDNSColliderRef
) inContext
;
18101 SocketRef sock
= kInvalidSocketRef
;
18102 SocketContext
* sockCtx
= NULL
;
18104 if( me
->protocols
& kMDNSColliderProtocol_IPv4
)
18106 err
= CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort
, NULL
, me
->interfaceIndex
, true, NULL
, &sock
);
18107 require_noerr( err
, exit
);
18109 err
= SocketContextCreate( sock
, me
, &sockCtx
);
18110 require_noerr( err
, exit
);
18111 sock
= kInvalidSocketRef
;
18113 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _MDNSColliderReadHandler
, SocketContextCancelHandler
,
18114 sockCtx
, &me
->readSourceV4
);
18115 require_noerr( err
, exit
);
18116 me
->sockV4
= sockCtx
->sock
;
18119 dispatch_resume( me
->readSourceV4
);
18122 if( me
->protocols
& kMDNSColliderProtocol_IPv6
)
18124 err
= CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort
, NULL
, me
->interfaceIndex
, true, NULL
, &sock
);
18125 require_noerr( err
, exit
);
18127 err
= SocketContextCreate( sock
, me
, &sockCtx
);
18128 require_noerr( err
, exit
);
18129 sock
= kInvalidSocketRef
;
18131 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _MDNSColliderReadHandler
, SocketContextCancelHandler
,
18132 sockCtx
, &me
->readSourceV6
);
18133 require_noerr( err
, exit
);
18134 me
->sockV6
= sockCtx
->sock
;
18137 dispatch_resume( me
->readSourceV6
);
18140 _MDNSColliderExecuteProgram( me
);
18144 ForgetSocket( &sock
);
18145 ForgetSocketContext( &sockCtx
);
18146 if( err
) _MDNSColliderStop( me
, err
);
18149 //===========================================================================================================================
18150 // MDNSColliderStop
18151 //===========================================================================================================================
18153 static void _MDNSColliderUserStop( void *inContext
);
18155 static void MDNSColliderStop( MDNSColliderRef me
)
18158 dispatch_async_f( me
->queue
, me
, _MDNSColliderUserStop
);
18161 static void _MDNSColliderUserStop( void *inContext
)
18163 MDNSColliderRef
const me
= (MDNSColliderRef
) inContext
;
18165 _MDNSColliderStop( me
, kCanceledErr
);
18169 //===========================================================================================================================
18170 // MDNSColliderSetProtocols
18171 //===========================================================================================================================
18173 static void MDNSColliderSetProtocols( MDNSColliderRef me
, MDNSColliderProtocols inProtocols
)
18175 me
->protocols
= inProtocols
;
18178 //===========================================================================================================================
18179 // MDNSColliderSetInterfaceIndex
18180 //===========================================================================================================================
18182 static void MDNSColliderSetInterfaceIndex( MDNSColliderRef me
, uint32_t inInterfaceIndex
)
18184 me
->interfaceIndex
= inInterfaceIndex
;
18187 //===========================================================================================================================
18188 // MDNSColliderSetProgram
18189 //===========================================================================================================================
18191 #define kMDNSColliderProgCmd_Done "done"
18192 #define kMDNSColliderProgCmd_Loop "loop"
18193 #define kMDNSColliderProgCmd_Send "send"
18194 #define kMDNSColliderProgCmd_Probes "probes"
18195 #define kMDNSColliderProgCmd_Wait "wait"
18197 typedef uint32_t MDNSColliderProbeAction
;
18199 #define kMDNSColliderProbeAction_None 0
18200 #define kMDNSColliderProbeAction_Respond 1
18201 #define kMDNSColliderProbeAction_RespondUnicast 2
18202 #define kMDNSColliderProbeAction_RespondMulticast 3
18203 #define kMDNSColliderProbeAction_Probe 4
18204 #define kMDNSColliderProbeAction_MaxValue kMDNSColliderProbeAction_Probe
18206 #define kMDNSColliderProbeActionBits_Count 3
18207 #define kMDNSColliderProbeActionBits_Mask ( ( 1U << kMDNSColliderProbeActionBits_Count ) - 1 )
18208 #define kMDNSColliderProbeActionMaxProbeCount ( 32 / kMDNSColliderProbeActionBits_Count )
18210 check_compile_time( kMDNSColliderProbeAction_MaxValue
<= kMDNSColliderProbeActionBits_Mask
);
18212 static OSStatus
_MDNSColliderParseProbeActionString( const char *inString
, size_t inLen
, uint32_t *outBitmap
);
18214 static OSStatus
MDNSColliderSetProgram( MDNSColliderRef me
, const char *inProgramStr
)
18218 unsigned int loopDepth
;
18222 MDNSCInstruction
* program
= NULL
;
18223 uint32_t loopStart
[ kMaxLoopDepth
];
18226 for( cmd
= inProgramStr
; *cmd
; cmd
= next
)
18228 for( end
= cmd
; *end
&& ( *end
!= ';' ); ++end
) {}
18229 require_action_quiet( end
!= cmd
, exit
, err
= kMalformedErr
);
18230 next
= ( *end
== ';' ) ? ( end
+ 1 ) : end
;
18234 program
= (MDNSCInstruction
*) calloc( insCount
+ 1, sizeof( *program
) );
18235 require_action( program
, exit
, err
= kNoMemoryErr
);
18239 for( cmd
= inProgramStr
; *cmd
; cmd
= next
)
18246 MDNSCInstruction
* const ins
= &program
[ insCount
];
18248 while( isspace_safe( *cmd
) ) ++cmd
;
18249 for( end
= cmd
; *end
&& ( *end
!= ';' ); ++end
) {}
18250 next
= ( *end
== ';' ) ? ( end
+ 1 ) : end
;
18252 for( ptr
= cmd
; ( ptr
< end
) && !isspace_safe( *ptr
); ++ptr
) {}
18253 cmdLen
= (size_t)( ptr
- cmd
);
18257 if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Done
) == 0 )
18259 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
18260 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
18262 require_action_quiet( loopDepth
> 0, exit
, err
= kMalformedErr
);
18264 ins
->opcode
= kMDNSColliderOpCode_LoopPop
;
18265 ins
->operand
= loopStart
[ --loopDepth
];
18270 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Loop
) == 0 )
18272 for( arg
= ptr
; ( arg
< end
) && isspace_safe( *arg
); ++arg
) {}
18273 err
= DecimalTextToUInt32( arg
, end
, &value
, &ptr
);
18274 require_noerr_quiet( err
, exit
);
18275 require_action_quiet( value
> 0, exit
, err
= kValueErr
);
18277 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
18278 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
18280 ins
->opcode
= kMDNSColliderOpCode_LoopPush
;
18281 ins
->operand
= value
;
18283 require_action_quiet( loopDepth
< kMaxLoopDepth
, exit
, err
= kNoSpaceErr
);
18284 loopStart
[ loopDepth
++ ] = insCount
+ 1;
18289 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Probes
) == 0 )
18291 for( arg
= ptr
; ( arg
< end
) && isspace_safe( *arg
); ++arg
) {}
18292 for( ptr
= arg
; ( ptr
< end
) && !isspace_safe( *ptr
); ++ptr
) {}
18293 argLen
= (size_t)( ptr
- arg
);
18296 err
= _MDNSColliderParseProbeActionString( arg
, argLen
, &value
);
18297 require_noerr_quiet( err
, exit
);
18304 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
18305 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
18307 ins
->opcode
= kMDNSColliderOpCode_SetProbeActions
;
18308 ins
->operand
= value
;
18313 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Send
) == 0 )
18315 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
18316 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
18318 ins
->opcode
= kMDNSColliderOpCode_Send
;
18323 else if( strnicmpx( cmd
, cmdLen
, kMDNSColliderProgCmd_Wait
) == 0 )
18325 for( arg
= ptr
; ( arg
< end
) && isspace_safe( *arg
); ++arg
) {}
18326 err
= DecimalTextToUInt32( arg
, end
, &value
, &ptr
);
18327 require_noerr_quiet( err
, exit
);
18329 while( ( ptr
< end
) && isspace_safe( *ptr
) ) ++ptr
;
18330 require_action_quiet( ptr
== end
, exit
, err
= kMalformedErr
);
18332 ins
->opcode
= kMDNSColliderOpCode_Wait
;
18333 ins
->operand
= value
;
18336 // Unrecognized command
18345 require_action_quiet( loopDepth
== 0, exit
, err
= kMalformedErr
);
18347 program
[ insCount
].opcode
= kMDNSColliderOpCode_Exit
;
18349 FreeNullSafe( me
->program
);
18350 me
->program
= program
;
18355 FreeNullSafe( program
);
18359 static OSStatus
_MDNSColliderParseProbeActionString( const char *inString
, size_t inLen
, uint32_t *outBitmap
)
18363 const char * const end
= &inString
[ inLen
];
18373 MDNSColliderProbeAction action
;
18376 if( isdigit_safe( c
) )
18381 count
= ( count
* 10 ) + ( c
- '0' );
18382 require_action_quiet( count
<= ( kMDNSColliderProbeActionMaxProbeCount
- index
), exit
, err
= kCountErr
);
18383 require_action_quiet( ptr
< end
, exit
, err
= kUnderrunErr
);
18386 } while( isdigit_safe( c
) );
18387 require_action_quiet( count
> 0, exit
, err
= kCountErr
);
18391 require_action_quiet( index
< kMDNSColliderProbeActionMaxProbeCount
, exit
, err
= kMalformedErr
);
18397 case 'n': action
= kMDNSColliderProbeAction_None
; break;
18398 case 'r': action
= kMDNSColliderProbeAction_Respond
; break;
18399 case 'u': action
= kMDNSColliderProbeAction_RespondUnicast
; break;
18400 case 'm': action
= kMDNSColliderProbeAction_RespondMulticast
; break;
18401 case 'p': action
= kMDNSColliderProbeAction_Probe
; break;
18402 default: err
= kMalformedErr
; goto exit
;
18407 require_action_quiet( ( c
== '-' ) && ( ptr
< end
), exit
, err
= kMalformedErr
);
18409 while( count
-- > 0 )
18411 bitmap
|= ( action
<< ( index
* kMDNSColliderProbeActionBits_Count
) );
18416 *outBitmap
= bitmap
;
18423 //===========================================================================================================================
18424 // MDNSColliderSetStopHandler
18425 //===========================================================================================================================
18427 static void MDNSColliderSetStopHandler( MDNSColliderRef me
, MDNSColliderStopHandler_f inStopHandler
, void *inStopContext
)
18429 me
->stopHandler
= inStopHandler
;
18430 me
->stopContext
= inStopContext
;
18433 //===========================================================================================================================
18434 // MDNSColliderSetRecord
18435 //===========================================================================================================================
18437 #define kMDNSColliderDummyStr "\x16" "mdnscollider-sent-this" kLocalStr
18438 #define kMDNSColliderDummyName ( (const uint8_t *) kMDNSColliderDummyStr )
18439 #define kMDNSColliderDummyNameLen sizeof( kMDNSColliderDummyStr )
18442 MDNSColliderSetRecord(
18443 MDNSColliderRef me
,
18444 const uint8_t * inName
,
18446 const void * inRDataPtr
,
18447 size_t inRDataLen
)
18452 uint8_t * targetPtr
= NULL
;
18454 uint8_t * responsePtr
= NULL
;
18455 size_t responseLen
;
18456 uint8_t * probePtr
= NULL
;
18459 DataBuffer_Init( &msgDB
, NULL
, 0, kMDNSMessageSizeMax
);
18461 err
= DomainNameDup( inName
, &targetPtr
, &targetLen
);
18462 require_noerr_quiet( err
, exit
);
18464 // Create response message.
18466 memset( &header
, 0, sizeof( header
) );
18467 DNSHeaderSetFlags( &header
, kDNSHeaderFlag_Response
| kDNSHeaderFlag_AuthAnswer
);
18468 DNSHeaderSetAnswerCount( &header
, 1 );
18470 err
= DataBuffer_Append( &msgDB
, &header
, sizeof( header
) );
18471 require_noerr( err
, exit
);
18473 err
= _DataBuffer_AppendDNSRecord( &msgDB
, targetPtr
, targetLen
, inType
, kDNSServiceClass_IN
| kRRClassCacheFlushBit
,
18474 1976, inRDataPtr
, inRDataLen
);
18475 require_noerr( err
, exit
);
18477 err
= DataBuffer_Detach( &msgDB
, &responsePtr
, &responseLen
);
18478 require_noerr( err
, exit
);
18480 // Create probe message.
18482 memset( &header
, 0, sizeof( header
) );
18483 DNSHeaderSetQuestionCount( &header
, 2 );
18484 DNSHeaderSetAuthorityCount( &header
, 1 );
18486 err
= DataBuffer_Append( &msgDB
, &header
, sizeof( header
) );
18487 require_noerr( err
, exit
);
18489 err
= _DataBuffer_AppendDNSQuestion( &msgDB
, targetPtr
, targetLen
, kDNSServiceType_ANY
, kDNSServiceClass_IN
);
18490 require_noerr( err
, exit
);
18492 err
= _DataBuffer_AppendDNSQuestion( &msgDB
, kMDNSColliderDummyName
, kMDNSColliderDummyNameLen
,
18493 kDNSServiceType_NULL
, kDNSServiceClass_IN
);
18494 require_noerr( err
, exit
);
18496 err
= _DataBuffer_AppendDNSRecord( &msgDB
, targetPtr
, targetLen
, inType
, kDNSServiceClass_IN
,
18497 1976, inRDataPtr
, inRDataLen
);
18498 require_noerr( err
, exit
);
18500 err
= DataBuffer_Detach( &msgDB
, &probePtr
, &probeLen
);
18501 require_noerr( err
, exit
);
18503 FreeNullSafe( me
->target
);
18504 me
->target
= targetPtr
;
18507 FreeNullSafe( me
->responsePtr
);
18508 me
->responsePtr
= responsePtr
;
18509 me
->responseLen
= responseLen
;
18510 responsePtr
= NULL
;
18512 FreeNullSafe( me
->probePtr
);
18513 me
->probePtr
= probePtr
;
18514 me
->probeLen
= probeLen
;
18518 DataBuffer_Free( &msgDB
);
18519 FreeNullSafe( targetPtr
);
18520 FreeNullSafe( responsePtr
);
18521 FreeNullSafe( probePtr
);
18525 //===========================================================================================================================
18526 // _MDNSColliderStop
18527 //===========================================================================================================================
18529 static void _MDNSColliderStop( MDNSColliderRef me
, OSStatus inError
)
18531 dispatch_source_forget( &me
->waitTimer
);
18532 dispatch_source_forget( &me
->readSourceV4
);
18533 dispatch_source_forget( &me
->readSourceV6
);
18534 me
->sockV4
= kInvalidSocketRef
;
18535 me
->sockV6
= kInvalidSocketRef
;
18539 me
->stopped
= true;
18540 if( me
->stopHandler
) me
->stopHandler( me
->stopContext
, inError
);
18545 //===========================================================================================================================
18546 // _MDNSColliderReadHandler
18547 //===========================================================================================================================
18549 static MDNSColliderProbeAction
_MDNSColliderGetProbeAction( uint32_t inBitmap
, unsigned int inProbeNumber
);
18550 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction
);
18552 static void _MDNSColliderReadHandler( void *inContext
)
18555 struct timeval now
;
18556 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
18557 MDNSColliderRef
const me
= (MDNSColliderRef
) sockCtx
->userContext
;
18559 sockaddr_ip sender
;
18560 const DNSHeader
* hdr
;
18561 const uint8_t * ptr
;
18562 const struct sockaddr
* dest
;
18563 int probeFound
, probeIsQU
;
18564 unsigned int qCount
, i
;
18565 MDNSColliderProbeAction action
;
18567 gettimeofday( &now
, NULL
);
18569 err
= SocketRecvFrom( sockCtx
->sock
, me
->msgBuf
, sizeof( me
->msgBuf
), &msgLen
, &sender
, sizeof( sender
),
18570 NULL
, NULL
, NULL
, NULL
);
18571 require_noerr( err
, exit
);
18573 require_quiet( msgLen
>= kDNSHeaderLength
, exit
);
18574 hdr
= (const DNSHeader
*) me
->msgBuf
;
18576 probeFound
= false;
18578 qCount
= DNSHeaderGetQuestionCount( hdr
);
18579 ptr
= (const uint8_t *) &hdr
[ 1 ];
18580 for( i
= 0; i
< qCount
; ++i
)
18582 uint16_t qtype
, qclass
;
18583 uint8_t qname
[ kDomainNameLengthMax
];
18585 err
= DNSMessageExtractQuestion( me
->msgBuf
, msgLen
, ptr
, qname
, &qtype
, &qclass
, &ptr
);
18586 require_noerr_quiet( err
, exit
);
18588 if( ( qtype
== kDNSServiceType_NULL
) && ( qclass
== kDNSServiceClass_IN
) &&
18589 DomainNameEqual( qname
, kMDNSColliderDummyName
) )
18591 probeFound
= false;
18595 if( qtype
!= kDNSServiceType_ANY
) continue;
18596 if( ( qclass
& ~kQClassUnicastResponseBit
) != kDNSServiceClass_IN
) continue;
18597 if( !DomainNameEqual( qname
, me
->target
) ) continue;
18602 probeIsQU
= ( qclass
& kQClassUnicastResponseBit
) ? true : false;
18605 require_quiet( probeFound
, exit
);
18608 action
= _MDNSColliderGetProbeAction( me
->probeActionMap
, me
->probeCount
);
18610 mc_ulog( kLogLevelInfo
, "Received probe from %##a at %{du:time} (action: %s):\n\n%#1{du:dnsmsg}",
18611 &sender
, &now
, _MDNSColliderProbeActionToString( action
), me
->msgBuf
, msgLen
);
18613 if( ( action
== kMDNSColliderProbeAction_Respond
) ||
18614 ( action
== kMDNSColliderProbeAction_RespondUnicast
) ||
18615 ( action
== kMDNSColliderProbeAction_RespondMulticast
) )
18617 if( ( ( action
== kMDNSColliderProbeAction_Respond
) && probeIsQU
) ||
18618 ( action
== kMDNSColliderProbeAction_RespondUnicast
) )
18622 else if( ( ( action
== kMDNSColliderProbeAction_Respond
) && !probeIsQU
) ||
18623 ( action
== kMDNSColliderProbeAction_RespondMulticast
) )
18625 dest
= ( sender
.sa
.sa_family
== AF_INET
) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
18628 err
= _MDNSColliderSendResponse( me
, sockCtx
->sock
, dest
);
18629 require_noerr( err
, exit
);
18631 else if( action
== kMDNSColliderProbeAction_Probe
)
18633 dest
= ( sender
.sa
.sa_family
== AF_INET
) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
18635 err
= _MDNSColliderSendProbe( me
, sockCtx
->sock
, dest
);
18636 require_noerr( err
, exit
);
18643 static MDNSColliderProbeAction
_MDNSColliderGetProbeAction( uint32_t inBitmap
, unsigned int inProbeNumber
)
18645 MDNSColliderProbeAction action
;
18647 if( ( inProbeNumber
>= 1 ) && ( inProbeNumber
<= kMDNSColliderProbeActionMaxProbeCount
) )
18649 action
= ( inBitmap
>> ( ( inProbeNumber
- 1 ) * kMDNSColliderProbeActionBits_Count
) ) &
18650 kMDNSColliderProbeActionBits_Mask
;
18654 action
= kMDNSColliderProbeAction_None
;
18659 static const char * _MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction
)
18663 case kMDNSColliderProbeAction_None
: return( "None" );
18664 case kMDNSColliderProbeAction_Respond
: return( "Respond" );
18665 case kMDNSColliderProbeAction_RespondUnicast
: return( "Respond (unicast)" );
18666 case kMDNSColliderProbeAction_RespondMulticast
: return( "Respond (multicast)" );
18667 case kMDNSColliderProbeAction_Probe
: return( "Probe" );
18668 default: return( "???" );
18672 //===========================================================================================================================
18673 // _MDNSColliderExecuteProgram
18674 //===========================================================================================================================
18676 static void _MDNSColliderExecuteProgram( void *inContext
)
18679 MDNSColliderRef
const me
= (MDNSColliderRef
) inContext
;
18682 dispatch_forget( &me
->waitTimer
);
18687 const MDNSCInstruction
* const ins
= &me
->program
[ me
->pc
++ ];
18690 switch( ins
->opcode
)
18692 case kMDNSColliderOpCode_Send
:
18693 if( IsValidSocket( me
->sockV4
) )
18695 err
= _MDNSColliderSendResponse( me
, me
->sockV4
, GetMDNSMulticastAddrV4() );
18696 require_noerr( err
, exit
);
18698 if( IsValidSocket( me
->sockV6
) )
18700 err
= _MDNSColliderSendResponse( me
, me
->sockV6
, GetMDNSMulticastAddrV6() );
18701 require_noerr( err
, exit
);
18705 case kMDNSColliderOpCode_Wait
:
18706 waitMs
= ins
->operand
;
18709 err
= DispatchTimerOneShotCreate( dispatch_time_milliseconds( waitMs
), 1, me
->queue
,
18710 _MDNSColliderExecuteProgram
, me
, &me
->waitTimer
);
18711 require_noerr( err
, exit
);
18712 dispatch_resume( me
->waitTimer
);
18717 case kMDNSColliderOpCode_SetProbeActions
:
18718 me
->probeCount
= 0;
18719 me
->probeActionMap
= ins
->operand
;
18722 case kMDNSColliderOpCode_LoopPush
:
18723 check( me
->loopDepth
< kMaxLoopDepth
);
18724 me
->loopCounts
[ me
->loopDepth
++ ] = ins
->operand
;
18727 case kMDNSColliderOpCode_LoopPop
:
18728 check( me
->loopDepth
> 0 );
18729 if( --me
->loopCounts
[ me
->loopDepth
- 1 ] > 0 )
18731 me
->pc
= ins
->operand
;
18739 case kMDNSColliderOpCode_Exit
:
18745 dlogassert( "Unhandled opcode %u\n", ins
->opcode
);
18752 if( err
|| stop
) _MDNSColliderStop( me
, err
);
18755 //===========================================================================================================================
18756 // _MDNSColliderSendResponse
18757 //===========================================================================================================================
18759 static OSStatus
_MDNSColliderSendResponse( MDNSColliderRef me
, SocketRef inSock
, const struct sockaddr
*inDest
)
18764 n
= sendto( inSock
, (char *) me
->responsePtr
, me
->responseLen
, 0, inDest
, SockAddrGetSize( inDest
) );
18765 err
= map_socket_value_errno( inSock
, n
== (ssize_t
) me
->responseLen
, n
);
18769 //===========================================================================================================================
18770 // _MDNSColliderSendProbe
18771 //===========================================================================================================================
18773 static OSStatus
_MDNSColliderSendProbe( MDNSColliderRef me
, SocketRef inSock
, const struct sockaddr
*inDest
)
18778 n
= sendto( inSock
, (char *) me
->probePtr
, me
->probeLen
, 0, inDest
, SockAddrGetSize( inDest
) );
18779 err
= map_socket_value_errno( inSock
, n
== (ssize_t
) me
->probeLen
, n
);
18783 //===========================================================================================================================
18784 // ServiceBrowserCreate
18785 //===========================================================================================================================
18787 typedef struct SBDomain SBDomain
;
18788 typedef struct SBServiceType SBServiceType
;
18789 typedef struct SBServiceBrowse SBServiceBrowse
;
18790 typedef struct SBServiceInstance SBServiceInstance
;
18791 typedef struct SBIPAddress SBIPAddress
;
18793 struct ServiceBrowserPrivate
18795 CFRuntimeBase base
; // CF object base.
18796 dispatch_queue_t queue
; // Queue for service browser's events.
18797 DNSServiceRef connection
; // Shared connection for DNS-SD ops.
18798 DNSServiceRef domainsQuery
; // Query for recommended browsing domains.
18799 char * domain
; // If non-null, then browsing is limited to this domain.
18800 StringListItem
* serviceTypeList
; // If non-null, then browsing is limited to these service types.
18801 ServiceBrowserCallback_f userCallback
; // User's callback. Called when browsing stops.
18802 void * userContext
; // User's callback context.
18803 SBDomain
* domainList
; // List of domains and their browse results.
18804 dispatch_source_t stopTimer
; // Timer to stop browsing after browseTimeSecs.
18805 uint32_t ifIndex
; // If non-zero, then browsing is limited to this interface.
18806 unsigned int browseTimeSecs
; // Amount of time to spend browsing in seconds.
18807 Boolean includeAWDL
; // True if the IncludeAWDL flag should be used for DNS-SD ops that
18808 // use the "any" interface.
18813 SBDomain
* next
; // Next domain object in list.
18814 ServiceBrowserRef browser
; // Pointer to parent service browser.
18815 char * name
; // Name of the domain.
18816 DNSServiceRef servicesQuery
; // Query for services (_services._dns-sd._udp.<domain> PTR record) in domain.
18817 SBServiceType
* typeList
; // List of service types to browse for in this domain.
18820 struct SBServiceType
18822 SBServiceType
* next
; // Next service type object in list.
18823 char * name
; // Name of the service type.
18824 SBServiceBrowse
* browseList
; // List of browses for this service type.
18827 struct SBServiceBrowse
18829 SBServiceBrowse
* next
; // Next browse object in list.
18830 ServiceBrowserRef browser
; // Pointer to parent service browser.
18831 DNSServiceRef browse
; // Reference to DNSServiceBrowse op.
18832 SBServiceInstance
* instanceList
; // List of service instances that were discovered by this browse.
18833 uint64_t startTicks
; // Value of UpTicks() when the browse op began.
18834 uint32_t ifIndex
; // If non-zero, then the browse is limited to this interface.
18837 struct SBServiceInstance
18839 SBServiceInstance
* next
; // Next service instance object in list.
18840 ServiceBrowserRef browser
; // Pointer to parent service browser.
18841 char * name
; // Name of the service instance.
18842 uint32_t ifIndex
; // Index of interface over which this service instance was discovered.
18843 uint64_t discoverTimeUs
; // Time it took to discover this service instance in microseconds.
18844 DNSServiceRef resolve
; // Reference to DNSServiceResolve op for this service instance.
18845 uint64_t resolveStartTicks
; // Value of UpTicks() when the DNSServiceResolve op began.
18846 uint64_t resolveTimeUs
; // Time it took to resolve this service instance.
18847 char * hostname
; // Service instance's hostname. Result of DNSServiceResolve.
18848 uint16_t port
; // Service instance's port number. Result of DNSServiceResolve.
18849 uint8_t * txtPtr
; // Service instance's TXT record data. Result of DNSServiceResolve.
18850 size_t txtLen
; // Length of service instance's TXT record data.
18851 DNSServiceRef getAddrInfo
; // Reference to DNSServiceGetAddrInfo op for service instance's hostname.
18852 uint64_t gaiStartTicks
; // Value of UpTicks() when the DNSServiceGetAddrInfo op began.
18853 SBIPAddress
* ipaddrList
; // List of IP addresses that the hostname resolved to.
18858 SBIPAddress
* next
; // Next IP address object in list.
18859 sockaddr_ip sip
; // IPv4 or IPv6 address.
18860 uint64_t resolveTimeUs
; // Time it took to resolve this IP address in microseconds.
18865 SBRDomain
* domainList
; // List of domains in which services were found.
18866 int32_t refCount
; // This object's reference count.
18868 } ServiceBrowserResultsPrivate
;
18870 static void _ServiceBrowserStop( ServiceBrowserRef me
, OSStatus inError
);
18871 static OSStatus
_ServiceBrowserAddDomain( ServiceBrowserRef inBrowser
, const char *inDomain
);
18872 static OSStatus
_ServiceBrowserRemoveDomain( ServiceBrowserRef inBrowser
, const char *inName
);
18873 static void _ServiceBrowserTimerHandler( void *inContext
);
18874 static void DNSSD_API
18875 _ServiceBrowserDomainsQueryCallback(
18876 DNSServiceRef inSDRef
,
18877 DNSServiceFlags inFlags
,
18878 uint32_t inInterfaceIndex
,
18879 DNSServiceErrorType inError
,
18880 const char * inFullName
,
18883 uint16_t inRDataLen
,
18884 const void * inRDataPtr
,
18886 void * inContext
);
18887 static void DNSSD_API
18888 _ServiceBrowserServicesQueryCallback(
18889 DNSServiceRef inSDRef
,
18890 DNSServiceFlags inFlags
,
18891 uint32_t inInterfaceIndex
,
18892 DNSServiceErrorType inError
,
18893 const char * inFullName
,
18896 uint16_t inRDataLen
,
18897 const void * inRDataPtr
,
18899 void * inContext
);
18900 static void DNSSD_API
18901 _ServiceBrowserBrowseCallback(
18902 DNSServiceRef inSDRef
,
18903 DNSServiceFlags inFlags
,
18904 uint32_t inInterfaceIndex
,
18905 DNSServiceErrorType inError
,
18906 const char * inName
,
18907 const char * inRegType
,
18908 const char * inDomain
,
18909 void * inContext
);
18910 static void DNSSD_API
18911 _ServiceBrowserResolveCallback(
18912 DNSServiceRef inSDRef
,
18913 DNSServiceFlags inFlags
,
18914 uint32_t inInterfaceIndex
,
18915 DNSServiceErrorType inError
,
18916 const char * inFullName
,
18917 const char * inHostname
,
18920 const unsigned char * inTXTPtr
,
18921 void * inContext
);
18922 static void DNSSD_API
18923 _ServiceBrowserGAICallback(
18924 DNSServiceRef inSDRef
,
18925 DNSServiceFlags inFlags
,
18926 uint32_t inInterfaceIndex
,
18927 DNSServiceErrorType inError
,
18928 const char * inHostname
,
18929 const struct sockaddr
* inSockAddr
,
18931 void * inContext
);
18933 _ServiceBrowserAddServiceType(
18934 ServiceBrowserRef inBrowser
,
18935 SBDomain
* inDomain
,
18936 const char * inName
,
18937 uint32_t inIfIndex
);
18939 _ServiceBrowserRemoveServiceType(
18940 ServiceBrowserRef inBrowser
,
18941 SBDomain
* inDomain
,
18942 const char * inName
,
18943 uint32_t inIfIndex
);
18945 _ServiceBrowserAddServiceInstance(
18946 ServiceBrowserRef inBrowser
,
18947 SBServiceBrowse
* inBrowse
,
18948 uint32_t inIfIndex
,
18949 const char * inName
,
18950 const char * inRegType
,
18951 const char * inDomain
,
18952 uint64_t inDiscoverTimeUs
);
18954 _ServiceBrowserRemoveServiceInstance(
18955 ServiceBrowserRef inBrowser
,
18956 SBServiceBrowse
* inBrowse
,
18957 const char * inName
,
18958 uint32_t inIfIndex
);
18960 _ServiceBrowserAddIPAddress(
18961 ServiceBrowserRef inBrowser
,
18962 SBServiceInstance
* inInstance
,
18963 const struct sockaddr
* inSockAddr
,
18964 uint64_t inResolveTimeUs
);
18966 _ServiceBrowserRemoveIPAddress(
18967 ServiceBrowserRef inBrowser
,
18968 SBServiceInstance
* inInstance
,
18969 const struct sockaddr
* inSockAddr
);
18970 static OSStatus
_ServiceBrowserCreateResults( ServiceBrowserRef me
, ServiceBrowserResults
**outResults
);
18971 static OSStatus
_SBDomainCreate( const char *inName
, ServiceBrowserRef inBrowser
, SBDomain
**outDomain
);
18972 static void _SBDomainFree( SBDomain
*inDomain
);
18973 static OSStatus
_SBServiceTypeCreate( const char *inName
, SBServiceType
**outType
);
18974 static void _SBServiceTypeFree( SBServiceType
*inType
);
18975 static OSStatus
_SBServiceBrowseCreate( uint32_t inIfIndex
, ServiceBrowserRef inBrowser
, SBServiceBrowse
**outBrowse
);
18976 static void _SBServiceBrowseFree( SBServiceBrowse
*inBrowse
);
18978 _SBServiceInstanceCreate(
18979 const char * inName
,
18980 uint32_t inIfIndex
,
18981 uint64_t inDiscoverTimeUs
,
18982 ServiceBrowserRef inBrowser
,
18983 SBServiceInstance
** outInstance
);
18984 static void _SBServiceInstanceFree( SBServiceInstance
*inInstance
);
18986 _SBIPAddressCreate(
18987 const struct sockaddr
* inSockAddr
,
18988 uint64_t inResolveTimeUs
,
18989 SBIPAddress
** outIPAddress
);
18990 static void _SBIPAddressFree( SBIPAddress
*inIPAddress
);
18991 static void _SBIPAddressFreeList( SBIPAddress
*inList
);
18992 static OSStatus
_SBRDomainCreate( const char *inName
, SBRDomain
**outDomain
);
18993 static void _SBRDomainFree( SBRDomain
*inDomain
);
18994 static OSStatus
_SBRServiceTypeCreate( const char *inName
, SBRServiceType
**outType
);
18995 static void _SBRServiceTypeFree( SBRServiceType
*inType
);
18997 _SBRServiceInstanceCreate(
18998 const char * inName
,
18999 uint32_t inInterfaceIndex
,
19000 const char * inHostname
,
19002 const uint8_t * inTXTPtr
,
19004 uint64_t inDiscoverTimeUs
,
19005 uint64_t inResolveTimeUs
,
19006 SBRServiceInstance
** outInstance
);
19007 static void _SBRServiceInstanceFree( SBRServiceInstance
*inInstance
);
19009 _SBRIPAddressCreate(
19010 const struct sockaddr
* inSockAddr
,
19011 uint64_t inResolveTimeUs
,
19012 SBRIPAddress
** outIPAddress
);
19013 static void _SBRIPAddressFree( SBRIPAddress
*inIPAddress
);
19015 #define ForgetSBIPAddressList( X ) ForgetCustom( X, _SBIPAddressFreeList )
19017 CF_CLASS_DEFINE( ServiceBrowser
);
19020 ServiceBrowserCreate(
19021 dispatch_queue_t inQueue
,
19022 uint32_t inInterfaceIndex
,
19023 const char * inDomain
,
19024 unsigned int inBrowseTimeSecs
,
19025 Boolean inIncludeAWDL
,
19026 ServiceBrowserRef
* outBrowser
)
19029 ServiceBrowserRef obj
;
19031 CF_OBJECT_CREATE( ServiceBrowser
, obj
, err
, exit
);
19033 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
19034 obj
->ifIndex
= inInterfaceIndex
;
19037 obj
->domain
= strdup( inDomain
);
19038 require_action( obj
->domain
, exit
, err
= kNoMemoryErr
);
19040 obj
->browseTimeSecs
= inBrowseTimeSecs
;
19041 obj
->includeAWDL
= inIncludeAWDL
;
19048 CFReleaseNullSafe( obj
);
19052 //===========================================================================================================================
19053 // _ServiceBrowserFinalize
19054 //===========================================================================================================================
19056 static void _ServiceBrowserFinalize( CFTypeRef inObj
)
19058 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inObj
;
19059 StringListItem
* serviceType
;
19061 dispatch_forget( &me
->queue
);
19062 check( !me
->connection
);
19063 check( !me
->domainsQuery
);
19064 ForgetMem( &me
->domain
);
19065 while( ( serviceType
= me
->serviceTypeList
) != NULL
)
19067 me
->serviceTypeList
= serviceType
->next
;
19068 ForgetMem( &serviceType
->str
);
19069 free( serviceType
);
19071 check( !me
->domainList
);
19072 check( !me
->stopTimer
);
19075 //===========================================================================================================================
19076 // ServiceBrowserStart
19077 //===========================================================================================================================
19079 static void _ServiceBrowserStart( void *inContext
);
19081 static void ServiceBrowserStart( ServiceBrowserRef me
)
19084 dispatch_async_f( me
->queue
, me
, _ServiceBrowserStart
);
19087 static void _ServiceBrowserStart( void *inContext
)
19090 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inContext
;
19092 err
= DNSServiceCreateConnection( &me
->connection
);
19093 require_noerr( err
, exit
);
19095 err
= DNSServiceSetDispatchQueue( me
->connection
, me
->queue
);
19096 require_noerr( err
, exit
);
19100 err
= _ServiceBrowserAddDomain( me
, me
->domain
);
19101 require_noerr( err
, exit
);
19105 DNSServiceRef sdRef
;
19107 sdRef
= me
->connection
;
19108 err
= DNSServiceQueryRecord( &sdRef
, kDNSServiceFlagsShareConnection
, kDNSServiceInterfaceIndexLocalOnly
,
19109 "b._dns-sd._udp.local.", kDNSServiceType_PTR
, kDNSServiceClass_IN
, _ServiceBrowserDomainsQueryCallback
, me
);
19110 require_noerr( err
, exit
);
19112 me
->domainsQuery
= sdRef
;
19115 err
= DispatchTimerCreate( dispatch_time_seconds( me
->browseTimeSecs
), DISPATCH_TIME_FOREVER
,
19116 100 * kNanosecondsPerMillisecond
, me
->queue
, _ServiceBrowserTimerHandler
, NULL
, me
, &me
->stopTimer
);
19117 require_noerr( err
, exit
);
19118 dispatch_resume( me
->stopTimer
);
19121 if( err
) _ServiceBrowserStop( me
, err
);
19124 //===========================================================================================================================
19125 // ServiceBrowserAddServiceType
19126 //===========================================================================================================================
19128 static OSStatus
ServiceBrowserAddServiceType( ServiceBrowserRef me
, const char *inServiceType
)
19131 StringListItem
* item
;
19132 StringListItem
** itemPtr
;
19133 StringListItem
* newItem
= NULL
;
19135 for( itemPtr
= &me
->serviceTypeList
; ( item
= *itemPtr
) != NULL
; itemPtr
= &item
->next
)
19137 if( strcmp( item
->str
, inServiceType
) == 0 ) break;
19141 newItem
= (StringListItem
*) calloc( 1, sizeof( *newItem
) );
19142 require_action( newItem
, exit
, err
= kNoMemoryErr
);
19144 newItem
->str
= strdup( inServiceType
);
19145 require_action( newItem
->str
, exit
, err
= kNoMemoryErr
);
19147 *itemPtr
= newItem
;
19153 FreeNullSafe( newItem
);
19157 //===========================================================================================================================
19158 // ServiceBrowserSetCallback
19159 //===========================================================================================================================
19161 static void ServiceBrowserSetCallback( ServiceBrowserRef me
, ServiceBrowserCallback_f inCallback
, void *inContext
)
19163 me
->userCallback
= inCallback
;
19164 me
->userContext
= inContext
;
19167 //===========================================================================================================================
19168 // ServiceBrowserResultsRetain
19169 //===========================================================================================================================
19171 static void ServiceBrowserResultsRetain( ServiceBrowserResults
*inResults
)
19173 ServiceBrowserResultsPrivate
* const results
= (ServiceBrowserResultsPrivate
*) inResults
;
19175 atomic_add_32( &results
->refCount
, 1 );
19178 //===========================================================================================================================
19179 // ServiceBrowserResultsRelease
19180 //===========================================================================================================================
19182 static void ServiceBrowserResultsRelease( ServiceBrowserResults
*inResults
)
19184 ServiceBrowserResultsPrivate
* const results
= (ServiceBrowserResultsPrivate
*) inResults
;
19185 SBRDomain
* domain
;
19187 if( atomic_add_and_fetch_32( &results
->refCount
, -1 ) == 0 )
19189 while( ( domain
= inResults
->domainList
) != NULL
)
19191 inResults
->domainList
= domain
->next
;
19192 _SBRDomainFree( domain
);
19198 //===========================================================================================================================
19199 // _ServiceBrowserStop
19200 //===========================================================================================================================
19202 static void _ServiceBrowserStop( ServiceBrowserRef me
, OSStatus inError
)
19207 SBServiceBrowse
* b
;
19208 SBServiceInstance
* i
;
19210 dispatch_source_forget( &me
->stopTimer
);
19211 DNSServiceForget( &me
->domainsQuery
);
19212 for( d
= me
->domainList
; d
; d
= d
->next
)
19214 DNSServiceForget( &d
->servicesQuery
);
19215 for( t
= d
->typeList
; t
; t
= t
->next
)
19217 for( b
= t
->browseList
; b
; b
= b
->next
)
19219 DNSServiceForget( &b
->browse
);
19220 for( i
= b
->instanceList
; i
; i
= i
->next
)
19222 DNSServiceForget( &i
->resolve
);
19223 DNSServiceForget( &i
->getAddrInfo
);
19228 DNSServiceForget( &me
->connection
);
19230 if( me
->userCallback
)
19232 ServiceBrowserResults
* results
= NULL
;
19234 err
= _ServiceBrowserCreateResults( me
, &results
);
19235 if( !err
) err
= inError
;
19237 me
->userCallback( results
, err
, me
->userContext
);
19238 me
->userCallback
= NULL
;
19239 me
->userContext
= NULL
;
19240 if( results
) ServiceBrowserResultsRelease( results
);
19243 while( ( d
= me
->domainList
) != NULL
)
19245 me
->domainList
= d
->next
;
19246 _SBDomainFree( d
);
19251 //===========================================================================================================================
19252 // _ServiceBrowserAddDomain
19253 //===========================================================================================================================
19255 static OSStatus
_ServiceBrowserAddDomain( ServiceBrowserRef me
, const char *inDomain
)
19259 SBDomain
** domainPtr
;
19260 SBDomain
* newDomain
= NULL
;
19262 for( domainPtr
= &me
->domainList
; ( domain
= *domainPtr
) != NULL
; domainPtr
= &domain
->next
)
19264 if( strcasecmp( domain
->name
, inDomain
) == 0 ) break;
19266 require_action_quiet( !domain
, exit
, err
= kDuplicateErr
);
19268 err
= _SBDomainCreate( inDomain
, me
, &newDomain
);
19269 require_noerr_quiet( err
, exit
);
19271 if( me
->serviceTypeList
)
19273 const StringListItem
* item
;
19275 for( item
= me
->serviceTypeList
; item
; item
= item
->next
)
19277 err
= _ServiceBrowserAddServiceType( me
, newDomain
, item
->str
, me
->ifIndex
);
19278 if( err
== kDuplicateErr
) err
= kNoErr
;
19279 require_noerr( err
, exit
);
19285 DNSServiceFlags flags
;
19286 DNSServiceRef sdRef
;
19288 ASPrintF( &recordName
, "_services._dns-sd._udp.%s", newDomain
->name
);
19289 require_action( recordName
, exit
, err
= kNoMemoryErr
);
19291 flags
= kDNSServiceFlagsShareConnection
;
19292 if( ( me
->ifIndex
== kDNSServiceInterfaceIndexAny
) && me
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
19294 sdRef
= newDomain
->browser
->connection
;
19295 err
= DNSServiceQueryRecord( &sdRef
, flags
, me
->ifIndex
, recordName
, kDNSServiceType_PTR
, kDNSServiceClass_IN
,
19296 _ServiceBrowserServicesQueryCallback
, newDomain
);
19297 free( recordName
);
19298 require_noerr( err
, exit
);
19300 newDomain
->servicesQuery
= sdRef
;
19303 *domainPtr
= newDomain
;
19308 if( newDomain
) _SBDomainFree( newDomain
);
19312 //===========================================================================================================================
19313 // _ServiceBrowserRemoveDomain
19314 //===========================================================================================================================
19316 static OSStatus
_ServiceBrowserRemoveDomain( ServiceBrowserRef me
, const char *inName
)
19320 SBDomain
** domainPtr
;
19322 for( domainPtr
= &me
->domainList
; ( domain
= *domainPtr
) != NULL
; domainPtr
= &domain
->next
)
19324 if( strcasecmp( domain
->name
, inName
) == 0 ) break;
19329 *domainPtr
= domain
->next
;
19330 _SBDomainFree( domain
);
19335 err
= kNotFoundErr
;
19341 //===========================================================================================================================
19342 // _ServiceBrowserTimerHandler
19343 //===========================================================================================================================
19345 static void _ServiceBrowserTimerHandler( void *inContext
)
19347 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inContext
;
19349 _ServiceBrowserStop( me
, kNoErr
);
19352 //===========================================================================================================================
19353 // _ServiceBrowserDomainsQueryCallback
19354 //===========================================================================================================================
19356 static void DNSSD_API
19357 _ServiceBrowserDomainsQueryCallback(
19358 DNSServiceRef inSDRef
,
19359 DNSServiceFlags inFlags
,
19360 uint32_t inInterfaceIndex
,
19361 DNSServiceErrorType inError
,
19362 const char * inFullName
,
19365 uint16_t inRDataLen
,
19366 const void * inRDataPtr
,
19370 ServiceBrowserRef
const me
= (ServiceBrowserRef
) inContext
;
19372 char domainStr
[ kDNSServiceMaxDomainName
];
19375 Unused( inInterfaceIndex
);
19376 Unused( inFullName
);
19381 require_noerr( inError
, exit
);
19383 err
= DomainNameToString( inRDataPtr
, ( (const uint8_t *) inRDataPtr
) + inRDataLen
, domainStr
, NULL
);
19384 require_noerr( err
, exit
);
19386 if( inFlags
& kDNSServiceFlagsAdd
)
19388 err
= _ServiceBrowserAddDomain( me
, domainStr
);
19389 if( err
== kDuplicateErr
) err
= kNoErr
;
19390 require_noerr( err
, exit
);
19394 err
= _ServiceBrowserRemoveDomain( me
, domainStr
);
19395 if( err
== kNotFoundErr
) err
= kNoErr
;
19396 require_noerr( err
, exit
);
19403 //===========================================================================================================================
19404 // _ServiceBrowserServicesQueryCallback
19405 //===========================================================================================================================
19407 static void DNSSD_API
19408 _ServiceBrowserServicesQueryCallback(
19409 DNSServiceRef inSDRef
,
19410 DNSServiceFlags inFlags
,
19411 uint32_t inInterfaceIndex
,
19412 DNSServiceErrorType inError
,
19413 const char * inFullName
,
19416 uint16_t inRDataLen
,
19417 const void * inRDataPtr
,
19422 SBDomain
* const domain
= (SBDomain
*) inContext
;
19423 ServiceBrowserRef
const me
= domain
->browser
;
19424 const uint8_t * src
;
19425 const uint8_t * end
;
19428 uint8_t serviceType
[ 2 * ( 1 + kDomainLabelLengthMax
) + 1 ];
19429 char serviceTypeStr
[ kDNSServiceMaxDomainName
];
19432 Unused( inFullName
);
19437 require_noerr( inError
, exit
);
19439 check( inType
== kDNSServiceType_PTR
);
19440 check( inClass
== kDNSServiceClass_IN
);
19442 // The first two labels of the domain name in the RDATA describe a service type.
19443 // See <https://tools.ietf.org/html/rfc6763#section-9>.
19445 src
= (const uint8_t *) inRDataPtr
;
19446 end
= src
+ inRDataLen
;
19448 for( i
= 0; i
< 2; ++i
)
19452 require_action_quiet( ( end
- src
) > 0, exit
, err
= kUnderrunErr
);
19455 require_action_quiet( ( labelLen
> 0 ) && ( labelLen
<= kDomainLabelLengthMax
), exit
, err
= kMalformedErr
);
19456 require_action_quiet( ( (size_t)( end
- src
) ) >= ( 1 + labelLen
), exit
, err
= kUnderrunErr
);
19458 memcpy( dst
, src
, 1 + labelLen
);
19459 src
+= 1 + labelLen
;
19460 dst
+= 1 + labelLen
;
19464 err
= DomainNameToString( serviceType
, NULL
, serviceTypeStr
, NULL
);
19465 require_noerr( err
, exit
);
19467 if( inFlags
& kDNSServiceFlagsAdd
)
19469 err
= _ServiceBrowserAddServiceType( me
, domain
, serviceTypeStr
, inInterfaceIndex
);
19470 if( err
== kDuplicateErr
) err
= kNoErr
;
19471 require_noerr( err
, exit
);
19475 err
= _ServiceBrowserRemoveServiceType( me
, domain
, serviceTypeStr
, inInterfaceIndex
);
19476 if( err
== kNotFoundErr
) err
= kNoErr
;
19477 require_noerr( err
, exit
);
19484 //===========================================================================================================================
19485 // _ServiceBrowserBrowseCallback
19486 //===========================================================================================================================
19488 static void DNSSD_API
19489 _ServiceBrowserBrowseCallback(
19490 DNSServiceRef inSDRef
,
19491 DNSServiceFlags inFlags
,
19492 uint32_t inInterfaceIndex
,
19493 DNSServiceErrorType inError
,
19494 const char * inName
,
19495 const char * inRegType
,
19496 const char * inDomain
,
19500 const uint64_t nowTicks
= UpTicks();
19501 SBServiceBrowse
* const browse
= (SBServiceBrowse
*) inContext
;
19502 ServiceBrowserRef
const me
= (ServiceBrowserRef
) browse
->browser
;
19506 require_noerr( inError
, exit
);
19508 if( inFlags
& kDNSServiceFlagsAdd
)
19510 err
= _ServiceBrowserAddServiceInstance( me
, browse
, inInterfaceIndex
, inName
, inRegType
, inDomain
,
19511 UpTicksToMicroseconds( nowTicks
- browse
->startTicks
) );
19512 if( err
== kDuplicateErr
) err
= kNoErr
;
19513 require_noerr( err
, exit
);
19517 err
= _ServiceBrowserRemoveServiceInstance( me
, browse
, inName
, inInterfaceIndex
);
19518 if( err
== kNotFoundErr
) err
= kNoErr
;
19519 require_noerr( err
, exit
);
19526 //===========================================================================================================================
19527 // _ServiceBrowserResolveCallback
19528 //===========================================================================================================================
19530 static void DNSSD_API
19531 _ServiceBrowserResolveCallback(
19532 DNSServiceRef inSDRef
,
19533 DNSServiceFlags inFlags
,
19534 uint32_t inInterfaceIndex
,
19535 DNSServiceErrorType inError
,
19536 const char * inFullName
,
19537 const char * inHostname
,
19540 const unsigned char * inTXTPtr
,
19544 const uint64_t nowTicks
= UpTicks();
19545 SBServiceInstance
* const instance
= (SBServiceInstance
*) inContext
;
19546 ServiceBrowserRef
const me
= (ServiceBrowserRef
) instance
->browser
;
19550 Unused( inInterfaceIndex
);
19551 Unused( inFullName
);
19553 require_noerr( inError
, exit
);
19555 if( !MemEqual( instance
->txtPtr
, instance
->txtLen
, inTXTPtr
, inTXTLen
) )
19557 FreeNullSafe( instance
->txtPtr
);
19558 instance
->txtPtr
= memdup( inTXTPtr
, inTXTLen
);
19559 require_action( instance
->txtPtr
, exit
, err
= kNoMemoryErr
);
19561 instance
->txtLen
= inTXTLen
;
19564 instance
->port
= ntohs( inPort
);
19566 if( !instance
->hostname
|| ( strcasecmp( instance
->hostname
, inHostname
) != 0 ) )
19568 DNSServiceRef sdRef
;
19570 if( !instance
->hostname
) instance
->resolveTimeUs
= UpTicksToMicroseconds( nowTicks
- instance
->resolveStartTicks
);
19572 err
= ReplaceString( &instance
->hostname
, NULL
, inHostname
, kSizeCString
);
19573 require_noerr( err
, exit
);
19575 DNSServiceForget( &instance
->getAddrInfo
);
19576 ForgetSBIPAddressList( &instance
->ipaddrList
);
19578 sdRef
= me
->connection
;
19579 instance
->gaiStartTicks
= UpTicks();
19580 err
= DNSServiceGetAddrInfo( &sdRef
, kDNSServiceFlagsShareConnection
, instance
->ifIndex
,
19581 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, instance
->hostname
, _ServiceBrowserGAICallback
, instance
);
19582 require_noerr( err
, exit
);
19584 instance
->getAddrInfo
= sdRef
;
19591 //===========================================================================================================================
19592 // _ServiceBrowserGAICallback
19593 //===========================================================================================================================
19595 static void DNSSD_API
19596 _ServiceBrowserGAICallback(
19597 DNSServiceRef inSDRef
,
19598 DNSServiceFlags inFlags
,
19599 uint32_t inInterfaceIndex
,
19600 DNSServiceErrorType inError
,
19601 const char * inHostname
,
19602 const struct sockaddr
* inSockAddr
,
19607 const uint64_t nowTicks
= UpTicks();
19608 SBServiceInstance
* const instance
= (SBServiceInstance
*) inContext
;
19609 ServiceBrowserRef
const me
= (ServiceBrowserRef
) instance
->browser
;
19612 Unused( inInterfaceIndex
);
19613 Unused( inHostname
);
19616 require_noerr( inError
, exit
);
19618 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
19620 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
19624 if( inFlags
& kDNSServiceFlagsAdd
)
19626 err
= _ServiceBrowserAddIPAddress( me
, instance
, inSockAddr
,
19627 UpTicksToMicroseconds( nowTicks
- instance
->gaiStartTicks
) );
19628 if( err
== kDuplicateErr
) err
= kNoErr
;
19629 require_noerr( err
, exit
);
19633 err
= _ServiceBrowserRemoveIPAddress( me
, instance
, inSockAddr
);
19634 if( err
== kNotFoundErr
) err
= kNoErr
;
19635 require_noerr( err
, exit
);
19642 //===========================================================================================================================
19643 // _ServiceBrowserAddServiceType
19644 //===========================================================================================================================
19647 _ServiceBrowserAddServiceType(
19648 ServiceBrowserRef me
,
19649 SBDomain
* inDomain
,
19650 const char * inName
,
19651 uint32_t inIfIndex
)
19654 SBServiceType
* type
;
19655 SBServiceType
** typePtr
;
19656 SBServiceType
* newType
= NULL
;
19657 SBServiceBrowse
* browse
;
19658 SBServiceBrowse
** browsePtr
;
19659 SBServiceBrowse
* newBrowse
= NULL
;
19660 DNSServiceRef sdRef
;
19661 DNSServiceFlags flags
;
19663 for( typePtr
= &inDomain
->typeList
; ( type
= *typePtr
) != NULL
; typePtr
= &type
->next
)
19665 if( strcasecmp( type
->name
, inName
) == 0 ) break;
19669 err
= _SBServiceTypeCreate( inName
, &newType
);
19670 require_noerr_quiet( err
, exit
);
19675 for( browsePtr
= &type
->browseList
; ( browse
= *browsePtr
) != NULL
; browsePtr
= &browse
->next
)
19677 if( browse
->ifIndex
== inIfIndex
) break;
19679 require_action_quiet( !browse
, exit
, err
= kDuplicateErr
);
19681 err
= _SBServiceBrowseCreate( inIfIndex
, me
, &newBrowse
);
19682 require_noerr_quiet( err
, exit
);
19684 flags
= kDNSServiceFlagsShareConnection
;
19685 if( ( newBrowse
->ifIndex
== kDNSServiceInterfaceIndexAny
) && me
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
19687 sdRef
= me
->connection
;
19688 newBrowse
->startTicks
= UpTicks();
19689 err
= DNSServiceBrowse( &sdRef
, flags
, newBrowse
->ifIndex
, type
->name
, inDomain
->name
, _ServiceBrowserBrowseCallback
,
19691 require_noerr( err
, exit
);
19693 newBrowse
->browse
= sdRef
;
19694 *browsePtr
= newBrowse
;
19699 *typePtr
= newType
;
19704 if( newBrowse
) _SBServiceBrowseFree( newBrowse
);
19705 if( newType
) _SBServiceTypeFree( newType
);
19709 //===========================================================================================================================
19710 // _ServiceBrowserRemoveServiceType
19711 //===========================================================================================================================
19714 _ServiceBrowserRemoveServiceType(
19715 ServiceBrowserRef me
,
19716 SBDomain
* inDomain
,
19717 const char * inName
,
19718 uint32_t inIfIndex
)
19721 SBServiceType
* type
;
19722 SBServiceType
** typePtr
;
19723 SBServiceBrowse
* browse
;
19724 SBServiceBrowse
** browsePtr
;
19728 for( typePtr
= &inDomain
->typeList
; ( type
= *typePtr
) != NULL
; typePtr
= &type
->next
)
19730 if( strcasecmp( type
->name
, inName
) == 0 ) break;
19732 require_action_quiet( type
, exit
, err
= kNotFoundErr
);
19734 for( browsePtr
= &type
->browseList
; ( browse
= *browsePtr
) != NULL
; browsePtr
= &browse
->next
)
19736 if( browse
->ifIndex
== inIfIndex
) break;
19738 require_action_quiet( browse
, exit
, err
= kNotFoundErr
);
19740 *browsePtr
= browse
->next
;
19741 _SBServiceBrowseFree( browse
);
19742 if( !type
->browseList
)
19744 *typePtr
= type
->next
;
19745 _SBServiceTypeFree( type
);
19753 //===========================================================================================================================
19754 // _ServiceBrowserAddServiceInstance
19755 //===========================================================================================================================
19758 _ServiceBrowserAddServiceInstance(
19759 ServiceBrowserRef me
,
19760 SBServiceBrowse
* inBrowse
,
19761 uint32_t inIfIndex
,
19762 const char * inName
,
19763 const char * inRegType
,
19764 const char * inDomain
,
19765 uint64_t inDiscoverTimeUs
)
19768 DNSServiceRef sdRef
;
19769 SBServiceInstance
* instance
;
19770 SBServiceInstance
** instancePtr
;
19771 SBServiceInstance
* newInstance
= NULL
;
19773 for( instancePtr
= &inBrowse
->instanceList
; ( instance
= *instancePtr
) != NULL
; instancePtr
= &instance
->next
)
19775 if( ( instance
->ifIndex
== inIfIndex
) && ( strcasecmp( instance
->name
, inName
) == 0 ) ) break;
19777 require_action_quiet( !instance
, exit
, err
= kDuplicateErr
);
19779 err
= _SBServiceInstanceCreate( inName
, inIfIndex
, inDiscoverTimeUs
, me
, &newInstance
);
19780 require_noerr_quiet( err
, exit
);
19782 sdRef
= me
->connection
;
19783 newInstance
->resolveStartTicks
= UpTicks();
19784 err
= DNSServiceResolve( &sdRef
, kDNSServiceFlagsShareConnection
, newInstance
->ifIndex
, inName
, inRegType
, inDomain
,
19785 _ServiceBrowserResolveCallback
, newInstance
);
19786 require_noerr( err
, exit
);
19788 newInstance
->resolve
= sdRef
;
19789 *instancePtr
= newInstance
;
19790 newInstance
= NULL
;
19793 if( newInstance
) _SBServiceInstanceFree( newInstance
);
19797 //===========================================================================================================================
19798 // _ServiceBrowserRemoveServiceInstance
19799 //===========================================================================================================================
19802 _ServiceBrowserRemoveServiceInstance(
19803 ServiceBrowserRef me
,
19804 SBServiceBrowse
* inBrowse
,
19805 const char * inName
,
19806 uint32_t inIfIndex
)
19809 SBServiceInstance
* instance
;
19810 SBServiceInstance
** ptr
;
19814 for( ptr
= &inBrowse
->instanceList
; ( instance
= *ptr
) != NULL
; ptr
= &instance
->next
)
19816 if( ( instance
->ifIndex
== inIfIndex
) && ( strcasecmp( instance
->name
, inName
) == 0 ) ) break;
19818 require_action_quiet( instance
, exit
, err
= kNotFoundErr
);
19820 *ptr
= instance
->next
;
19821 _SBServiceInstanceFree( instance
);
19828 //===========================================================================================================================
19829 // _ServiceBrowserAddIPAddress
19830 //===========================================================================================================================
19833 _ServiceBrowserAddIPAddress(
19834 ServiceBrowserRef me
,
19835 SBServiceInstance
* inInstance
,
19836 const struct sockaddr
* inSockAddr
,
19837 uint64_t inResolveTimeUs
)
19840 SBIPAddress
* ipaddr
;
19841 SBIPAddress
** ipaddrPtr
;
19842 SBIPAddress
* newIPAddr
= NULL
;
19846 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
19848 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
19853 for( ipaddrPtr
= &inInstance
->ipaddrList
; ( ipaddr
= *ipaddrPtr
) != NULL
; ipaddrPtr
= &ipaddr
->next
)
19855 if( SockAddrCompareAddr( &ipaddr
->sip
, inSockAddr
) == 0 ) break;
19857 require_action_quiet( !ipaddr
, exit
, err
= kDuplicateErr
);
19859 err
= _SBIPAddressCreate( inSockAddr
, inResolveTimeUs
, &newIPAddr
);
19860 require_noerr_quiet( err
, exit
);
19862 *ipaddrPtr
= newIPAddr
;
19867 if( newIPAddr
) _SBIPAddressFree( newIPAddr
);
19871 //===========================================================================================================================
19872 // _ServiceBrowserRemoveIPAddress
19873 //===========================================================================================================================
19876 _ServiceBrowserRemoveIPAddress(
19877 ServiceBrowserRef me
,
19878 SBServiceInstance
* inInstance
,
19879 const struct sockaddr
* inSockAddr
)
19882 SBIPAddress
* ipaddr
;
19883 SBIPAddress
** ipaddrPtr
;
19887 for( ipaddrPtr
= &inInstance
->ipaddrList
; ( ipaddr
= *ipaddrPtr
) != NULL
; ipaddrPtr
= &ipaddr
->next
)
19889 if( SockAddrCompareAddr( &ipaddr
->sip
.sa
, inSockAddr
) == 0 ) break;
19891 require_action_quiet( ipaddr
, exit
, err
= kNotFoundErr
);
19893 *ipaddrPtr
= ipaddr
->next
;
19894 _SBIPAddressFree( ipaddr
);
19901 //===========================================================================================================================
19902 // _ServiceBrowserCreateResults
19903 //===========================================================================================================================
19905 static OSStatus
_ServiceBrowserCreateResults( ServiceBrowserRef me
, ServiceBrowserResults
**outResults
)
19910 SBServiceBrowse
* b
;
19911 SBServiceInstance
* i
;
19913 ServiceBrowserResultsPrivate
* results
;
19914 SBRDomain
** domainPtr
;
19916 results
= (ServiceBrowserResultsPrivate
*) calloc( 1, sizeof( *results
) );
19917 require_action( results
, exit
, err
= kNoMemoryErr
);
19919 results
->refCount
= 1;
19921 domainPtr
= &results
->domainList
;
19922 for( d
= me
->domainList
; d
; d
= d
->next
)
19924 SBRDomain
* domain
;
19925 SBRServiceType
** typePtr
;
19927 err
= _SBRDomainCreate( d
->name
, &domain
);
19928 require_noerr_quiet( err
, exit
);
19929 *domainPtr
= domain
;
19930 domainPtr
= &domain
->next
;
19932 typePtr
= &domain
->typeList
;
19933 for( t
= d
->typeList
; t
; t
= t
->next
)
19935 SBRServiceType
* type
;
19936 SBRServiceInstance
** instancePtr
;
19938 err
= _SBRServiceTypeCreate( t
->name
, &type
);
19939 require_noerr_quiet( err
, exit
);
19941 typePtr
= &type
->next
;
19943 instancePtr
= &type
->instanceList
;
19944 for( b
= t
->browseList
; b
; b
= b
->next
)
19946 for( i
= b
->instanceList
; i
; i
= i
->next
)
19948 SBRServiceInstance
* instance
;
19949 SBRIPAddress
** ipaddrPtr
;
19951 err
= _SBRServiceInstanceCreate( i
->name
, i
->ifIndex
, i
->hostname
, i
->port
, i
->txtPtr
, i
->txtLen
,
19952 i
->discoverTimeUs
, i
->resolveTimeUs
, &instance
);
19953 require_noerr_quiet( err
, exit
);
19954 *instancePtr
= instance
;
19955 instancePtr
= &instance
->next
;
19957 ipaddrPtr
= &instance
->ipaddrList
;
19958 for( a
= i
->ipaddrList
; a
; a
= a
->next
)
19960 SBRIPAddress
* ipaddr
;
19962 err
= _SBRIPAddressCreate( &a
->sip
.sa
, a
->resolveTimeUs
, &ipaddr
);
19963 require_noerr_quiet( err
, exit
);
19965 *ipaddrPtr
= ipaddr
;
19966 ipaddrPtr
= &ipaddr
->next
;
19973 *outResults
= (ServiceBrowserResults
*) results
;
19978 if( results
) ServiceBrowserResultsRelease( (ServiceBrowserResults
*) results
);
19982 //===========================================================================================================================
19984 //===========================================================================================================================
19986 static OSStatus
_SBDomainCreate( const char *inName
, ServiceBrowserRef inBrowser
, SBDomain
**outDomain
)
19991 obj
= (SBDomain
*) calloc( 1, sizeof( *obj
) );
19992 require_action( obj
, exit
, err
= kNoMemoryErr
);
19994 obj
->name
= strdup( inName
);
19995 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
19997 obj
->browser
= inBrowser
;
20004 if( obj
) _SBDomainFree( obj
);
20008 //===========================================================================================================================
20010 //===========================================================================================================================
20012 static void _SBDomainFree( SBDomain
*inDomain
)
20014 SBServiceType
* type
;
20016 ForgetMem( &inDomain
->name
);
20017 DNSServiceForget( &inDomain
->servicesQuery
);
20018 while( ( type
= inDomain
->typeList
) != NULL
)
20020 inDomain
->typeList
= type
->next
;
20021 _SBServiceTypeFree( type
);
20026 //===========================================================================================================================
20027 // _SBServiceTypeCreate
20028 //===========================================================================================================================
20030 static OSStatus
_SBServiceTypeCreate( const char *inName
, SBServiceType
**outType
)
20033 SBServiceType
* obj
;
20035 obj
= (SBServiceType
*) calloc( 1, sizeof( *obj
) );
20036 require_action( obj
, exit
, err
= kNoMemoryErr
);
20038 obj
->name
= strdup( inName
);
20039 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
20046 if( obj
) _SBServiceTypeFree( obj
);
20050 //===========================================================================================================================
20051 // _SBServiceTypeFree
20052 //===========================================================================================================================
20054 static void _SBServiceTypeFree( SBServiceType
*inType
)
20056 SBServiceBrowse
* browse
;
20058 ForgetMem( &inType
->name
);
20059 while( ( browse
= inType
->browseList
) != NULL
)
20061 inType
->browseList
= browse
->next
;
20062 _SBServiceBrowseFree( browse
);
20067 //===========================================================================================================================
20068 // _SBServiceBrowseCreate
20069 //===========================================================================================================================
20071 static OSStatus
_SBServiceBrowseCreate( uint32_t inIfIndex
, ServiceBrowserRef inBrowser
, SBServiceBrowse
**outBrowse
)
20074 SBServiceBrowse
* obj
;
20076 obj
= (SBServiceBrowse
*) calloc( 1, sizeof( *obj
) );
20077 require_action( obj
, exit
, err
= kNoMemoryErr
);
20079 obj
->ifIndex
= inIfIndex
;
20080 obj
->browser
= inBrowser
;
20088 //===========================================================================================================================
20089 // _SBServiceBrowseFree
20090 //===========================================================================================================================
20092 static void _SBServiceBrowseFree( SBServiceBrowse
*inBrowse
)
20094 SBServiceInstance
* instance
;
20096 DNSServiceForget( &inBrowse
->browse
);
20097 while( ( instance
= inBrowse
->instanceList
) != NULL
)
20099 inBrowse
->instanceList
= instance
->next
;
20100 _SBServiceInstanceFree( instance
);
20105 //===========================================================================================================================
20106 // _SBServiceInstanceCreate
20107 //===========================================================================================================================
20110 _SBServiceInstanceCreate(
20111 const char * inName
,
20112 uint32_t inIfIndex
,
20113 uint64_t inDiscoverTimeUs
,
20114 ServiceBrowserRef inBrowser
,
20115 SBServiceInstance
** outInstance
)
20118 SBServiceInstance
* obj
;
20120 obj
= (SBServiceInstance
*) calloc( 1, sizeof( *obj
) );
20121 require_action( obj
, exit
, err
= kNoMemoryErr
);
20123 obj
->name
= strdup( inName
);
20124 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
20126 obj
->ifIndex
= inIfIndex
;
20127 obj
->discoverTimeUs
= inDiscoverTimeUs
;
20128 obj
->browser
= inBrowser
;
20130 *outInstance
= obj
;
20135 if( obj
) _SBServiceInstanceFree( obj
);
20139 //===========================================================================================================================
20140 // _SBServiceInstanceFree
20141 //===========================================================================================================================
20143 static void _SBServiceInstanceFree( SBServiceInstance
*inInstance
)
20145 ForgetMem( &inInstance
->name
);
20146 DNSServiceForget( &inInstance
->resolve
);
20147 ForgetMem( &inInstance
->hostname
);
20148 ForgetMem( &inInstance
->txtPtr
);
20149 DNSServiceForget( &inInstance
->getAddrInfo
);
20150 ForgetSBIPAddressList( &inInstance
->ipaddrList
);
20151 free( inInstance
);
20154 //===========================================================================================================================
20155 // _SBIPAddressCreate
20156 //===========================================================================================================================
20158 static OSStatus
_SBIPAddressCreate( const struct sockaddr
*inSockAddr
, uint64_t inResolveTimeUs
, SBIPAddress
**outIPAddress
)
20163 obj
= (SBIPAddress
*) calloc( 1, sizeof( *obj
) );
20164 require_action( obj
, exit
, err
= kNoMemoryErr
);
20166 SockAddrCopy( inSockAddr
, &obj
->sip
);
20167 obj
->resolveTimeUs
= inResolveTimeUs
;
20169 *outIPAddress
= obj
;
20176 //===========================================================================================================================
20177 // _SBIPAddressFree
20178 //===========================================================================================================================
20180 static void _SBIPAddressFree( SBIPAddress
*inIPAddress
)
20182 free( inIPAddress
);
20185 //===========================================================================================================================
20186 // _SBIPAddressFreeList
20187 //===========================================================================================================================
20189 static void _SBIPAddressFreeList( SBIPAddress
*inList
)
20191 SBIPAddress
* ipaddr
;
20193 while( ( ipaddr
= inList
) != NULL
)
20195 inList
= ipaddr
->next
;
20196 _SBIPAddressFree( ipaddr
);
20200 //===========================================================================================================================
20201 // _SBRDomainCreate
20202 //===========================================================================================================================
20204 static OSStatus
_SBRDomainCreate( const char *inName
, SBRDomain
**outDomain
)
20209 obj
= (SBRDomain
*) calloc( 1, sizeof( *obj
) );
20210 require_action( obj
, exit
, err
= kNoMemoryErr
);
20212 obj
->name
= strdup( inName
);
20213 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
20220 if( obj
) _SBRDomainFree( obj
);
20224 //===========================================================================================================================
20226 //===========================================================================================================================
20228 static void _SBRDomainFree( SBRDomain
*inDomain
)
20230 SBRServiceType
* type
;
20232 ForgetMem( &inDomain
->name
);
20233 while( ( type
= inDomain
->typeList
) != NULL
)
20235 inDomain
->typeList
= type
->next
;
20236 _SBRServiceTypeFree( type
);
20241 //===========================================================================================================================
20242 // _SBRServiceTypeCreate
20243 //===========================================================================================================================
20245 static OSStatus
_SBRServiceTypeCreate( const char *inName
, SBRServiceType
**outType
)
20248 SBRServiceType
* obj
;
20250 obj
= (SBRServiceType
*) calloc( 1, sizeof( *obj
) );
20251 require_action( obj
, exit
, err
= kNoMemoryErr
);
20253 obj
->name
= strdup( inName
);
20254 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
20261 if( obj
) _SBRServiceTypeFree( obj
);
20265 //===========================================================================================================================
20266 // _SBRServiceTypeFree
20267 //===========================================================================================================================
20269 static void _SBRServiceTypeFree( SBRServiceType
*inType
)
20271 SBRServiceInstance
* instance
;
20273 ForgetMem( &inType
->name
);
20274 while( ( instance
= inType
->instanceList
) != NULL
)
20276 inType
->instanceList
= instance
->next
;
20277 _SBRServiceInstanceFree( instance
);
20282 //===========================================================================================================================
20283 // _SBRServiceInstanceCreate
20284 //===========================================================================================================================
20287 _SBRServiceInstanceCreate(
20288 const char * inName
,
20289 uint32_t inInterfaceIndex
,
20290 const char * inHostname
,
20292 const uint8_t * inTXTPtr
,
20294 uint64_t inDiscoverTimeUs
,
20295 uint64_t inResolveTimeUs
,
20296 SBRServiceInstance
** outInstance
)
20299 SBRServiceInstance
* obj
;
20301 obj
= (SBRServiceInstance
*) calloc( 1, sizeof( *obj
) );
20302 require_action( obj
, exit
, err
= kNoMemoryErr
);
20304 obj
->name
= strdup( inName
);
20305 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
20309 obj
->hostname
= strdup( inHostname
);
20310 require_action( obj
->hostname
, exit
, err
= kNoMemoryErr
);
20314 obj
->txtPtr
= (uint8_t *) memdup( inTXTPtr
, inTXTLen
);
20315 require_action( obj
->txtPtr
, exit
, err
= kNoMemoryErr
);
20316 obj
->txtLen
= inTXTLen
;
20318 obj
->discoverTimeUs
= inDiscoverTimeUs
;
20319 obj
->resolveTimeUs
= inResolveTimeUs
;
20320 obj
->ifIndex
= inInterfaceIndex
;
20321 obj
->port
= inPort
;
20323 *outInstance
= obj
;
20328 if( obj
) _SBRServiceInstanceFree( obj
);
20332 //===========================================================================================================================
20333 // _SBRServiceInstanceFree
20334 //===========================================================================================================================
20336 static void _SBRServiceInstanceFree( SBRServiceInstance
*inInstance
)
20338 SBRIPAddress
* ipaddr
;
20340 ForgetMem( &inInstance
->name
);
20341 ForgetMem( &inInstance
->hostname
);
20342 ForgetMem( &inInstance
->txtPtr
);
20343 while( ( ipaddr
= inInstance
->ipaddrList
) != NULL
)
20345 inInstance
->ipaddrList
= ipaddr
->next
;
20346 _SBRIPAddressFree( ipaddr
);
20348 free( inInstance
);
20351 //===========================================================================================================================
20352 // _SBRIPAddressCreate
20353 //===========================================================================================================================
20356 _SBRIPAddressCreate(
20357 const struct sockaddr
* inSockAddr
,
20358 uint64_t inResolveTimeUs
,
20359 SBRIPAddress
** outIPAddress
)
20362 SBRIPAddress
* obj
;
20364 obj
= (SBRIPAddress
*) calloc( 1, sizeof( *obj
) );
20365 require_action( obj
, exit
, err
= kNoMemoryErr
);
20367 SockAddrCopy( inSockAddr
, &obj
->sip
);
20368 obj
->resolveTimeUs
= inResolveTimeUs
;
20370 *outIPAddress
= obj
;
20377 //===========================================================================================================================
20378 // _SBRIPAddressFree
20379 //===========================================================================================================================
20381 static void _SBRIPAddressFree( SBRIPAddress
*inIPAddress
)
20383 free( inIPAddress
);
20386 //===========================================================================================================================
20389 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
20390 //===========================================================================================================================
20392 OSStatus
SocketWriteAll( SocketRef inSock
, const void *inData
, size_t inSize
, int32_t inTimeoutSecs
)
20395 const uint8_t * src
;
20396 const uint8_t * end
;
20398 struct timeval timeout
;
20401 FD_ZERO( &writeSet
);
20402 src
= (const uint8_t *) inData
;
20403 end
= src
+ inSize
;
20406 FD_SET( inSock
, &writeSet
);
20407 timeout
.tv_sec
= inTimeoutSecs
;
20408 timeout
.tv_usec
= 0;
20409 n
= select( (int)( inSock
+ 1 ), NULL
, &writeSet
, NULL
, &timeout
);
20410 if( n
== 0 ) { err
= kTimeoutErr
; goto exit
; }
20411 err
= map_socket_value_errno( inSock
, n
> 0, n
);
20412 require_noerr( err
, exit
);
20414 n
= send( inSock
, (char *) src
, (size_t)( end
- src
), 0 );
20415 err
= map_socket_value_errno( inSock
, n
>= 0, n
);
20416 if( err
== EINTR
) continue;
20417 require_noerr( err
, exit
);
20427 //===========================================================================================================================
20428 // ParseIPv4Address
20430 // Warning: "inBuffer" may be modified even in error cases.
20432 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
20433 //===========================================================================================================================
20435 static OSStatus
ParseIPv4Address( const char *inStr
, uint8_t inBuffer
[ 4 ], const char **outStr
)
20451 for( ; ( c
= *inStr
) != '\0'; ++inStr
)
20453 if( isdigit_safe( c
) )
20455 v
= ( *dst
* 10 ) + ( c
- '0' );
20456 require_action_quiet( v
<= 255, exit
, err
= kRangeErr
);
20457 *dst
= (uint8_t) v
;
20461 require_action_quiet( segments
<= 4, exit
, err
= kOverrunErr
);
20465 else if( ( c
== '.' ) && sawDigit
)
20467 require_action_quiet( segments
< 4, exit
, err
= kMalformedErr
);
20476 require_action_quiet( segments
== 4, exit
, err
= kUnderrunErr
);
20485 //===========================================================================================================================
20486 // StringToIPv4Address
20488 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
20489 //===========================================================================================================================
20492 StringToIPv4Address(
20493 const char * inStr
,
20494 StringToIPAddressFlags inFlags
,
20497 uint32_t * outSubnet
,
20498 uint32_t * outRouter
,
20499 const char ** outStr
)
20509 uint32_t subnetMask
;
20512 require_action( inStr
, exit
, err
= kParamErr
);
20514 // Parse the address-only part of the address (e.g. "1.2.3.4").
20516 err
= ParseIPv4Address( inStr
, buf
, &inStr
);
20517 require_noerr_quiet( err
, exit
);
20518 ip
= (uint32_t)( ( buf
[ 0 ] << 24 ) | ( buf
[ 1 ] << 16 ) | ( buf
[ 2 ] << 8 ) | buf
[ 3 ] );
20521 // Parse the port (if any).
20527 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPort
), exit
, err
= kUnexpectedErr
);
20528 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) port
= ( port
* 10 ) + ( c
- '0' );
20529 require_action_quiet( port
<= 65535, exit
, err
= kRangeErr
);
20533 // Parse the prefix length (if any).
20541 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPrefix
), exit
, err
= kUnexpectedErr
);
20542 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) prefix
= ( prefix
* 10 ) + ( c
- '0' );
20543 require_action_quiet( ( prefix
>= 0 ) && ( prefix
<= 32 ), exit
, err
= kRangeErr
);
20546 subnetMask
= ( prefix
> 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix
) ) : 0;
20547 router
= ( ip
& subnetMask
) | 1;
20550 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
20552 if( outIP
) *outIP
= ip
;
20553 if( outPort
&& hasPort
) *outPort
= port
;
20554 if( outSubnet
&& hasPrefix
) *outSubnet
= subnetMask
;
20555 if( outRouter
&& hasPrefix
) *outRouter
= router
;
20556 if( outStr
) *outStr
= inStr
;
20563 //===========================================================================================================================
20564 // ParseIPv6Address
20566 // Note: Parsed according to the rules specified in RFC 3513.
20567 // Warning: "inBuffer" may be modified even in error cases.
20569 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
20570 //===========================================================================================================================
20572 static OSStatus
ParseIPv6Address( const char *inStr
, int inAllowV4Mapped
, uint8_t inBuffer
[ 16 ], const char **outStr
)
20574 // Table to map uppercase hex characters - '0' to their numeric values.
20575 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
20576 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 };
20581 uint8_t * colonPtr
;
20588 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
20590 for( i
= 0; i
< 16; ++i
) inBuffer
[ i
] = 0;
20592 // Special case leading :: (e.g. "::1") to simplify processing later.
20594 if( *inStr
== ':' )
20597 require_action_quiet( *inStr
== ':', exit
, err
= kMalformedErr
);
20600 // Parse the address.
20608 while( ( ( c
= *inStr
++ ) != '\0' ) && ( c
!= '%' ) && ( c
!= '/' ) && ( c
!= ']' ) )
20610 if( ( c
>= 'a' ) && ( c
<= 'f' ) ) c
-= ( 'a' - 'A' );
20611 if( ( ( c
>= '0' ) && ( c
<= '9' ) ) || ( ( c
>= 'A' ) && ( c
<= 'F' ) ) )
20614 check( c
< (int) countof( kASCIItoHexTable
) );
20615 v
= ( v
<< 4 ) | kASCIItoHexTable
[ c
];
20616 require_action_quiet( v
<= 0xFFFF, exit
, err
= kRangeErr
);
20625 require_action_quiet( !colonPtr
, exit
, err
= kMalformedErr
);
20629 require_action_quiet( *inStr
!= '\0', exit
, err
= kUnderrunErr
);
20630 require_action_quiet( ( dst
+ 2 ) <= lim
, exit
, err
= kOverrunErr
);
20631 *dst
++ = (uint8_t)( ( v
>> 8 ) & 0xFF );
20632 *dst
++ = (uint8_t)( v
& 0xFF );
20638 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
20640 if( inAllowV4Mapped
&& ( c
== '.' ) && ( ( dst
+ 4 ) <= lim
) )
20642 err
= ParseIPv4Address( ptr
, dst
, &inStr
);
20643 require_noerr_quiet( err
, exit
);
20646 ++inStr
; // Increment because the code below expects the end to be at "inStr - 1".
20652 require_action_quiet( ( dst
+ 2 ) <= lim
, exit
, err
= kOverrunErr
);
20653 *dst
++ = (uint8_t)( ( v
>> 8 ) & 0xFF );
20654 *dst
++ = (uint8_t)( v
& 0xFF );
20656 check( dst
<= lim
);
20659 require_action_quiet( dst
< lim
, exit
, err
= kOverrunErr
);
20660 n
= (int)( dst
- colonPtr
);
20661 for( i
= 1; i
<= n
; ++i
)
20663 lim
[ -i
] = colonPtr
[ n
- i
];
20664 colonPtr
[ n
- i
] = 0;
20668 require_action_quiet( dst
== lim
, exit
, err
= kUnderrunErr
);
20670 *outStr
= inStr
- 1;
20677 //===========================================================================================================================
20680 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
20681 //===========================================================================================================================
20683 static OSStatus
ParseIPv6Scope( const char *inStr
, uint32_t *outScope
, const char **outStr
)
20685 #if( TARGET_OS_POSIX )
20687 char scopeStr
[ 64 ];
20694 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
20697 lim
= dst
+ ( countof( scopeStr
) - 1 );
20698 while( ( ( c
= *inStr
) != '\0' ) && ( c
!= ':' ) && ( c
!= '/' ) && ( c
!= ']' ) && ( dst
< lim
) )
20703 check( dst
<= lim
);
20705 // First try to map as a name and if that fails, treat it as a numeric scope.
20707 scope
= if_nametoindex( scopeStr
);
20710 for( ptr
= scopeStr
; ( ( c
= *ptr
) >= '0' ) && ( c
<= '9' ); ++ptr
)
20712 scope
= ( scope
* 10 ) + ( ( (uint8_t) c
) - '0' );
20714 require_action_quiet( c
== '\0', exit
, err
= kMalformedErr
);
20715 require_action_quiet( ( ptr
!= scopeStr
) && ( ( (int)( ptr
- scopeStr
) ) <= 10 ), exit
, err
= kMalformedErr
);
20727 const char * start
;
20731 for( start
= inStr
; ( ( c
= *inStr
) >= '0' ) && ( c
<= '9' ); ++inStr
)
20733 scope
= ( scope
* 10 ) + ( c
- '0' );
20735 require_action_quiet( ( inStr
!= start
) && ( ( (int)( inStr
- start
) ) <= 10 ), exit
, err
= kMalformedErr
);
20746 //===========================================================================================================================
20747 // StringToIPv6Address
20749 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
20750 //===========================================================================================================================
20753 StringToIPv6Address(
20754 const char * inStr
,
20755 StringToIPAddressFlags inFlags
,
20756 uint8_t outIPv6
[ 16 ],
20757 uint32_t * outScope
,
20760 const char ** outStr
)
20763 uint8_t ipv6
[ 16 ];
20774 require_action( inStr
, exit
, err
= kParamErr
);
20776 if( *inStr
== '[' ) ++inStr
; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
20778 // Parse the address-only part of the address (e.g. "1::1").
20780 err
= ParseIPv6Address( inStr
, !( inFlags
& kStringToIPAddressFlagsNoIPv4Mapped
), ipv6
, &inStr
);
20781 require_noerr_quiet( err
, exit
);
20784 // Parse the scope, port, or prefix length.
20795 if( c
== '%' ) // Scope (e.g. "%en0" or "%5")
20797 require_action_quiet( !hasScope
, exit
, err
= kMalformedErr
);
20798 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoScope
), exit
, err
= kUnexpectedErr
);
20800 err
= ParseIPv6Scope( inStr
, &scope
, &inStr
);
20801 require_noerr_quiet( err
, exit
);
20805 else if( c
== ':' ) // Port (e.g. ":80")
20807 require_action_quiet( !hasPort
, exit
, err
= kMalformedErr
);
20808 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPort
), exit
, err
= kUnexpectedErr
);
20809 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) port
= ( port
* 10 ) + ( c
- '0' );
20810 require_action_quiet( port
<= 65535, exit
, err
= kRangeErr
);
20813 else if( c
== '/' ) // Prefix Length (e.g. "/64")
20815 require_action_quiet( !hasPrefix
, exit
, err
= kMalformedErr
);
20816 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPrefix
), exit
, err
= kUnexpectedErr
);
20817 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) prefix
= ( prefix
* 10 ) + ( c
- '0' );
20818 require_action_quiet( ( prefix
>= 0 ) && ( prefix
<= 128 ), exit
, err
= kRangeErr
);
20821 else if( c
== ']' )
20823 require_action_quiet( !hasBracket
, exit
, err
= kMalformedErr
);
20833 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
20835 if( outIPv6
) for( i
= 0; i
< 16; ++i
) outIPv6
[ i
] = ipv6
[ i
];
20836 if( outScope
&& hasScope
) *outScope
= scope
;
20837 if( outPort
&& hasPort
) *outPort
= port
;
20838 if( outPrefix
&& hasPrefix
) *outPrefix
= prefix
;
20839 if( outStr
) *outStr
= inStr
;
20846 //===========================================================================================================================
20847 // StringArray_Free
20849 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
20850 //===========================================================================================================================
20852 void StringArray_Free( char **inArray
, size_t inCount
)
20856 for( i
= 0; i
< inCount
; ++i
)
20858 free( inArray
[ i
] );
20860 if( inCount
> 0 ) free( inArray
);
20863 //===========================================================================================================================
20864 // ParseQuotedEscapedString
20866 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
20867 //===========================================================================================================================
20870 ParseQuotedEscapedString(
20871 const char * inSrc
,
20872 const char * inEnd
,
20873 const char * inDelimiters
,
20876 size_t * outCopiedLen
,
20877 size_t * outTotalLen
,
20878 const char ** outSrc
)
20880 const unsigned char * src
;
20881 const unsigned char * end
;
20882 unsigned char * dst
;
20883 unsigned char * lim
;
20887 Boolean singleQuote
;
20888 Boolean doubleQuote
;
20890 if( inEnd
== NULL
) inEnd
= inSrc
+ strlen( inSrc
);
20891 src
= (const unsigned char *) inSrc
;
20892 end
= (const unsigned char *) inEnd
;
20893 dst
= (unsigned char *) inBuf
;
20894 lim
= dst
+ inMaxLen
;
20895 while( ( src
< end
) && isspace_safe( *src
) ) ++src
; // Skip leading spaces.
20896 if( src
>= end
) return( false );
20898 // Parse each argument from the string.
20900 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
20903 singleQuote
= false;
20904 doubleQuote
= false;
20910 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
20914 singleQuote
= false;
20918 else if( doubleQuote
)
20920 // Double quotes protect everything except double quotes and backslashes. A backslash can be
20921 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
20922 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
20923 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
20924 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
20928 doubleQuote
= false;
20931 else if( c
== '\\' )
20936 if( ( c2
== '"' ) || ( c2
== '\\' ) )
20941 else if( c2
== '\n' )
20946 else if( ( c2
== 'x' ) || ( c2
== 'X' ) )
20950 if( ( ( end
- src
) >= 2 ) && IsHexPair( src
) )
20952 c
= HexPairToByte( src
);
20956 else if( isoctal_safe( c2
) )
20958 if( ( ( end
- src
) >= 3 ) && IsOctalTriple( src
) )
20960 c
= OctalTripleToByte( src
);
20967 else if( strchr( inDelimiters
, c
) )
20971 else if( c
== '\\' )
20973 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
20974 // A backslash followed by a newline disappears completely.
20975 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
20976 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
20986 else if( ( c
== 'x' ) || ( c
== 'X' ) )
20989 if( ( ( end
- src
) >= 2 ) && IsHexPair( src
) )
20991 c
= HexPairToByte( src
);
20995 else if( isoctal_safe( c
) )
20997 if( ( ( end
- src
) >= 3 ) && IsOctalTriple( src
) )
20999 c
= OctalTripleToByte( src
);
21013 else if( c
== '\'' )
21015 singleQuote
= true;
21018 else if( c
== '"' )
21020 doubleQuote
= true;
21026 if( inBuf
) *dst
= c
;
21032 if( outCopiedLen
) *outCopiedLen
= (size_t)( dst
- ( (unsigned char *) inBuf
) );
21033 if( outTotalLen
) *outTotalLen
= totalLen
;
21034 if( outSrc
) *outSrc
= (const char *) src
;
21038 //===========================================================================================================================
21039 // _ServerSocketOpenEx2
21041 // Note: Based on ServerSocketOpenEx() from CoreUtils. Added parameter to not use SO_REUSEPORT.
21042 //===========================================================================================================================
21045 _ServerSocketOpenEx2(
21049 const void * inAddr
,
21053 Boolean inNoPortReuse
,
21054 SocketRef
* outSock
)
21064 port
= ( inPort
< 0 ) ? -inPort
: inPort
; // Negated port number means "try this port, but allow dynamic".
21066 sock
= socket( inFamily
, inType
, inProtocol
);
21067 err
= map_socket_creation_errno( sock
);
21068 require_noerr_quiet( err
, exit
);
21070 #if( defined( SO_NOSIGPIPE ) )
21071 setsockopt( sock
, SOL_SOCKET
, SO_NOSIGPIPE
, &(int){ 1 }, (socklen_t
) sizeof( int ) );
21074 err
= SocketMakeNonBlocking( sock
);
21075 require_noerr( err
, exit
);
21077 // Set receive buffer size. This has to be done on the listening socket *before* listen is called because
21078 // accept does not return until after the window scale option is exchanged during the 3-way handshake.
21079 // Since accept returns a new socket, the only way to use a larger window scale option is to set the buffer
21080 // size on the listening socket since SO_RCVBUF is inherited by the accepted socket. See UNPv1e3 Section 7.5.
21082 err
= SocketSetBufferSize( sock
, SO_RCVBUF
, inRcvBufSize
);
21083 check_noerr( err
);
21085 // Allow port or address reuse because we may bind separate IPv4 and IPv6 sockets to the same port.
21087 if( ( inType
!= SOCK_DGRAM
) || !inNoPortReuse
)
21090 name
= ( inType
== SOCK_DGRAM
) ? SO_REUSEPORT
: SO_REUSEADDR
;
21091 err
= setsockopt( sock
, SOL_SOCKET
, name
, (char *) &option
, (socklen_t
) sizeof( option
) );
21092 err
= map_socket_noerr_errno( sock
, err
);
21093 require_noerr( err
, exit
);
21096 if( inFamily
== AF_INET
)
21098 // Bind to the port. If it fails, retry with a dynamic port.
21100 memset( &sip
.v4
, 0, sizeof( sip
.v4
) );
21101 SIN_LEN_SET( &sip
.v4
);
21102 sip
.v4
.sin_family
= AF_INET
;
21103 sip
.v4
.sin_port
= htons( (uint16_t) port
);
21104 sip
.v4
.sin_addr
.s_addr
= inAddr
? *( (const uint32_t *) inAddr
) : htonl( INADDR_ANY
);
21105 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v4
) );
21106 err
= map_socket_noerr_errno( sock
, err
);
21107 if( err
&& ( inPort
< 0 ) )
21109 sip
.v4
.sin_port
= 0;
21110 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v4
) );
21111 err
= map_socket_noerr_errno( sock
, err
);
21113 require_noerr( err
, exit
);
21115 #if( defined( AF_INET6 ) )
21116 else if( inFamily
== AF_INET6
)
21118 // Restrict this socket to IPv6 only because we're going to use a separate socket for IPv4.
21121 err
= setsockopt( sock
, IPPROTO_IPV6
, IPV6_V6ONLY
, (char *) &option
, (socklen_t
) sizeof( option
) );
21122 err
= map_socket_noerr_errno( sock
, err
);
21123 require_noerr( err
, exit
);
21125 // Bind to the port. If it fails, retry with a dynamic port.
21127 memset( &sip
.v6
, 0, sizeof( sip
.v6
) );
21128 SIN6_LEN_SET( &sip
.v6
);
21129 sip
.v6
.sin6_family
= AF_INET6
;
21130 sip
.v6
.sin6_port
= htons( (uint16_t) port
);
21131 sip
.v6
.sin6_addr
= inAddr
? *( (const struct in6_addr
*) inAddr
) : in6addr_any
;
21132 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v6
) );
21133 err
= map_socket_noerr_errno( sock
, err
);
21134 if( err
&& ( inPort
< 0 ) )
21136 sip
.v6
.sin6_port
= 0;
21137 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v6
) );
21138 err
= map_socket_noerr_errno( sock
, err
);
21140 require_noerr( err
, exit
);
21145 dlogassert( "Unsupported family: %d", inFamily
);
21146 err
= kUnsupportedErr
;
21150 if( inType
== SOCK_STREAM
)
21152 err
= listen( sock
, SOMAXCONN
);
21153 err
= map_socket_noerr_errno( sock
, err
);
21156 err
= listen( sock
, 5 );
21157 err
= map_socket_noerr_errno( sock
, err
);
21158 require_noerr( err
, exit
);
21164 len
= (socklen_t
) sizeof( sip
);
21165 err
= getsockname( sock
, &sip
.sa
, &len
);
21166 err
= map_socket_noerr_errno( sock
, err
);
21167 require_noerr( err
, exit
);
21169 *outPort
= SockAddrGetPort( &sip
);
21172 sock
= kInvalidSocketRef
;
21175 ForgetSocket( &sock
);
21179 //===========================================================================================================================
21182 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
21183 //===========================================================================================================================
21185 void * memdup( const void *inPtr
, size_t inLen
)
21189 mem
= malloc( ( inLen
> 0 ) ? inLen
: 1 ); // If inLen is 0, use 1 since malloc( 0 ) is not well defined.
21190 require( mem
, exit
);
21191 if( inLen
> 0 ) memcpy( mem
, inPtr
, inLen
);
21197 #if( !TARGET_OS_WINDOWS )
21198 //===========================================================================================================================
21201 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
21202 //===========================================================================================================================
21204 int memicmp( const void *inP1
, const void *inP2
, size_t inLen
)
21206 const unsigned char * p1
;
21207 const unsigned char * e1
;
21208 const unsigned char * p2
;
21212 p1
= (const unsigned char *) inP1
;
21214 p2
= (const unsigned char *) inP2
;
21219 c1
= tolower( c1
);
21220 c2
= tolower( c2
);
21221 if( c1
< c2
) return( -1 );
21222 if( c1
> c2
) return( 1 );
21228 //===========================================================================================================================
21231 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
21232 //===========================================================================================================================
21234 uint32_t FNV1( const void *inData
, size_t inSize
)
21236 const uint8_t * src
= (const uint8_t *) inData
;
21237 const uint8_t * const end
= src
+ inSize
;
21240 hash
= 0x811c9dc5U
;
21241 while( src
!= end
)
21243 hash
*= 0x01000193;