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/CFUtils.h>
10 #include <CoreUtils/CommandLineUtils.h>
11 #include <CoreUtils/DataBufferUtils.h>
12 #include <CoreUtils/DebugServices.h>
13 #include <CoreUtils/HTTPUtils.h>
14 #include <CoreUtils/JSONUtils.h>
15 #include <CoreUtils/LogUtils.h>
16 #include <CoreUtils/MiscUtils.h>
17 #include <CoreUtils/NetUtils.h>
18 #include <CoreUtils/PrintFUtils.h>
19 #include <CoreUtils/RandomNumberUtils.h>
20 #include <CoreUtils/SoftLinking.h>
21 #include <CoreUtils/StringUtils.h>
22 #include <CoreUtils/TickUtils.h>
24 #include <dns_sd_private.h>
26 #include CF_RUNTIME_HEADER
28 #if( TARGET_OS_DARWIN )
29 #include <CFNetwork/CFHost.h>
30 #include <CoreFoundation/CoreFoundation.h>
31 #include <SystemConfiguration/SCPrivate.h>
37 #include <sys/proc_info.h>
40 #if( TARGET_OS_POSIX )
41 #include <sys/resource.h>
44 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
45 #include "tweetnacl.h" // TweetNaCl from <https://tweetnacl.cr.yp.to/software.html>.
48 //===========================================================================================================================
50 //===========================================================================================================================
52 #define kDNSSDUtilNumVersion NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 )
54 #if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) )
55 #define DNSSDUTIL_SOURCE_VERSION "0.0.0"
58 //===========================================================================================================================
60 //===========================================================================================================================
62 // DNS-SD API flag descriptors
64 #define kDNSServiceFlagsDescriptors \
65 "\x00" "AutoTrigger\0" \
68 "\x03" "NoAutoRename\0" \
71 "\x06" "BrowseDomains\0" \
72 "\x07" "RegistrationDomains\0" \
73 "\x08" "LongLivedQuery\0" \
74 "\x09" "AllowRemoteQuery\0" \
75 "\x0A" "ForceMulticast\0" \
76 "\x0B" "KnownUnique\0" \
77 "\x0C" "ReturnIntermediates\0" \
78 "\x0D" "NonBrowsable\0" \
79 "\x0E" "ShareConnection\0" \
80 "\x0F" "SuppressUnusable\0" \
82 "\x11" "IncludeP2P\0" \
83 "\x12" "WakeOnResolve\0" \
84 "\x13" "BackgroundTrafficClass\0" \
85 "\x14" "IncludeAWDL\0" \
87 "\x16" "UnicastResponse\0" \
88 "\x17" "ValidateOptional\0" \
89 "\x18" "WakeOnlyService\0" \
90 "\x19" "ThresholdOne\0" \
91 "\x1A" "ThresholdFinder\0" \
92 "\x1B" "DenyCellular\0" \
93 "\x1C" "ServiceIndex\0" \
94 "\x1D" "DenyExpensive\0" \
95 "\x1E" "PathEvaluationDone\0" \
98 #define kDNSServiceProtocolDescriptors \
105 #define kBadDNSServiceRef ( (DNSServiceRef)(intptr_t) -1 )
107 //===========================================================================================================================
109 //===========================================================================================================================
112 #define kDNSCompressionOffsetMax 0x3FFF
113 #define kDNSMaxUDPMessageSize 512
114 #define kDNSMaxTCPMessageSize UINT16_MAX
116 #define kDomainLabelLengthMax 63
117 #define kDomainNameLengthMax 256
123 uint8_t questionCount
[ 2 ];
124 uint8_t answerCount
[ 2 ];
125 uint8_t authorityCount
[ 2 ];
126 uint8_t additionalCount
[ 2 ];
130 #define kDNSHeaderLength 12
131 check_compile_time( sizeof( DNSHeader
) == kDNSHeaderLength
);
133 #define DNSHeaderGetID( HDR ) ReadBig16( ( HDR )->id )
134 #define DNSHeaderGetFlags( HDR ) ReadBig16( ( HDR )->flags )
135 #define DNSHeaderGetQuestionCount( HDR ) ReadBig16( ( HDR )->questionCount )
136 #define DNSHeaderGetAnswerCount( HDR ) ReadBig16( ( HDR )->answerCount )
137 #define DNSHeaderGetAuthorityCount( HDR ) ReadBig16( ( HDR )->authorityCount )
138 #define DNSHeaderGetAdditionalCount( HDR ) ReadBig16( ( HDR )->additionalCount )
140 #define DNSHeaderSetID( HDR, X ) WriteBig16( ( HDR )->id, (X) )
141 #define DNSHeaderSetFlags( HDR, X ) WriteBig16( ( HDR )->flags, (X) )
142 #define DNSHeaderSetQuestionCount( HDR, X ) WriteBig16( ( HDR )->questionCount, (X) )
143 #define DNSHeaderSetAnswerCount( HDR, X ) WriteBig16( ( HDR )->answerCount, (X) )
144 #define DNSHeaderSetAuthorityCount( HDR, X ) WriteBig16( ( HDR )->authorityCount, (X) )
145 #define DNSHeaderSetAdditionalCount( HDR, X ) WriteBig16( ( HDR )->additionalCount, (X) )
147 // Single-bit DNS header fields
149 #define kDNSHeaderFlag_Response ( 1 << 15 ) // QR (bit 15), Query (0)/Response (1)
150 #define kDNSHeaderFlag_AuthAnswer ( 1 << 10 ) // AA (bit 10), Authoritative Answer
151 #define kDNSHeaderFlag_Truncation ( 1 << 9 ) // TC (bit 9), TrunCation
152 #define kDNSHeaderFlag_RecursionDesired ( 1 << 8 ) // RD (bit 8), Recursion Desired
153 #define kDNSHeaderFlag_RecursionAvailable ( 1 << 7 ) // RA (bit 7), Recursion Available
154 #define kDNSHeaderFlag_Z ( 1 << 6 ) // Z (bit 6), Reserved (must be zero)
155 #define kDNSHeaderFlag_AuthenticData ( 1 << 5 ) // AD (bit 5), Authentic Data (RFC 2535, Section 6)
156 #define kDNSHeaderFlag_CheckingDisabled ( 1 << 4 ) // CD (bit 4), Checking Disabled (RFC 2535, Section 6)
158 // OPCODE (bits 14-11), Operation Code
160 #define DNSFlagsGetOpCode( FLAGS ) ( ( (FLAGS) >> 11 ) & 0x0FU )
161 #define DNSFlagsSetOpCode( FLAGS, OPCODE ) \
162 do{ (FLAGS) = ( (FLAGS) & ~0x7800U ) | ( ( (OPCODE) & 0x0FU ) << 11 ); } while( 0 )
164 #define kDNSOpCode_Query 0 // QUERY (standard query)
165 #define kDNSOpCode_InverseQuery 1 // IQUERY (inverse query)
166 #define kDNSOpCode_Status 2 // STATUS
167 #define kDNSOpCode_Notify 4 // NOTIFY
168 #define kDNSOpCode_Update 5 // UPDATE
170 // RCODE (bits 3-0), Response Code
172 #define DNSFlagsGetRCode( FLAGS ) ( (FLAGS) & 0x0FU )
173 #define DNSFlagsSetRCode( FLAGS, RCODE ) \
174 do{ (FLAGS) = ( (FLAGS) & ~0x000FU ) | ( (RCODE) & 0x0FU ); } while( 0 )
176 #define kDNSRCode_NoError 0
177 #define kDNSRCode_FormatError 1
178 #define kDNSRCode_ServerFailure 2
179 #define kDNSRCode_NXDomain 3
180 #define kDNSRCode_NotImplemented 4
181 #define kDNSRCode_Refused 5
188 } DNSQuestionFixedFields
;
190 check_compile_time( sizeof( DNSQuestionFixedFields
) == 4 );
192 #define DNSQuestionFixedFieldsInit( FIELDS, QTYPE, QCLASS ) \
193 do { WriteBig16( (FIELDS)->type, QTYPE ); WriteBig16( (FIELDS)->class, QCLASS ); } while( 0 )
195 #define DNSQuestionFixedFieldsGetType( FIELDS ) ReadBig16( (FIELDS)->type )
196 #define DNSQuestionFixedFieldsGetClass( FIELDS ) ReadBig16( (FIELDS)->class )
203 uint8_t rdlength
[ 2 ];
205 } DNSRecordFixedFields
;
207 check_compile_time( sizeof( DNSRecordFixedFields
) == 10 );
209 #define DNSRecordFixedFieldsInit( FIELDS, TYPE, CLASS, TTL, RDLENGTH ) \
212 WriteBig16( (FIELDS)->type, TYPE ); \
213 WriteBig16( (FIELDS)->class, CLASS ); \
214 WriteBig32( (FIELDS)->ttl, TTL ); \
215 WriteBig16( (FIELDS)->rdlength, RDLENGTH ); \
219 //===========================================================================================================================
221 //===========================================================================================================================
223 #define kMDNSPort 5353
225 #define kDefaultMDNSMessageID 0
226 #define kDefaultMDNSQueryFlags 0
228 #define kQClassUnicastResponseBit ( 1U << 15 )
229 #define kRRClassCacheFlushBit ( 1U << 15 )
231 //===========================================================================================================================
233 //===========================================================================================================================
235 // IPv4 address block 203.0.113.0/24 (TEST-NET-3) is reserved for documentation. See <https://tools.ietf.org/html/rfc5737>.
237 #define kTestDNSServerBaseAddrV4 UINT32_C( 0xCB007100 ) // 203.0.113.0
239 // IPv6 address block 2001:db8::/32 is reserved for documentation. See <https://tools.ietf.org/html/rfc3849>.
241 #define kTestDNSServerBaseAddrV6 \
242 ( (const uint8_t *) "\x20\x01\x0D\xB8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" )
244 //===========================================================================================================================
245 // Gerneral Command Options
246 //===========================================================================================================================
248 // Command option macros
250 #define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON ) \
251 CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None, \
254 #define kRequiredOptionSuffix " [REQUIRED]"
256 #define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
257 CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, \
258 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
259 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
261 #define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
262 MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
264 #define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
265 CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
266 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
267 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
269 #define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \
270 CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL )
272 #define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
273 CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
274 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
275 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
277 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
278 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
280 #define CFStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
281 CLI_OPTION_CFSTRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
282 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
283 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
285 // DNS-SD API flag options
287 static int gDNSSDFlags
= 0;
288 static int gDNSSDFlag_BrowseDomains
= false;
289 static int gDNSSDFlag_DenyCellular
= false;
290 static int gDNSSDFlag_DenyExpensive
= false;
291 static int gDNSSDFlag_ForceMulticast
= false;
292 static int gDNSSDFlag_IncludeAWDL
= false;
293 static int gDNSSDFlag_NoAutoRename
= false;
294 static int gDNSSDFlag_PathEvaluationDone
= false;
295 static int gDNSSDFlag_RegistrationDomains
= false;
296 static int gDNSSDFlag_ReturnIntermediates
= false;
297 static int gDNSSDFlag_Shared
= false;
298 static int gDNSSDFlag_SuppressUnusable
= false;
299 static int gDNSSDFlag_Timeout
= false;
300 static int gDNSSDFlag_UnicastResponse
= false;
301 static int gDNSSDFlag_Unique
= false;
302 static int gDNSSDFlag_WakeOnResolve
= false;
304 #define DNSSDFlagsOption() \
305 IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \
306 "DNSServiceFlags as an integer. This value is bitwise ORed with other single flag options.", false )
308 #define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \
309 BooleanOption( SHORT_CHAR, # FLAG_NAME, &gDNSSDFlag_ ## FLAG_NAME, "Use kDNSServiceFlags" # FLAG_NAME "." )
311 #define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular )
312 #define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive )
313 #define DNSSDFlagsOption_ForceMulticast() DNSSDFlagOption( 'M', ForceMulticast )
314 #define DNSSDFlagsOption_IncludeAWDL() DNSSDFlagOption( 'A', IncludeAWDL )
315 #define DNSSDFlagsOption_NoAutoRename() DNSSDFlagOption( 'N', NoAutoRename )
316 #define DNSSDFlagsOption_PathEvalDone() DNSSDFlagOption( 'P', PathEvaluationDone )
317 #define DNSSDFlagsOption_ReturnIntermediates() DNSSDFlagOption( 'I', ReturnIntermediates )
318 #define DNSSDFlagsOption_Shared() DNSSDFlagOption( 'S', Shared )
319 #define DNSSDFlagsOption_SuppressUnusable() DNSSDFlagOption( 'S', SuppressUnusable )
320 #define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout )
321 #define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse )
322 #define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique )
323 #define DNSSDFlagsOption_WakeOnResolve() DNSSDFlagOption( 'W', WakeOnResolve )
327 static const char * gInterface
= NULL
;
329 #define InterfaceOption() \
330 StringOption( 'i', "interface", &gInterface, "interface", \
331 "Network interface by name or index. Use index -1 for local-only.", false )
333 // Connection options
335 #define kConnectionArg_Normal ""
336 #define kConnectionArgPrefix_PID "pid:"
337 #define kConnectionArgPrefix_UUID "uuid:"
339 static const char * gConnectionOpt
= kConnectionArg_Normal
;
341 #define ConnectionOptions() \
342 { kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type", \
343 kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL, \
344 "Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL }
346 #define kConnectionSection_Name "Connection Option"
347 #define kConnectionSection_Text \
348 "The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n" \
349 "the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n" \
350 "specifying the connection option without an argument, i.e.,\n" \
354 "To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n" \
356 " --connection=pid:<PID>\n" \
358 "to specify the delegator by PID, or use\n" \
360 " --connection=uuid:<UUID>\n" \
362 "to specify the delegator by UUID.\n" \
364 "To not use a main connection at all, but instead perform operations on their own implicit connections, use\n" \
368 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
370 // Help text for record data options
372 #define kRDataArgPrefix_Domain "domain:"
373 #define kRDataArgPrefix_File "file:"
374 #define kRDataArgPrefix_HexString "hex:"
375 #define kRDataArgPrefix_IPv4 "ipv4:"
376 #define kRDataArgPrefix_IPv6 "ipv6:"
377 #define kRDataArgPrefix_SRV "srv:"
378 #define kRDataArgPrefix_String "string:"
379 #define kRDataArgPrefix_TXT "txt:"
381 #define kRecordDataSection_Name "Record Data Arguments"
382 #define kRecordDataSection_Text \
383 "A record data argument is specified in one of the following formats:\n" \
385 "Format Syntax Example\n" \
386 "Domain name domain:<domain name> domain:demo._test._tcp.local\n" \
387 "File containing record data file:<file path> file:/path/to/rdata.bin\n" \
388 "Hexadecimal string hex:<hex string> hex:c0000201 or hex:'C0 00 02 01'\n" \
389 "IPv4 address ipv4:<IPv4 address> ipv4:192.0.2.1\n" \
390 "IPv6 address ipv6:<IPv6 address> ipv6:2001:db8::1\n" \
391 "SRV record srv:<priority>,<weight>,<port>,<target> srv:0,0,64206,example.local\n" \
392 "String (w/escaped hex bytes) string:<string> string:'\\x09color=red'\n" \
393 "TXT record keys and values txt:<comma-delimited keys and values> txt:'vers=1.0,lang=en\\,es\\,fr,passreq'\n"
395 #define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text )
397 //===========================================================================================================================
398 // Browse Command Options
399 //===========================================================================================================================
401 static char ** gBrowse_ServiceTypes
= NULL
;
402 static size_t gBrowse_ServiceTypesCount
= 0;
403 static const char * gBrowse_Domain
= NULL
;
404 static int gBrowse_DoResolve
= false;
405 static int gBrowse_QueryTXT
= false;
406 static int gBrowse_TimeLimitSecs
= 0;
408 static CLIOption kBrowseOpts
[] =
411 MultiStringOption( 't', "type", &gBrowse_ServiceTypes
, &gBrowse_ServiceTypesCount
, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ),
412 StringOption( 'd', "domain", &gBrowse_Domain
, "domain", "Domain in which to browse for the service type(s).", false ),
414 CLI_OPTION_GROUP( "Flags" ),
416 DNSSDFlagsOption_IncludeAWDL(),
418 CLI_OPTION_GROUP( "Operation" ),
420 BooleanOption( 0 , "resolve", &gBrowse_DoResolve
, "Resolve service instances." ),
421 BooleanOption( 0 , "queryTXT", &gBrowse_QueryTXT
, "Query TXT records of service instances." ),
422 IntegerOption( 'l', "timeLimit", &gBrowse_TimeLimitSecs
, "seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ),
428 //===========================================================================================================================
429 // GetAddrInfo Command Options
430 //===========================================================================================================================
432 static const char * gGetAddrInfo_Name
= NULL
;
433 static int gGetAddrInfo_ProtocolIPv4
= false;
434 static int gGetAddrInfo_ProtocolIPv6
= false;
435 static int gGetAddrInfo_OneShot
= false;
436 static int gGetAddrInfo_TimeLimitSecs
= 0;
438 static CLIOption kGetAddrInfoOpts
[] =
441 StringOption( 'n', "name", &gGetAddrInfo_Name
, "domain name", "Domain name to resolve.", true ),
442 BooleanOption( 0 , "ipv4", &gGetAddrInfo_ProtocolIPv4
, "Use kDNSServiceProtocol_IPv4." ),
443 BooleanOption( 0 , "ipv6", &gGetAddrInfo_ProtocolIPv6
, "Use kDNSServiceProtocol_IPv6." ),
445 CLI_OPTION_GROUP( "Flags" ),
447 DNSSDFlagsOption_DenyCellular(),
448 DNSSDFlagsOption_DenyExpensive(),
449 DNSSDFlagsOption_PathEvalDone(),
450 DNSSDFlagsOption_ReturnIntermediates(),
451 DNSSDFlagsOption_SuppressUnusable(),
452 DNSSDFlagsOption_Timeout(),
454 CLI_OPTION_GROUP( "Operation" ),
456 BooleanOption( 'o', "oneshot", &gGetAddrInfo_OneShot
, "Finish after first set of results." ),
457 IntegerOption( 'l', "timeLimit", &gGetAddrInfo_TimeLimitSecs
, "seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ),
463 //===========================================================================================================================
464 // QueryRecord Command Options
465 //===========================================================================================================================
467 static const char * gQueryRecord_Name
= NULL
;
468 static const char * gQueryRecord_Type
= NULL
;
469 static int gQueryRecord_OneShot
= false;
470 static int gQueryRecord_TimeLimitSecs
= 0;
471 static int gQueryRecord_RawRData
= false;
473 static CLIOption kQueryRecordOpts
[] =
476 StringOption( 'n', "name", &gQueryRecord_Name
, "domain name", "Full domain name of record to query.", true ),
477 StringOption( 't', "type", &gQueryRecord_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
479 CLI_OPTION_GROUP( "Flags" ),
481 DNSSDFlagsOption_IncludeAWDL(),
482 DNSSDFlagsOption_ForceMulticast(),
483 DNSSDFlagsOption_Timeout(),
484 DNSSDFlagsOption_ReturnIntermediates(),
485 DNSSDFlagsOption_SuppressUnusable(),
486 DNSSDFlagsOption_UnicastResponse(),
487 DNSSDFlagsOption_DenyCellular(),
488 DNSSDFlagsOption_DenyExpensive(),
489 DNSSDFlagsOption_PathEvalDone(),
491 CLI_OPTION_GROUP( "Operation" ),
493 BooleanOption( 'o', "oneshot", &gQueryRecord_OneShot
, "Finish after first set of results." ),
494 IntegerOption( 'l', "timeLimit", &gQueryRecord_TimeLimitSecs
, "seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ),
495 BooleanOption( 0 , "raw", &gQueryRecord_RawRData
, "Show record data as a hexdump." ),
501 //===========================================================================================================================
502 // Register Command Options
503 //===========================================================================================================================
505 static const char * gRegister_Name
= NULL
;
506 static const char * gRegister_Type
= NULL
;
507 static const char * gRegister_Domain
= NULL
;
508 static int gRegister_Port
= 0;
509 static const char * gRegister_TXT
= NULL
;
510 static int gRegister_LifetimeMs
= -1;
511 static const char ** gAddRecord_Types
= NULL
;
512 static size_t gAddRecord_TypesCount
= 0;
513 static const char ** gAddRecord_Data
= NULL
;
514 static size_t gAddRecord_DataCount
= 0;
515 static const char ** gAddRecord_TTLs
= NULL
;
516 static size_t gAddRecord_TTLsCount
= 0;
517 static const char * gUpdateRecord_Data
= NULL
;
518 static int gUpdateRecord_DelayMs
= 0;
519 static int gUpdateRecord_TTL
= 0;
521 static CLIOption kRegisterOpts
[] =
524 StringOption( 'n', "name", &gRegister_Name
, "service name", "Name of service.", false ),
525 StringOption( 't', "type", &gRegister_Type
, "service type", "Service type, e.g., \"_ssh._tcp\".", true ),
526 StringOption( 'd', "domain", &gRegister_Domain
, "domain", "Domain in which to advertise the service.", false ),
527 IntegerOption( 'p', "port", &gRegister_Port
, "port number", "Service's port number.", true ),
528 StringOption( 0 , "txt", &gRegister_TXT
, "record data", "The TXT record data. See " kRecordDataSection_Name
" below.", false ),
530 CLI_OPTION_GROUP( "Flags" ),
532 DNSSDFlagsOption_IncludeAWDL(),
533 DNSSDFlagsOption_NoAutoRename(),
535 CLI_OPTION_GROUP( "Operation" ),
536 IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs
, "ms", "Lifetime of the service registration in milliseconds.", false ),
538 CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ),
539 StringOption( 0 , "updateData", &gUpdateRecord_Data
, "record data", "Record data for the record update. See " kRecordDataSection_Name
" below.", false ),
540 IntegerOption( 0 , "updateDelay", &gUpdateRecord_DelayMs
, "ms", "Number of milliseconds after registration to wait before record update.", false ),
541 IntegerOption( 0 , "updateTTL", &gUpdateRecord_TTL
, "seconds", "Time-to-live of the updated record.", false ),
543 CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ),
544 MultiStringOption( 0 , "addType", &gAddRecord_Types
, &gAddRecord_TypesCount
, "record type", "Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ),
545 MultiStringOptionEx( 0 , "addData", &gAddRecord_Data
, &gAddRecord_DataCount
, "record data", "Additional record's data. See " kRecordDataSection_Name
" below.", false, NULL
),
546 MultiStringOption( 0 , "addTTL", &gAddRecord_TTLs
, &gAddRecord_TTLsCount
, "seconds", "Time-to-live of additional record in seconds. Use '0' for default.", false ),
552 //===========================================================================================================================
553 // RegisterRecord Command Options
554 //===========================================================================================================================
556 static const char * gRegisterRecord_Name
= NULL
;
557 static const char * gRegisterRecord_Type
= NULL
;
558 static const char * gRegisterRecord_Data
= NULL
;
559 static int gRegisterRecord_TTL
= 0;
560 static int gRegisterRecord_LifetimeMs
= -1;
561 static const char * gRegisterRecord_UpdateData
= NULL
;
562 static int gRegisterRecord_UpdateDelayMs
= 0;
563 static int gRegisterRecord_UpdateTTL
= 0;
565 static CLIOption kRegisterRecordOpts
[] =
568 StringOption( 'n', "name", &gRegisterRecord_Name
, "record name", "Fully qualified domain name of record.", true ),
569 StringOption( 't', "type", &gRegisterRecord_Type
, "record type", "Record type by name (e.g., TXT, PTR, A) or number.", true ),
570 StringOption( 'd', "data", &gRegisterRecord_Data
, "record data", "The record data. See " kRecordDataSection_Name
" below.", false ),
571 IntegerOption( 0 , "ttl", &gRegisterRecord_TTL
, "seconds", "Time-to-live in seconds. Use '0' for default.", false ),
573 CLI_OPTION_GROUP( "Flags" ),
575 DNSSDFlagsOption_IncludeAWDL(),
576 DNSSDFlagsOption_Shared(),
577 DNSSDFlagsOption_Unique(),
579 CLI_OPTION_GROUP( "Operation" ),
580 IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs
, "ms", "Lifetime of the service registration in milliseconds.", false ),
582 CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ),
583 StringOption( 0 , "updateData", &gRegisterRecord_UpdateData
, "record data", "Record data for the record update.", false ),
584 IntegerOption( 0 , "updateDelay", &gRegisterRecord_UpdateDelayMs
, "ms", "Number of milliseconds after registration to wait before record update.", false ),
585 IntegerOption( 0 , "updateTTL", &gRegisterRecord_UpdateTTL
, "seconds", "Time-to-live of the updated record.", false ),
591 //===========================================================================================================================
592 // Resolve Command Options
593 //===========================================================================================================================
595 static char * gResolve_Name
= NULL
;
596 static char * gResolve_Type
= NULL
;
597 static char * gResolve_Domain
= NULL
;
598 static int gResolve_TimeLimitSecs
= 0;
600 static CLIOption kResolveOpts
[] =
603 StringOption( 'n', "name", &gResolve_Name
, "service name", "Name of the service instance to resolve.", true ),
604 StringOption( 't', "type", &gResolve_Type
, "service type", "Type of the service instance to resolve.", true ),
605 StringOption( 'd', "domain", &gResolve_Domain
, "domain", "Domain of the service instance to resolve.", true ),
607 CLI_OPTION_GROUP( "Flags" ),
609 DNSSDFlagsOption_ForceMulticast(),
610 DNSSDFlagsOption_IncludeAWDL(),
611 DNSSDFlagsOption_ReturnIntermediates(),
612 DNSSDFlagsOption_WakeOnResolve(),
614 CLI_OPTION_GROUP( "Operation" ),
616 IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs
, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ),
622 //===========================================================================================================================
623 // Reconfirm Command Options
624 //===========================================================================================================================
626 static const char * gReconfirmRecord_Name
= NULL
;
627 static const char * gReconfirmRecord_Type
= NULL
;
628 static const char * gReconfirmRecord_Class
= NULL
;
629 static const char * gReconfirmRecord_Data
= NULL
;
631 static CLIOption kReconfirmOpts
[] =
634 StringOption( 'n', "name", &gReconfirmRecord_Name
, "record name", "Full name of the record to reconfirm.", true ),
635 StringOption( 't', "type", &gReconfirmRecord_Type
, "record type", "Type of the record to reconfirm.", true ),
636 StringOption( 'c', "class", &gReconfirmRecord_Class
, "record class", "Class of the record to reconfirm. Default class is IN.", false ),
637 StringOption( 'd', "data", &gReconfirmRecord_Data
, "record data", "The record data. See " kRecordDataSection_Name
" below.", false ),
639 CLI_OPTION_GROUP( "Flags" ),
646 //===========================================================================================================================
647 // getaddrinfo-POSIX Command Options
648 //===========================================================================================================================
650 static const char * gGAIPOSIX_HostName
= NULL
;
651 static const char * gGAIPOSIX_ServName
= NULL
;
652 static const char * gGAIPOSIX_Family
= NULL
;
653 static int gGAIPOSIXFlag_AddrConfig
= false;
654 static int gGAIPOSIXFlag_All
= false;
655 static int gGAIPOSIXFlag_CanonName
= false;
656 static int gGAIPOSIXFlag_NumericHost
= false;
657 static int gGAIPOSIXFlag_NumericServ
= false;
658 static int gGAIPOSIXFlag_Passive
= false;
659 static int gGAIPOSIXFlag_V4Mapped
= false;
660 #if( defined( AI_V4MAPPED_CFG ) )
661 static int gGAIPOSIXFlag_V4MappedCFG
= false;
663 #if( defined( AI_DEFAULT ) )
664 static int gGAIPOSIXFlag_Default
= false;
666 #if( defined( AI_UNUSABLE ) )
667 static int gGAIPOSIXFlag_Unusable
= false;
670 static CLIOption kGetAddrInfoPOSIXOpts
[] =
672 StringOption( 'n', "hostname", &gGAIPOSIX_HostName
, "hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ),
673 StringOption( 's', "servname", &gGAIPOSIX_ServName
, "servname", "Port number in decimal or service name from services(5).", false ),
675 CLI_OPTION_GROUP( "Hints " ),
676 StringOptionEx( 'f', "family", &gGAIPOSIX_Family
, "address family", "Address family to use for hints ai_family field.", false,
678 "Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n"
679 "address family is specified, then AF_UNSPEC is used.\n"
681 BooleanOption( 0 , "flag-addrconfig", &gGAIPOSIXFlag_AddrConfig
, "In hints ai_flags field, set AI_ADDRCONFIG." ),
682 BooleanOption( 0 , "flag-all", &gGAIPOSIXFlag_All
, "In hints ai_flags field, set AI_ALL." ),
683 BooleanOption( 0 , "flag-canonname", &gGAIPOSIXFlag_CanonName
, "In hints ai_flags field, set AI_CANONNAME." ),
684 BooleanOption( 0 , "flag-numerichost", &gGAIPOSIXFlag_NumericHost
, "In hints ai_flags field, set AI_NUMERICHOST." ),
685 BooleanOption( 0 , "flag-numericserv", &gGAIPOSIXFlag_NumericServ
, "In hints ai_flags field, set AI_NUMERICSERV." ),
686 BooleanOption( 0 , "flag-passive", &gGAIPOSIXFlag_Passive
, "In hints ai_flags field, set AI_PASSIVE." ),
687 BooleanOption( 0 , "flag-v4mapped", &gGAIPOSIXFlag_V4Mapped
, "In hints ai_flags field, set AI_V4MAPPED." ),
688 #if( defined( AI_V4MAPPED_CFG ) )
689 BooleanOption( 0 , "flag-v4mappedcfg", &gGAIPOSIXFlag_V4MappedCFG
, "In hints ai_flags field, set AI_V4MAPPED_CFG." ),
691 #if( defined( AI_DEFAULT ) )
692 BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default
, "In hints ai_flags field, set AI_DEFAULT." ),
694 #if( defined( AI_UNUSABLE ) )
695 BooleanOption( 0 , "flag-unusable", &gGAIPOSIXFlag_Unusable
, "In hints ai_flags field, set AI_UNUSABLE." ),
698 CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ),
702 //===========================================================================================================================
703 // ReverseLookup Command Options
704 //===========================================================================================================================
706 static const char * gReverseLookup_IPAddr
= NULL
;
707 static int gReverseLookup_OneShot
= false;
708 static int gReverseLookup_TimeLimitSecs
= 0;
710 static CLIOption kReverseLookupOpts
[] =
713 StringOption( 'a', "address", &gReverseLookup_IPAddr
, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ),
715 CLI_OPTION_GROUP( "Flags" ),
717 DNSSDFlagsOption_ForceMulticast(),
718 DNSSDFlagsOption_ReturnIntermediates(),
719 DNSSDFlagsOption_SuppressUnusable(),
721 CLI_OPTION_GROUP( "Operation" ),
723 BooleanOption( 'o', "oneshot", &gReverseLookup_OneShot
, "Finish after first set of results." ),
724 IntegerOption( 'l', "timeLimit", &gReverseLookup_TimeLimitSecs
, "seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ),
730 //===========================================================================================================================
731 // PortMapping Command Options
732 //===========================================================================================================================
734 static int gPortMapping_ProtocolTCP
= false;
735 static int gPortMapping_ProtocolUDP
= false;
736 static int gPortMapping_InternalPort
= 0;
737 static int gPortMapping_ExternalPort
= 0;
738 static int gPortMapping_TTL
= 0;
740 static CLIOption kPortMappingOpts
[] =
743 BooleanOption( 0, "tcp", &gPortMapping_ProtocolTCP
, "Use kDNSServiceProtocol_TCP." ),
744 BooleanOption( 0, "udp", &gPortMapping_ProtocolUDP
, "Use kDNSServiceProtocol_UDP." ),
745 IntegerOption( 0, "internalPort", &gPortMapping_InternalPort
, "port number", "Internal port.", false ),
746 IntegerOption( 0, "externalPort", &gPortMapping_ExternalPort
, "port number", "Requested external port. Use '0' for any external port.", false ),
747 IntegerOption( 0, "ttl", &gPortMapping_TTL
, "seconds", "Requested TTL (renewal period) in seconds. Use '0' for a default value.", false ),
749 CLI_OPTION_GROUP( "Flags" ),
752 CLI_OPTION_GROUP( "Operation" ),
759 //===========================================================================================================================
760 // BrowseAll Command Options
761 //===========================================================================================================================
763 static const char * gBrowseAll_Domain
= NULL
;
764 static char ** gBrowseAll_ServiceTypes
= NULL
;
765 static size_t gBrowseAll_ServiceTypesCount
= 0;
766 static int gBrowseAll_BrowseTimeSecs
= 5;
767 static int gBrowseAll_MaxConnectTimeSecs
= 0;
769 static CLIOption kBrowseAllOpts
[] =
772 StringOption( 'd', "domain", &gBrowseAll_Domain
, "domain", "Domain in which to browse for the service.", false ),
773 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 ),
775 CLI_OPTION_GROUP( "Flags" ),
776 DNSSDFlagsOption_IncludeAWDL(),
778 CLI_OPTION_GROUP( "Operation" ),
779 IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs
, "seconds", "Amount of time to spend browsing. (Default: 5 seconds)", false ),
780 IntegerOption( 'c', "maxConnectTime", &gBrowseAll_MaxConnectTimeSecs
, "seconds", "Max duration of connection attempts. If <= 0, then no connections are attempted. (Default: 0 seconds)", false ),
784 //===========================================================================================================================
785 // GetNameInfo Command Options
786 //===========================================================================================================================
788 static void GetNameInfoCmd( void );
790 static char * gGetNameInfo_IPAddress
= NULL
;
791 static int gGetNameInfoFlag_DGram
= false;
792 static int gGetNameInfoFlag_NameReqd
= false;
793 static int gGetNameInfoFlag_NoFQDN
= false;
794 static int gGetNameInfoFlag_NumericHost
= false;
795 static int gGetNameInfoFlag_NumericScope
= false;
796 static int gGetNameInfoFlag_NumericServ
= false;
798 static CLIOption kGetNameInfoOpts
[] =
800 StringOption( 'a', "address", &gGetNameInfo_IPAddress
, "IP address", "IPv4 or IPv6 address to use in sockaddr structure.", true ),
802 CLI_OPTION_GROUP( "Flags" ),
803 BooleanOption( 0 , "flag-dgram", &gGetNameInfoFlag_DGram
, "Use NI_DGRAM flag." ),
804 BooleanOption( 0 , "flag-namereqd", &gGetNameInfoFlag_NameReqd
, "Use NI_NAMEREQD flag." ),
805 BooleanOption( 0 , "flag-nofqdn", &gGetNameInfoFlag_NoFQDN
, "Use NI_NOFQDN flag." ),
806 BooleanOption( 0 , "flag-numerichost", &gGetNameInfoFlag_NumericHost
, "Use NI_NUMERICHOST flag." ),
807 BooleanOption( 0 , "flag-numericscope", &gGetNameInfoFlag_NumericScope
, "Use NI_NUMERICSCOPE flag." ),
808 BooleanOption( 0 , "flag-numericserv", &gGetNameInfoFlag_NumericServ
, "Use NI_NUMERICSERV flag." ),
810 CLI_SECTION( "Notes", "See getnameinfo(3) man page for more details.\n" ),
814 //===========================================================================================================================
815 // GetAddrInfoStress Command Options
816 //===========================================================================================================================
818 static int gGAIStress_TestDurationSecs
= 0;
819 static int gGAIStress_ConnectionCount
= 0;
820 static int gGAIStress_DurationMinMs
= 0;
821 static int gGAIStress_DurationMaxMs
= 0;
822 static int gGAIStress_RequestCountMax
= 0;
824 static CLIOption kGetAddrInfoStressOpts
[] =
828 CLI_OPTION_GROUP( "Flags" ),
829 DNSSDFlagsOption_ReturnIntermediates(),
830 DNSSDFlagsOption_SuppressUnusable(),
832 CLI_OPTION_GROUP( "Operation" ),
833 IntegerOption( 0, "testDuration", &gGAIStress_TestDurationSecs
, "seconds", "Stress test duration in seconds. Use '0' for forever.", false ),
834 IntegerOption( 0, "connectionCount", &gGAIStress_ConnectionCount
, "integer", "Number of simultaneous DNS-SD connections.", true ),
835 IntegerOption( 0, "requestDurationMin", &gGAIStress_DurationMinMs
, "ms", "Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
836 IntegerOption( 0, "requestDurationMax", &gGAIStress_DurationMaxMs
, "ms", "Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
837 IntegerOption( 0, "consecutiveRequestMax", &gGAIStress_RequestCountMax
, "integer", "Maximum number of requests on a connection before restarting it.", true ),
841 //===========================================================================================================================
842 // DNSQuery Command Options
843 //===========================================================================================================================
845 static char * gDNSQuery_Name
= NULL
;
846 static char * gDNSQuery_Type
= "A";
847 static char * gDNSQuery_Server
= NULL
;
848 static int gDNSQuery_TimeLimitSecs
= 5;
849 static int gDNSQuery_UseTCP
= false;
850 static int gDNSQuery_Flags
= kDNSHeaderFlag_RecursionDesired
;
851 static int gDNSQuery_RawRData
= false;
852 static int gDNSQuery_Verbose
= false;
854 #if( TARGET_OS_DARWIN )
855 #define kDNSQueryServerOptionIsRequired false
857 #define kDNSQueryServerOptionIsRequired true
860 static CLIOption kDNSQueryOpts
[] =
862 StringOption( 'n', "name", &gDNSQuery_Name
, "name", "Question name (QNAME) to put in DNS query message.", true ),
863 StringOption( 't', "type", &gDNSQuery_Type
, "type", "Question type (QTYPE) to put in DNS query message. Default value is 'A'.", false ),
864 StringOption( 's', "server", &gDNSQuery_Server
, "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired
),
865 IntegerOption( 'l', "timeLimit", &gDNSQuery_TimeLimitSecs
, "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ),
866 BooleanOption( 0 , "tcp", &gDNSQuery_UseTCP
, "Send the DNS query via TCP instead of UDP." ),
867 IntegerOption( 'f', "flags", &gDNSQuery_Flags
, "flags", "16-bit value for DNS header flags/codes field. Default value is 0x0100 (Recursion Desired).", false ),
868 BooleanOption( 0 , "raw", &gDNSQuery_RawRData
, "Present record data as a hexdump." ),
869 BooleanOption( 'v', "verbose", &gDNSQuery_Verbose
, "Prints the DNS message to be sent to the server." ),
873 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
874 //===========================================================================================================================
875 // DNSCrypt Command Options
876 //===========================================================================================================================
878 static char * gDNSCrypt_ProviderName
= NULL
;
879 static char * gDNSCrypt_ProviderKey
= NULL
;
880 static char * gDNSCrypt_Name
= NULL
;
881 static char * gDNSCrypt_Type
= NULL
;
882 static char * gDNSCrypt_Server
= NULL
;
883 static int gDNSCrypt_TimeLimitSecs
= 5;
884 static int gDNSCrypt_RawRData
= false;
885 static int gDNSCrypt_Verbose
= false;
887 static CLIOption kDNSCryptOpts
[] =
889 StringOption( 'p', "providerName", &gDNSCrypt_ProviderName
, "name", "The DNSCrypt provider name.", true ),
890 StringOption( 'k', "providerKey", &gDNSCrypt_ProviderKey
, "hex string", "The DNSCrypt provider's public signing key.", true ),
891 StringOption( 'n', "name", &gDNSCrypt_Name
, "name", "Question name (QNAME) to put in DNS query message.", true ),
892 StringOption( 't', "type", &gDNSCrypt_Type
, "type", "Question type (QTYPE) to put in DNS query message.", true ),
893 StringOption( 's', "server", &gDNSCrypt_Server
, "IP address", "DNS server's IPv4 or IPv6 address.", true ),
894 IntegerOption( 'l', "timeLimit", &gDNSCrypt_TimeLimitSecs
, "seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ),
895 BooleanOption( 0 , "raw", &gDNSCrypt_RawRData
, "Present record data as a hexdump." ),
896 BooleanOption( 'v', "verbose", &gDNSCrypt_Verbose
, "Prints the DNS message to be sent to the server." ),
901 //===========================================================================================================================
902 // MDNSQuery Command Options
903 //===========================================================================================================================
905 static char * gMDNSQuery_Name
= NULL
;
906 static char * gMDNSQuery_Type
= NULL
;
907 static int gMDNSQuery_SourcePort
= 0;
908 static int gMDNSQuery_IsQU
= false;
909 static int gMDNSQuery_RawRData
= false;
910 static int gMDNSQuery_UseIPv4
= false;
911 static int gMDNSQuery_UseIPv6
= false;
912 static int gMDNSQuery_AllResponses
= false;
913 static int gMDNSQuery_ReceiveSecs
= 1;
915 static CLIOption kMDNSQueryOpts
[] =
917 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
918 StringOption( 'n', "name", &gMDNSQuery_Name
, "name", "Question name (QNAME) to put in mDNS message.", true ),
919 StringOption( 't', "type", &gMDNSQuery_Type
, "type", "Question type (QTYPE) to put in mDNS message.", true ),
920 IntegerOption( 'p', "sourcePort", &gMDNSQuery_SourcePort
, "port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ),
921 BooleanOption( 'u', "QU", &gMDNSQuery_IsQU
, "Set the unicast-response bit, i.e., send a QU question." ),
922 BooleanOption( 0 , "raw", &gMDNSQuery_RawRData
, "Present record data as a hexdump." ),
923 BooleanOption( 0 , "ipv4", &gMDNSQuery_UseIPv4
, "Use IPv4." ),
924 BooleanOption( 0 , "ipv6", &gMDNSQuery_UseIPv6
, "Use IPv6." ),
925 BooleanOption( 'a', "allResponses", &gMDNSQuery_AllResponses
, "Print all received mDNS messages, not just those containing answers." ),
926 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 ),
930 //===========================================================================================================================
931 // PIDToUUID Command Options
932 //===========================================================================================================================
934 static int gPIDToUUID_PID
= 0;
936 static CLIOption kPIDToUUIDOpts
[] =
938 IntegerOption( 'p', "pid", &gPIDToUUID_PID
, "PID", "Process ID.", true ),
942 //===========================================================================================================================
943 // DNSServer Command Options
944 //===========================================================================================================================
946 #define kDNSServerInfoText_Intro \
947 "The DNS server answers certain queries in the d.test. domain. Responses are dynamically generated based on the\n" \
948 "presence of special labels in the query's QNAME. There are currently seven types of special labels that can be\n" \
949 "used to generate specific responses: Alias labels, Alias-TTL labels, Count labels, Tag labels, TTL labels, the\n" \
950 "IPv4 label, and the IPv6 label.\n"
952 #define kDNSServerInfoText_NameExistence \
953 "A name is considered to exist if and only if it ends in d.test., and the other labels, if\n" \
954 "any, consist of\n" \
956 " 1. at most one Alias or Alias-TTL label as the first label;\n" \
957 " 2. at most one Count label;\n" \
958 " 3. zero or more Tag labels;\n" \
959 " 4. at most one TTL label; and\n" \
960 " 5. at most one IPv4 or IPv6 label.\n"
962 #define kDNSServerInfoText_ResourceRecords \
963 "Currently, the server only provides CNAME, A, and AAAA records.\n" \
965 "Names that exist and begin with an Alias or Alias-TTL label are aliases of canonical names, i.e., they're the\n" \
966 "names of CNAME records. See \"Alias Labels\" and \"Alias-TTL Labels\" for details.\n" \
968 "Names that exist and have an IPv4 label have at least one A record, but no AAAA records. Names that exist and\n" \
969 "have an IPv6 label, have at least one AAAA record, but no A records. All other names that exist have at least\n" \
970 "one A record and at least one AAAA record. See \"Count Labels\" for how the number of address records for a\n" \
971 "given name is determined.\n" \
973 "A records contain IPv4 addresses in the 203.0.113.0/24 block, while AAAA records contain IPv6 addresses in the\n" \
974 "2001:db8::/32 block. Both of these address blocks are reserved for documentation.\n" \
975 "See <https://tools.ietf.org/html/rfc5737> and <https://tools.ietf.org/html/rfc3849>.\n" \
977 "Unless otherwise specified, all resource records will use a default TTL. The default TTL can be set with the\n" \
978 "--defaultTTL option. See \"Alias-TTL Labels\" and \"TTL Labels\" for details on how to query for records with\n" \
979 "specific TTL values.\n"
981 #define kDNSServerInfoText_AliasLabel \
982 "Alias labels are of the form \"alias\" or \"alias-N\", where N is an integer in [2 .. 2^31 - 1].\n" \
984 "If QNAME exist and its first label is Alias label \"alias-N\", then the response will contain exactly N CNAME\n" \
987 " 1. For each i in [3 .. N], the response will contain a CNAME record whose name is identical to QNAME,\n" \
988 " except that the first label is \"alias-i\" instead, and whose RDATA is the name of the other CNAME\n" \
989 " record whose name has \"alias-(i - 1)\" as its first label.\n" \
991 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
992 " is \"alias-2\" instead, and whose RDATA is the name identical to QNAME, except that the first label is\n" \
993 " \"alias\" instead.\n" \
995 " 3. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
996 " is \"alias\" instead, and whose RDATA is the name identical to QNAME stripped of its first label.\n" \
998 "If QNAME exist and its first label is Alias label \"alias\", then the response will contain a single CNAME\n" \
999 "record. The CNAME record's name will be equal to QNAME and its RDATA will be the name identical to QNAME\n" \
1000 "stripped of its first label.\n" \
1002 "Example. A response to a query with a QNAME of alias-3.count-5.d.test will contain the following CNAME\n" \
1005 " alias-4.count-5.d.test. 60 IN CNAME alias-3.count-5.d.test.\n" \
1006 " alias-3.count-5.d.test. 60 IN CNAME alias-2.count-5.d.test.\n" \
1007 " alias-2.count-5.d.test. 60 IN CNAME alias.count-5.d.test.\n" \
1008 " alias.count-5.d.test. 60 IN CNAME count-5.d.test.\n"
1010 #define kDNSServerInfoText_AliasTTLLabel \
1011 "Alias-TTL labels are of the form \"alias-ttl-T_1[-T_2[...-T_N]]\", where each T_i is an integer in\n" \
1012 "[0 .. 2^31 - 1] and N is a positive integer bounded by the size of the maximum legal label length (63 octets).\n" \
1014 "If QNAME exists and its first label is Alias-TTL label \"alias-ttl-T_1...-T_N\", then the response will contain\n" \
1015 "exactly N CNAME records:\n" \
1017 " 1. For each i in [1 .. N - 1], the response will contain a CNAME record whose name is identical to QNAME,\n" \
1018 " except that the first label is \"alias-ttl-T_i...-T_N\" instead, whose TTL value is T_i, and whose RDATA\n" \
1019 " is the name of the other CNAME record whose name has \"alias-ttl-T_(i+1)...-T_N\" as its first label.\n" \
1021 " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \
1022 " is \"alias-ttl-T_N\", whose TTL is T_N, and whose RDATA is identical to QNAME stripped of its first\n" \
1025 "Example. A response to a query with a QNAME of alias-ttl-20-40-80.count-5.d.test will contain the following\n" \
1026 "CNAME records:\n" \
1028 " alias-ttl-20-40-80.count-5.d.test. 20 IN CNAME alias-ttl-40-80.count-5.d.test.\n" \
1029 " alias-ttl-40-80.count-5.d.test. 40 IN CNAME alias-ttl-80.count-5.d.test.\n" \
1030 " alias-ttl-80.count-5.d.test. 80 IN CNAME count-5.d.test.\n"
1032 #define kDNSServerInfoText_CountLabel \
1033 "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\n" \
1034 "is an integer in [N_1 .. 255].\n" \
1036 "If QNAME exists, contains Count label \"count-N\", and has the type of address records specified by QTYPE, then\n" \
1037 "the response will contain exactly N address records:\n" \
1039 " 1. For i in [1 .. N], the response will contain an address record of type QTYPE whose name is equal to\n" \
1040 " QNAME and whose RDATA is an address equal to a constant base address + i.\n" \
1042 " 2. The address records will be ordered by the address contained in RDATA in ascending order.\n" \
1044 "Example. A response to an A record query with a QNAME of alias.count-3.d.test will contain the following A\n" \
1047 " count-3.d.test. 60 IN A 203.0.113.1\n" \
1048 " count-3.d.test. 60 IN A 203.0.113.2\n" \
1049 " count-3.d.test. 60 IN A 203.0.113.3\n" \
1051 "If QNAME exists, contains Count label \"count-N_1-N_2\", and has the type of address records specified by\n" \
1052 "QTYPE, then the response will contain exactly N_1 address records:\n" \
1054 " 1. Each of the address records will be of type QTYPE, have name equal to QNAME, and have as its RDATA a\n" \
1055 " unique address equal to a constant base address + i, where i is a randomly chosen integer in [1 .. N_2].\n" \
1057 " 2. The order of the address records will be random.\n" \
1059 "Example. A response to a AAAA record query with a QNAME of count-3-100.ttl-20.d.test could contain the\n" \
1060 "following AAAA records:\n" \
1062 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8::c\n" \
1063 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8::3a\n" \
1064 " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8::4f\n" \
1066 "If QNAME exists, but doesn't have the type of address records specified by QTYPE, then the response will\n" \
1067 "contain no address records, regardless of whether it contains a Count label.\n" \
1069 "QNAMEs that exist, but don't have a Count label are treated as though they contain a count label equal to\n" \
1072 #define kDNSServerInfoText_TagLabel \
1073 "Tag labels are labels prefixed with \"tag-\" and contain zero or more arbitrary octets after the prefix.\n" \
1075 "This type of label exists to allow testers to \"uniquify\" domain names. Tag labels can also serve as padding\n" \
1076 "to increase the sizes of domain names.\n"
1078 #define kDNSServerInfoText_TTLLabel \
1079 "TTL labels are of the form \"ttl-T\", where T is an integer in [0 .. 2^31 - 1].\n" \
1081 "If the name specified by QNAME exists, and contains TTL label \"ttl-T\", then all non-CNAME records contained\n" \
1082 "in the response will have a TTL value equal to T.\n"
1084 #define kDNSServerInfoText_IPv4Label \
1085 "The IPv4 label is \"ipv4\". See \"Resource Records\" for the affect of this label.\n"
1087 #define kDNSServerInfoText_IPv6Label \
1088 "The IPv6 label is \"ipv6\". See \"Resource Records\" for the affect of this label.\n"
1090 #define kDNSServerDefaultTTL 60
1092 static int gDNSServer_LoopbackOnly
= false;
1093 static int gDNSServer_Foreground
= false;
1094 static int gDNSServer_ResponseDelayMs
= 0;
1095 static int gDNSServer_DefaultTTL
= kDNSServerDefaultTTL
;
1096 #if( TARGET_OS_DARWIN )
1097 static const char * gDNSServer_FollowPID
= NULL
;
1100 static CLIOption kDNSServerOpts
[] =
1102 BooleanOption( 'l', "loopback", &gDNSServer_LoopbackOnly
, "Bind to to the loopback interface." ),
1103 BooleanOption( 'f', "foreground", &gDNSServer_Foreground
, "Directlog output to stdout instead of system logging." ),
1104 IntegerOption( 'd', "responseDelay", &gDNSServer_ResponseDelayMs
, "ms", "The amount of additional delay in milliseconds to apply to responses. (default: 0)", false ),
1105 IntegerOption( 0 , "defaultTTL", &gDNSServer_DefaultTTL
, "seconds", "Resource record TTL value to use when unspecified. (default: 60)", false ),
1106 #if( TARGET_OS_DARWIN )
1107 StringOption( 0 , "followPID", &gDNSServer_FollowPID
, "pid", "Exit when the process (usually the parent proccess) specified by PID exits.", false ),
1110 CLI_SECTION( "Intro", kDNSServerInfoText_Intro
),
1111 CLI_SECTION( "Name Existence", kDNSServerInfoText_NameExistence
),
1112 CLI_SECTION( "Resource Records", kDNSServerInfoText_ResourceRecords
),
1113 CLI_SECTION( "Alias Labels", kDNSServerInfoText_AliasLabel
),
1114 CLI_SECTION( "Alias-TTL Labels", kDNSServerInfoText_AliasTTLLabel
),
1115 CLI_SECTION( "Count Labels", kDNSServerInfoText_CountLabel
),
1116 CLI_SECTION( "Tag Labels", kDNSServerInfoText_TagLabel
),
1117 CLI_SECTION( "TTL Labels", kDNSServerInfoText_TTLLabel
),
1118 CLI_SECTION( "IPv4 Label", kDNSServerInfoText_IPv4Label
),
1119 CLI_SECTION( "IPv6 Label", kDNSServerInfoText_IPv6Label
),
1123 static void DNSServerCmd( void );
1125 //===========================================================================================================================
1126 // Test Command Options
1127 //===========================================================================================================================
1129 static const char * gGAIPerf_TestSuite
= NULL
;
1130 static int gGAIPerf_CallDelayMs
= 10;
1131 static int gGAIPerf_ServerDelayMs
= 10;
1132 static int gGAIPerf_DefaultIterCount
= 100;
1133 static const char * gGAIPerf_OutputFilePath
= NULL
;
1134 static const char * gGAIPerf_OutputFormat
= "json";
1135 static int gGAIPerf_OutputAppendNewLine
= false;
1137 static void GAIPerfCmd( void );
1139 #define kGAIPerfSectionTitle_TestSuiteBasic "Test Suite \"Basic\""
1140 #define kGAIPerfSectionText_TestSuiteBasic \
1141 "This test suite consists of the following three test cases:\n" \
1143 "Test Case #1: Resolve a domain name with\n" \
1145 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1147 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1148 "server queries.\n" \
1150 "Test Case #2: Resolve a domain name with\n" \
1152 " 2 CNAME records, 4 A records, and 4 AAAA records\n" \
1154 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1155 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1156 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1158 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1159 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1160 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1162 "Test Case #3: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1164 #define kGAIPerfSectionTitle_TestSuiteAdvanced "Test Suite \"Advanced\""
1165 #define kGAIPerfSectionText_TestSuiteAdvanced \
1166 "This test suite consists of 33 test cases. Test cases 1 through 32 can be described in the following way\n" \
1168 "Test Case #N (where N is in [1, 32] and odd): Resolve a domain name with\n" \
1170 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1172 "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \
1173 "server queries.\n" \
1175 "Test Case #N (where N is in [1, 32] and even): Resolve a domain name with\n" \
1177 " N_c CNAME records, N_a A records, and N_a AAAA records\n" \
1179 "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \
1180 "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \
1181 "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \
1183 "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \
1184 "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \
1185 "domain name in the preliminary iteration isn't counted in the performance stats.\n" \
1187 "N_c and N_a take on the following values, depending on the value of N:\n" \
1189 " N_c is 0 if N is in [1, 8].\n" \
1190 " N_c is 1 if N is in [9, 16].\n" \
1191 " N_c is 2 if N is in [17, 24].\n" \
1192 " N_c is 4 if N is in [25, 32].\n" \
1194 " N_a is 1 if N mod 8 is 1 or 2.\n" \
1195 " N_a is 2 if N mod 8 is 3 or 4.\n" \
1196 " N_a is 4 if N mod 8 is 5 or 6.\n" \
1197 " N_a is 8 if N mod 8 is 7 or 0.\n" \
1201 "Test Case #33: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1203 static CLIOption kGAIPerfOpts
[] =
1205 StringOptionEx( 's', "suite", &gGAIPerf_TestSuite
, "name", "Name of the predefined test suite to run.", true,
1207 "There are currently two predefined test suites, 'basic' and 'advanced', which are described below.\n"
1210 StringOption( 'o', "output", &gGAIPerf_OutputFilePath
, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1211 StringOptionEx( 'f', "format", &gGAIPerf_OutputFormat
, "format", "Specifies the test results output format. (default: json)", false,
1213 "Use 'json' for JavaScript Object Notation (JSON).\n"
1214 "Use 'xml' for property list XML version 1.0.\n"
1215 "Use 'binary' for property list binary version 1.0.\n"
1218 BooleanOption( 'n', "appendNewline", &gGAIPerf_OutputAppendNewLine
, "If the output format is JSON, output a trailing newline character." ),
1219 IntegerOption( 0 , "callDelay", &gGAIPerf_CallDelayMs
, "ms", "Time to wait before calling DNSServiceGetAddrInfo() in milliseconds. (default: 10)", false ),
1220 IntegerOption( 0 , "responseDelay", &gGAIPerf_ServerDelayMs
, "ms", "Additional delay in milliseconds to have the test DNS server apply to responses. (default: 0)", false ),
1221 IntegerOption( 'i', "iterations", &gGAIPerf_DefaultIterCount
, "count", "The default number of test case iterations. (default: 100)", false ),
1223 CLI_SECTION( kGAIPerfSectionTitle_TestSuiteBasic
, kGAIPerfSectionText_TestSuiteBasic
),
1224 CLI_SECTION( kGAIPerfSectionTitle_TestSuiteAdvanced
, kGAIPerfSectionText_TestSuiteAdvanced
),
1228 static CLIOption kTestOpts
[] =
1230 Command( "gaiperf", GAIPerfCmd
, kGAIPerfOpts
, "Run DNSServiceGetAddrInfo() performance tests.", false ),
1234 //===========================================================================================================================
1235 // SSDP Command Options
1236 //===========================================================================================================================
1238 static int gSSDPDiscover_MX
= 1;
1239 static const char * gSSDPDiscover_ST
= "ssdp:all";
1240 static int gSSDPDiscover_ReceiveSecs
= 1;
1241 static int gSSDPDiscover_UseIPv4
= false;
1242 static int gSSDPDiscover_UseIPv6
= false;
1243 static int gSSDPDiscover_Verbose
= false;
1245 static CLIOption kSSDPDiscoverOpts
[] =
1247 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
1248 IntegerOption( 'm', "mx", &gSSDPDiscover_MX
, "seconds", "MX value in search request, i.e., max response delay in seconds. (Default: 1 second)", false ),
1249 StringOption( 's', "st", &gSSDPDiscover_ST
, "string", "ST value in search request, i.e., the search target. (Default: \"ssdp:all\")", false ),
1250 IntegerOption( 'r', "receiveTime", &gSSDPDiscover_ReceiveSecs
, "seconds", "Amount of time to spend receiving responses. -1 means unlimited. (Default: 1 second)", false ),
1251 BooleanOption( 0 , "ipv4", &gSSDPDiscover_UseIPv4
, "Use IPv4, i.e., multicast to 239.255.255.250:1900." ),
1252 BooleanOption( 0 , "ipv6", &gSSDPDiscover_UseIPv6
, "Use IPv6, i.e., multicast to [ff02::c]:1900" ),
1253 BooleanOption( 'v', "verbose", &gSSDPDiscover_Verbose
, "Prints the search request(s) that were sent." ),
1257 static void SSDPDiscoverCmd( void );
1259 static CLIOption kSSDPOpts
[] =
1261 Command( "discover", SSDPDiscoverCmd
, kSSDPDiscoverOpts
, "Crafts and multicasts an SSDP search message.", false ),
1265 #if( TARGET_OS_DARWIN )
1266 //===========================================================================================================================
1267 // res_query Command Options
1268 //===========================================================================================================================
1270 static void ResQueryCmd( void );
1272 static const char * gResQuery_Name
= NULL
;
1273 static const char * gResQuery_Type
= NULL
;
1274 static const char * gResQuery_Class
= NULL
;
1275 static int gResQuery_UseLibInfo
= false;
1277 static CLIOption kResQueryOpts
[] =
1279 StringOption( 'n', "name", &gResQuery_Name
, "domain name", "Full domain name of record to query.", true ),
1280 StringOption( 't', "type", &gResQuery_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1281 StringOption( 'c', "class", &gResQuery_Class
, "record class", "Record class by name or number. Default class is IN.", false ),
1282 BooleanOption( 0 , "libinfo", &gResQuery_UseLibInfo
, "Use res_query from libinfo instead of libresolv." ),
1286 //===========================================================================================================================
1287 // dns_query Command Options
1288 //===========================================================================================================================
1290 static void ResolvDNSQueryCmd( void );
1292 static const char * gResolvDNSQuery_Name
= NULL
;
1293 static const char * gResolvDNSQuery_Type
= NULL
;
1294 static const char * gResolvDNSQuery_Class
= NULL
;
1295 static const char * gResolvDNSQuery_Path
= NULL
;
1297 static CLIOption kResolvDNSQueryOpts
[] =
1299 StringOption( 'n', "name", &gResolvDNSQuery_Name
, "domain name", "Full domain name of record to query.", true ),
1300 StringOption( 't', "type", &gResolvDNSQuery_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
1301 StringOption( 'c', "class", &gResolvDNSQuery_Class
, "record class", "Record class by name or number. Default class is IN.", false ),
1302 StringOption( 'p', "path", &gResolvDNSQuery_Path
, "file path", "The path argument to pass to dns_open() before calling dns_query(). Default value is NULL.", false ),
1306 //===========================================================================================================================
1307 // CFHost Command Options
1308 //===========================================================================================================================
1310 static void CFHostCmd( void );
1312 static const char * gCFHost_Name
= NULL
;
1313 static int gCFHost_WaitSecs
= 0;
1315 static CLIOption kCFHostOpts
[] =
1317 StringOption( 'n', "name", &gCFHost_Name
, "hostname", "Hostname to resolve.", true ),
1318 IntegerOption( 'w', "wait", &gCFHost_WaitSecs
, "seconds", "Time in seconds to wait before a normal exit. (default: 0)", false ),
1322 static CLIOption kLegacyOpts
[] =
1324 Command( "res_query", ResQueryCmd
, kResQueryOpts
, "Uses res_query() from either libresolv or libinfo to query for a record.", true ),
1325 Command( "dns_query", ResolvDNSQueryCmd
, kResolvDNSQueryOpts
, "Uses dns_query() from libresolv to query for a record.", true ),
1326 Command( "cfhost", CFHostCmd
, kCFHostOpts
, "Uses CFHost to resolve a hostname.", true ),
1330 //===========================================================================================================================
1331 // DNSConfigAdd Command Options
1332 //===========================================================================================================================
1334 static void DNSConfigAddCmd( void );
1336 static CFStringRef gDNSConfigAdd_ID
= NULL
;
1337 static char ** gDNSConfigAdd_IPAddrArray
= NULL
;
1338 static size_t gDNSConfigAdd_IPAddrCount
= 0;
1339 static char ** gDNSConfigAdd_DomainArray
= NULL
;
1340 static size_t gDNSConfigAdd_DomainCount
= 0;
1341 static const char * gDNSConfigAdd_Interface
= NULL
;
1343 static CLIOption kDNSConfigAddOpts
[] =
1345 CFStringOption( 0 , "id", &gDNSConfigAdd_ID
, "ID", "Arbitrary ID to use for resolver entry.", true ),
1346 MultiStringOption( 'a', "address", &gDNSConfigAdd_IPAddrArray
, &gDNSConfigAdd_IPAddrCount
, "IP address", "DNS server IP address(es). Can be specified more than once.", true ),
1347 MultiStringOption( 'd', "domain", &gDNSConfigAdd_DomainArray
, &gDNSConfigAdd_DomainCount
, "domain", "Specific domain(s) for the resolver entry. Can be specified more than once.", false ),
1348 StringOption( 'i', "interface", &gDNSConfigAdd_Interface
, "interface name", "Specific interface for the resolver entry.", false ),
1350 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1354 //===========================================================================================================================
1355 // DNSConfigRemove Command Options
1356 //===========================================================================================================================
1358 static void DNSConfigRemoveCmd( void );
1360 static CFStringRef gDNSConfigRemove_ID
= NULL
;
1362 static CLIOption kDNSConfigRemoveOpts
[] =
1364 CFStringOption( 0, "id", &gDNSConfigRemove_ID
, "ID", "ID of resolver entry to remove.", true ),
1366 CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
1370 static CLIOption kDNSConfigOpts
[] =
1372 Command( "add", DNSConfigAddCmd
, kDNSConfigAddOpts
, "Add a supplemental resolver entry to the system's DNS configuration.", true ),
1373 Command( "remove", DNSConfigRemoveCmd
, kDNSConfigRemoveOpts
, "Remove a supplemental resolver entry from the system's DNS configuration.", true ),
1376 #endif // TARGET_OS_DARWIN
1378 //===========================================================================================================================
1380 //===========================================================================================================================
1382 static OSStatus
VersionOptionCallback( CLIOption
*inOption
, const char *inArg
, int inUnset
);
1384 static void BrowseCmd( void );
1385 static void GetAddrInfoCmd( void );
1386 static void QueryRecordCmd( void );
1387 static void RegisterCmd( void );
1388 static void RegisterRecordCmd( void );
1389 static void ResolveCmd( void );
1390 static void ReconfirmCmd( void );
1391 static void GetAddrInfoPOSIXCmd( void );
1392 static void ReverseLookupCmd( void );
1393 static void PortMappingCmd( void );
1394 static void BrowseAllCmd( void );
1395 static void GetAddrInfoStressCmd( void );
1396 static void DNSQueryCmd( void );
1397 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
1398 static void DNSCryptCmd( void );
1400 static void MDNSQueryCmd( void );
1401 static void PIDToUUIDCmd( void );
1402 static void DaemonVersionCmd( void );
1404 static CLIOption kGlobalOpts
[] =
1406 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback
, NULL
, NULL
,
1407 kCLIOptionFlags_NoArgument
| kCLIOptionFlags_GlobalOnly
, "Displays the version of this tool.", NULL
),
1412 Command( "browse", BrowseCmd
, kBrowseOpts
, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
1413 Command( "getAddrInfo", GetAddrInfoCmd
, kGetAddrInfoOpts
, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
1414 Command( "queryRecord", QueryRecordCmd
, kQueryRecordOpts
, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
1415 Command( "register", RegisterCmd
, kRegisterOpts
, "Uses DNSServiceRegister() to register a service.", false ),
1416 Command( "registerRecord", RegisterRecordCmd
, kRegisterRecordOpts
, "Uses DNSServiceRegisterRecord() to register a record.", false ),
1417 Command( "resolve", ResolveCmd
, kResolveOpts
, "Uses DNSServiceResolve() to resolve a service.", false ),
1418 Command( "reconfirm", ReconfirmCmd
, kReconfirmOpts
, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
1419 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd
, kGetAddrInfoPOSIXOpts
, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
1420 Command( "reverseLookup", ReverseLookupCmd
, kReverseLookupOpts
, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
1421 Command( "portMapping", PortMappingCmd
, kPortMappingOpts
, "Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ),
1422 Command( "browseAll", BrowseAllCmd
, kBrowseAllOpts
, "Browse and resolve all (or specific) services and, optionally, attempt connections.", false ),
1424 // Uncommon commands.
1426 Command( "getnameinfo", GetNameInfoCmd
, kGetNameInfoOpts
, "Calls getnameinfo() and prints results.", true ),
1427 Command( "getAddrInfoStress", GetAddrInfoStressCmd
, kGetAddrInfoStressOpts
, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
1428 Command( "DNSQuery", DNSQueryCmd
, kDNSQueryOpts
, "Crafts and sends a DNS query.", true ),
1429 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
1430 Command( "DNSCrypt", DNSCryptCmd
, kDNSCryptOpts
, "Crafts and sends a DNSCrypt query.", true ),
1432 Command( "mDNSQuery", MDNSQueryCmd
, kMDNSQueryOpts
, "Crafts and sends an mDNS query over the specified interface.", true ),
1433 Command( "pid2uuid", PIDToUUIDCmd
, kPIDToUUIDOpts
, "Prints the UUID of a process.", true ),
1434 Command( "server", DNSServerCmd
, kDNSServerOpts
, "DNS server for testing.", true ),
1435 Command( "test", NULL
, kTestOpts
, "Commands for testing DNS-SD.", true ),
1436 Command( "ssdp", NULL
, kSSDPOpts
, "Commands for testing Simple Service Discovery Protocol (SSDP).", true ),
1437 #if( TARGET_OS_DARWIN )
1438 Command( "legacy", NULL
, kLegacyOpts
, "Commands for legacy non-DNS-SD API.", true ),
1439 Command( "dnsconfig", NULL
, kDNSConfigOpts
, "Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ),
1441 Command( "daemonVersion", DaemonVersionCmd
, NULL
, "Prints the version of the DNS-SD daemon.", true ),
1447 //===========================================================================================================================
1448 // Helper Prototypes
1449 //===========================================================================================================================
1451 #define kExitReason_OneShotDone "one-shot done"
1452 #define kExitReason_ReceivedResponse "received response"
1453 #define kExitReason_SIGINT "interrupt signal"
1454 #define kExitReason_Timeout "timeout"
1455 #define kExitReason_TimeLimit "time limit"
1457 static void Exit( void *inContext
) ATTRIBUTE_NORETURN
;
1460 PrintFTimestampHandler(
1461 PrintFContext
* inContext
,
1462 PrintFFormat
* inFormat
,
1463 PrintFVAList
* inArgs
,
1464 void * inUserContext
);
1466 PrintFDNSMessageHandler(
1467 PrintFContext
* inContext
,
1468 PrintFFormat
* inFormat
,
1469 PrintFVAList
* inArgs
,
1470 void * inUserContext
);
1472 static DNSServiceFlags
GetDNSSDFlagsFromOpts( void );
1476 kConnectionType_None
= 0,
1477 kConnectionType_Normal
= 1,
1478 kConnectionType_DelegatePID
= 2,
1479 kConnectionType_DelegateUUID
= 3
1485 ConnectionType type
;
1496 CreateConnectionFromArgString(
1497 const char * inString
,
1498 dispatch_queue_t inQueue
,
1499 DNSServiceRef
* outSDRef
,
1500 ConnectionDesc
* outDesc
);
1501 static OSStatus
InterfaceIndexFromArgString( const char *inString
, uint32_t *outIndex
);
1502 static OSStatus
RecordDataFromArgString( const char *inString
, uint8_t **outDataPtr
, size_t *outDataLen
);
1503 static OSStatus
RecordTypeFromArgString( const char *inString
, uint16_t *outValue
);
1504 static OSStatus
RecordClassFromArgString( const char *inString
, uint16_t *outValue
);
1506 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
1508 static char * InterfaceIndexToName( uint32_t inIfIndex
, char inNameBuf
[ kInterfaceNameBufLen
] );
1509 static const char * RecordTypeToString( unsigned int inValue
);
1512 DNSMessageExtractDomainName(
1513 const uint8_t * inMsgPtr
,
1515 const uint8_t * inNamePtr
,
1516 uint8_t inBuf
[ kDomainNameLengthMax
],
1517 const uint8_t ** outNextPtr
);
1519 DNSMessageExtractDomainNameString(
1520 const void * inMsgPtr
,
1522 const void * inNamePtr
,
1523 char inBuf
[ kDNSServiceMaxDomainName
],
1524 const uint8_t ** outNextPtr
);
1526 DNSMessageExtractRecord(
1527 const uint8_t * inMsgPtr
,
1529 const uint8_t * inPtr
,
1530 uint8_t inNameBuf
[ kDomainNameLengthMax
],
1532 uint16_t * outClass
,
1534 const uint8_t ** outRDataPtr
,
1535 size_t * outRDataLen
,
1536 const uint8_t ** outPtr
);
1537 static OSStatus
DNSMessageGetAnswerSection( const uint8_t *inMsgPtr
, size_t inMsgLen
, const uint8_t **outPtr
);
1539 DNSRecordDataToString(
1540 const void * inRDataPtr
,
1542 unsigned int inRDataType
,
1543 const void * inMsgPtr
,
1545 char ** outString
);
1547 DomainNameAppendString(
1548 uint8_t inDomainName
[ kDomainNameLengthMax
],
1549 const char * inString
,
1550 uint8_t ** outEndPtr
);
1551 static Boolean
DomainNameEqual( const uint8_t *inName1
, const uint8_t *inName2
);
1552 static size_t DomainNameLength( const uint8_t *inName
);
1554 DomainNameFromString(
1555 uint8_t inDomainName
[ kDomainNameLengthMax
],
1556 const char * inString
,
1557 uint8_t ** outEndPtr
);
1560 const uint8_t * inDomainName
,
1561 const uint8_t * inEnd
,
1562 char inBuf
[ kDNSServiceMaxDomainName
],
1563 const uint8_t ** outNextPtr
);
1567 const uint8_t * inMsgPtr
,
1573 #define kDNSQueryMessageMaxLen ( kDNSHeaderLength + kDomainNameLengthMax + 4 )
1576 WriteDNSQueryMessage(
1577 uint8_t inMsg
[ kDNSQueryMessageMaxLen
],
1580 const char * inQName
,
1583 size_t * outMsgLen
);
1587 typedef void ( *DispatchHandler
)( void *inContext
);
1590 DispatchSignalSourceCreate(
1592 DispatchHandler inEventHandler
,
1594 dispatch_source_t
* outSource
);
1596 DispatchSocketSourceCreate(
1598 dispatch_source_type_t inType
,
1599 dispatch_queue_t inQueue
,
1600 DispatchHandler inEventHandler
,
1601 DispatchHandler inCancelHandler
,
1603 dispatch_source_t
* outSource
);
1605 #define DispatchReadSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
1606 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_READ, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
1608 #define DispatchWriteSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
1609 DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_WRITE, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
1612 DispatchTimerCreate(
1613 dispatch_time_t inStart
,
1614 uint64_t inIntervalNs
,
1615 uint64_t inLeewayNs
,
1616 dispatch_queue_t inQueue
,
1617 DispatchHandler inEventHandler
,
1618 DispatchHandler inCancelHandler
,
1620 dispatch_source_t
* outTimer
);
1622 DispatchProcessMonitorCreate(
1624 unsigned long inFlags
,
1625 dispatch_queue_t inQueue
,
1626 DispatchHandler inEventHandler
,
1627 DispatchHandler inCancelHandler
,
1629 dispatch_source_t
* outMonitor
);
1631 static const char * ServiceTypeDescription( const char *inName
);
1635 SocketRef sock
; // Socket.
1636 void * userContext
; // User context.
1637 int32_t refCount
; // Reference count.
1641 static OSStatus
SocketContextCreate( SocketRef inSock
, void * inUserContext
, SocketContext
**outContext
);
1642 static SocketContext
* SocketContextRetain( SocketContext
*inContext
);
1643 static void SocketContextRelease( SocketContext
*inContext
);
1644 static void SocketContextCancelHandler( void *inContext
);
1646 #define ForgetSocketContext( X ) ForgetCustom( X, SocketContextRelease )
1648 static OSStatus
StringToInt32( const char *inString
, int32_t *outValue
);
1649 static OSStatus
StringToUInt32( const char *inString
, uint32_t *outValue
);
1650 static OSStatus
StringToLongLong( const char *inString
, long long *outValue
);
1651 static OSStatus
StringToARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
1652 static OSStatus
StringToAAAARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
1653 static OSStatus
StringToDomainName( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
1654 #if( TARGET_OS_DARWIN )
1655 static OSStatus
GetDefaultDNSServer( sockaddr_ip
*outAddr
);
1658 _ServerSocketOpenEx2(
1662 const void * inAddr
,
1666 Boolean inNoPortReuse
,
1667 SocketRef
* outSock
);
1669 typedef uint64_t MicroTime64
;
1671 static MicroTime64
GetCurrentMicroTime( void ); // Gets the number of milliseconds since 1970-01-01T00:00:00Z
1673 #define AddRmvString( X ) ( ( (X) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" )
1674 #define Unused( X ) (void)(X)
1676 //===========================================================================================================================
1678 //===========================================================================================================================
1680 int main( int argc
, const char **argv
)
1684 // Route DebugServices logging output to stderr.
1686 dlog_control( "DebugServices:output=file;stderr" );
1688 PrintFRegisterExtension( "du:time", PrintFTimestampHandler
, NULL
);
1689 PrintFRegisterExtension( "du:dnsmsg", PrintFDNSMessageHandler
, NULL
);
1690 CLIInit( argc
, argv
);
1691 err
= CLIParse( kGlobalOpts
, kCLIFlags_None
);
1692 if( err
) exit( 1 );
1694 return( gExitCode
);
1697 //===========================================================================================================================
1698 // VersionOptionCallback
1699 //===========================================================================================================================
1701 static OSStatus
VersionOptionCallback( CLIOption
*inOption
, const char *inArg
, int inUnset
)
1703 const char * srcVers
;
1704 #if( MDNSRESPONDER_PROJECT )
1712 #if( MDNSRESPONDER_PROJECT )
1713 srcVers
= SourceVersionToCString( _DNS_SD_H
, srcStr
);
1715 srcVers
= DNSSDUTIL_SOURCE_VERSION
;
1717 FPrintF( stdout
, "%s version %v (%s)\n", gProgramName
, kDNSSDUtilNumVersion
, srcVers
);
1719 return( kEndingErr
);
1722 //===========================================================================================================================
1724 //===========================================================================================================================
1726 typedef struct BrowseResolveOp BrowseResolveOp
;
1728 struct BrowseResolveOp
1730 BrowseResolveOp
* next
; // Next resolve operation in list.
1731 DNSServiceRef sdRef
; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
1732 char * fullName
; // Full name of the service to resolve.
1733 uint32_t interfaceIndex
; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
1738 DNSServiceRef mainRef
; // Main sdRef for shared connection.
1739 DNSServiceRef
* opRefs
; // Array of sdRefs for individual Browse operarions.
1740 size_t opRefsCount
; // Count of array of sdRefs for non-shared connections.
1741 const char * domain
; // Domain for DNSServiceBrowse operation(s).
1742 DNSServiceFlags flags
; // Flags for DNSServiceBrowse operation(s).
1743 char ** serviceTypes
; // Array of service types to browse for.
1744 size_t serviceTypesCount
; // Count of array of service types to browse for.
1745 int timeLimitSecs
; // Time limit of DNSServiceBrowse operation in seconds.
1746 BrowseResolveOp
* resolveList
; // List of resolve and/or TXT record query operations.
1747 uint32_t ifIndex
; // Interface index of DNSServiceBrowse operation(s).
1748 Boolean printedHeader
; // True if results header has been printed.
1749 Boolean doResolve
; // True if service instances are to be resolved.
1750 Boolean doResolveTXTOnly
; // True if TXT records of service instances are to be queried.
1754 static void BrowsePrintPrologue( const BrowseContext
*inContext
);
1755 static void BrowseContextFree( BrowseContext
*inContext
);
1756 static OSStatus
BrowseResolveOpCreate( const char *inFullName
, uint32_t inInterfaceIndex
, BrowseResolveOp
**outOp
);
1757 static void BrowseResolveOpFree( BrowseResolveOp
*inOp
);
1758 static void DNSSD_API
1760 DNSServiceRef inSDRef
,
1761 DNSServiceFlags inFlags
,
1762 uint32_t inInterfaceIndex
,
1763 DNSServiceErrorType inError
,
1764 const char * inName
,
1765 const char * inRegType
,
1766 const char * inDomain
,
1768 static void DNSSD_API
1769 BrowseResolveCallback(
1770 DNSServiceRef inSDRef
,
1771 DNSServiceFlags inFlags
,
1772 uint32_t inInterfaceIndex
,
1773 DNSServiceErrorType inError
,
1774 const char * inFullName
,
1775 const char * inHostname
,
1778 const unsigned char * inTXTPtr
,
1780 static void DNSSD_API
1781 BrowseQueryRecordCallback(
1782 DNSServiceRef inSDRef
,
1783 DNSServiceFlags inFlags
,
1784 uint32_t inInterfaceIndex
,
1785 DNSServiceErrorType inError
,
1786 const char * inFullName
,
1789 uint16_t inRDataLen
,
1790 const void * inRDataPtr
,
1794 static void BrowseCmd( void )
1798 BrowseContext
* context
= NULL
;
1799 dispatch_source_t signalSource
= NULL
;
1800 int useMainConnection
;
1802 // Set up SIGINT handler.
1804 signal( SIGINT
, SIG_IGN
);
1805 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
1806 require_noerr( err
, exit
);
1807 dispatch_resume( signalSource
);
1811 context
= (BrowseContext
*) calloc( 1, sizeof( *context
) );
1812 require_action( context
, exit
, err
= kNoMemoryErr
);
1814 context
->opRefs
= (DNSServiceRef
*) calloc( gBrowse_ServiceTypesCount
, sizeof( DNSServiceRef
) );
1815 require_action( context
->opRefs
, exit
, err
= kNoMemoryErr
);
1816 context
->opRefsCount
= gBrowse_ServiceTypesCount
;
1818 // Check command parameters.
1820 if( gBrowse_TimeLimitSecs
< 0 )
1822 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs
);
1827 // Create main connection.
1829 if( gConnectionOpt
)
1831 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
1832 require_noerr_quiet( err
, exit
);
1833 useMainConnection
= true;
1837 useMainConnection
= false;
1842 context
->flags
= GetDNSSDFlagsFromOpts();
1843 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
1847 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
1848 require_noerr_quiet( err
, exit
);
1850 // Set remaining parameters.
1852 context
->serviceTypes
= gBrowse_ServiceTypes
;
1853 context
->serviceTypesCount
= gBrowse_ServiceTypesCount
;
1854 context
->domain
= gBrowse_Domain
;
1855 context
->doResolve
= gBrowse_DoResolve
? true : false;
1856 context
->timeLimitSecs
= gBrowse_TimeLimitSecs
;
1857 context
->doResolveTXTOnly
= gBrowse_QueryTXT
? true : false;
1861 BrowsePrintPrologue( context
);
1863 // Start operation(s).
1865 for( i
= 0; i
< context
->serviceTypesCount
; ++i
)
1867 DNSServiceRef sdRef
;
1869 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
1870 err
= DNSServiceBrowse( &sdRef
, context
->flags
, context
->ifIndex
, context
->serviceTypes
[ i
], context
->domain
,
1871 BrowseCallback
, context
);
1872 require_noerr( err
, exit
);
1874 context
->opRefs
[ i
] = sdRef
;
1875 if( !useMainConnection
)
1877 err
= DNSServiceSetDispatchQueue( context
->opRefs
[ i
], dispatch_get_main_queue() );
1878 require_noerr( err
, exit
);
1884 if( context
->timeLimitSecs
> 0 )
1886 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
1887 kExitReason_TimeLimit
, Exit
);
1892 dispatch_source_forget( &signalSource
);
1893 if( context
) BrowseContextFree( context
);
1894 if( err
) exit( 1 );
1897 //===========================================================================================================================
1898 // BrowsePrintPrologue
1899 //===========================================================================================================================
1901 static void BrowsePrintPrologue( const BrowseContext
*inContext
)
1903 const int timeLimitSecs
= inContext
->timeLimitSecs
;
1904 const char * const * ptr
= (const char **) inContext
->serviceTypes
;
1905 const char * const * const end
= (const char **) inContext
->serviceTypes
+ inContext
->serviceTypesCount
;
1906 char ifName
[ kInterfaceNameBufLen
];
1908 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
1910 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
1911 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
1912 FPrintF( stdout
, "Service types: %s", *ptr
++ );
1913 while( ptr
< end
) FPrintF( stdout
, ", %s", *ptr
++ );
1914 FPrintF( stdout
, "\n" );
1915 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "<NULL> (default domains)" );
1916 FPrintF( stdout
, "Time limit: " );
1917 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
1918 else FPrintF( stdout
, "∞\n" );
1919 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
1920 FPrintF( stdout
, "---\n" );
1923 //===========================================================================================================================
1924 // BrowseContextFree
1925 //===========================================================================================================================
1927 static void BrowseContextFree( BrowseContext
*inContext
)
1931 for( i
= 0; i
< inContext
->opRefsCount
; ++i
)
1933 DNSServiceForget( &inContext
->opRefs
[ i
] );
1935 if( inContext
->serviceTypes
)
1937 StringArray_Free( inContext
->serviceTypes
, inContext
->serviceTypesCount
);
1938 inContext
->serviceTypes
= NULL
;
1939 inContext
->serviceTypesCount
= 0;
1941 DNSServiceForget( &inContext
->mainRef
);
1945 //===========================================================================================================================
1946 // BrowseResolveOpCreate
1947 //===========================================================================================================================
1949 static OSStatus
BrowseResolveOpCreate( const char *inFullName
, uint32_t inInterfaceIndex
, BrowseResolveOp
**outOp
)
1952 BrowseResolveOp
* resolveOp
;
1954 resolveOp
= (BrowseResolveOp
*) calloc( 1, sizeof( *resolveOp
) );
1955 require_action( resolveOp
, exit
, err
= kNoMemoryErr
);
1957 resolveOp
->fullName
= strdup( inFullName
);
1958 require_action( resolveOp
->fullName
, exit
, err
= kNoMemoryErr
);
1960 resolveOp
->interfaceIndex
= inInterfaceIndex
;
1967 if( resolveOp
) BrowseResolveOpFree( resolveOp
);
1971 //===========================================================================================================================
1972 // BrowseResolveOpFree
1973 //===========================================================================================================================
1975 static void BrowseResolveOpFree( BrowseResolveOp
*inOp
)
1977 DNSServiceForget( &inOp
->sdRef
);
1978 ForgetMem( &inOp
->fullName
);
1982 //===========================================================================================================================
1984 //===========================================================================================================================
1986 static void DNSSD_API
1988 DNSServiceRef inSDRef
,
1989 DNSServiceFlags inFlags
,
1990 uint32_t inInterfaceIndex
,
1991 DNSServiceErrorType inError
,
1992 const char * inName
,
1993 const char * inRegType
,
1994 const char * inDomain
,
1997 BrowseContext
* const context
= (BrowseContext
*) inContext
;
1999 BrowseResolveOp
* newOp
= NULL
;
2000 BrowseResolveOp
** p
;
2001 char fullName
[ kDNSServiceMaxDomainName
];
2006 gettimeofday( &now
, NULL
);
2009 require_noerr( err
, exit
);
2011 if( !context
->printedHeader
)
2013 FPrintF( stdout
, "%-26s A/R Flags IF %-20s %-20s Instance Name\n", "Timestamp", "Domain", "Service Type" );
2014 context
->printedHeader
= true;
2016 FPrintF( stdout
, "%{du:time} %-3s %5X %2d %-20s %-20s %s\n",
2017 &now
, AddRmvString( inFlags
), inFlags
, (int32_t) inInterfaceIndex
, inDomain
, inRegType
, inName
);
2019 if( !context
->doResolve
&& !context
->doResolveTXTOnly
) goto exit
;
2021 err
= DNSServiceConstructFullName( fullName
, inName
, inRegType
, inDomain
);
2022 require_noerr( err
, exit
);
2024 if( inFlags
& kDNSServiceFlagsAdd
)
2026 DNSServiceRef sdRef
;
2027 DNSServiceFlags flags
;
2029 err
= BrowseResolveOpCreate( fullName
, inInterfaceIndex
, &newOp
);
2030 require_noerr( err
, exit
);
2032 if( context
->mainRef
)
2034 sdRef
= context
->mainRef
;
2035 flags
= kDNSServiceFlagsShareConnection
;
2041 if( context
->doResolve
)
2043 err
= DNSServiceResolve( &sdRef
, flags
, inInterfaceIndex
, inName
, inRegType
, inDomain
, BrowseResolveCallback
,
2045 require_noerr( err
, exit
);
2049 err
= DNSServiceQueryRecord( &sdRef
, flags
, inInterfaceIndex
, fullName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
,
2050 BrowseQueryRecordCallback
, NULL
);
2051 require_noerr( err
, exit
);
2054 newOp
->sdRef
= sdRef
;
2055 if( !context
->mainRef
)
2057 err
= DNSServiceSetDispatchQueue( newOp
->sdRef
, dispatch_get_main_queue() );
2058 require_noerr( err
, exit
);
2060 for( p
= &context
->resolveList
; *p
; p
= &( *p
)->next
) {}
2066 BrowseResolveOp
* resolveOp
;
2068 for( p
= &context
->resolveList
; ( resolveOp
= *p
) != NULL
; p
= &resolveOp
->next
)
2070 if( ( resolveOp
->interfaceIndex
== inInterfaceIndex
) && ( strcasecmp( resolveOp
->fullName
, fullName
) == 0 ) )
2077 *p
= resolveOp
->next
;
2078 BrowseResolveOpFree( resolveOp
);
2083 if( newOp
) BrowseResolveOpFree( newOp
);
2084 if( err
) exit( 1 );
2087 //===========================================================================================================================
2088 // BrowseQueryRecordCallback
2089 //===========================================================================================================================
2091 static void DNSSD_API
2092 BrowseQueryRecordCallback(
2093 DNSServiceRef inSDRef
,
2094 DNSServiceFlags inFlags
,
2095 uint32_t inInterfaceIndex
,
2096 DNSServiceErrorType inError
,
2097 const char * inFullName
,
2100 uint16_t inRDataLen
,
2101 const void * inRDataPtr
,
2111 Unused( inContext
);
2113 gettimeofday( &now
, NULL
);
2116 require_noerr( err
, exit
);
2117 require_action( inType
== kDNSServiceType_TXT
, exit
, err
= kTypeErr
);
2119 FPrintF( stdout
, "%{du:time} %s %s TXT on interface %d\n TXT: %#{txt}\n",
2120 &now
, AddRmvString( inFlags
), inFullName
, (int32_t) inInterfaceIndex
, inRDataPtr
, (size_t) inRDataLen
);
2123 if( err
) exit( 1 );
2126 //===========================================================================================================================
2127 // BrowseResolveCallback
2128 //===========================================================================================================================
2130 static void DNSSD_API
2131 BrowseResolveCallback(
2132 DNSServiceRef inSDRef
,
2133 DNSServiceFlags inFlags
,
2134 uint32_t inInterfaceIndex
,
2135 DNSServiceErrorType inError
,
2136 const char * inFullName
,
2137 const char * inHostname
,
2140 const unsigned char * inTXTPtr
,
2144 char errorStr
[ 64 ];
2148 Unused( inContext
);
2150 gettimeofday( &now
, NULL
);
2152 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " error %#m", inError
);
2154 FPrintF( stdout
, "%{du:time} %s can be reached at %s:%u (interface %d)%?s\n",
2155 &now
, inFullName
, inHostname
, ntohs( inPort
), (int32_t) inInterfaceIndex
, inError
, errorStr
);
2158 FPrintF( stdout
, " TXT record: %#H\n", inTXTPtr
, (int) inTXTLen
, INT_MAX
);
2162 FPrintF( stdout
, " TXT record: %#{txt}\n", inTXTPtr
, (size_t) inTXTLen
);
2166 //===========================================================================================================================
2168 //===========================================================================================================================
2172 DNSServiceRef mainRef
; // Main sdRef for shared connection.
2173 DNSServiceRef opRef
; // sdRef for the DNSServiceGetAddrInfo operation.
2174 const char * name
; // Hostname to resolve.
2175 DNSServiceFlags flags
; // Flags argument for DNSServiceGetAddrInfo().
2176 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceGetAddrInfo().
2177 uint32_t ifIndex
; // Interface index argument for DNSServiceGetAddrInfo().
2178 int timeLimitSecs
; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
2179 Boolean printedHeader
; // True if the results header has been printed.
2180 Boolean oneShotMode
; // True if command is done after the first set of results (one-shot mode).
2181 Boolean needIPv4
; // True if in one-shot mode and an IPv4 result is needed.
2182 Boolean needIPv6
; // True if in one-shot mode and an IPv6 result is needed.
2184 } GetAddrInfoContext
;
2186 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext
*inContext
);
2187 static void GetAddrInfoContextFree( GetAddrInfoContext
*inContext
);
2188 static void DNSSD_API
2189 GetAddrInfoCallback(
2190 DNSServiceRef inSDRef
,
2191 DNSServiceFlags inFlags
,
2192 uint32_t inInterfaceIndex
,
2193 DNSServiceErrorType inError
,
2194 const char * inHostname
,
2195 const struct sockaddr
* inSockAddr
,
2199 static void GetAddrInfoCmd( void )
2202 DNSServiceRef sdRef
;
2203 GetAddrInfoContext
* context
= NULL
;
2204 dispatch_source_t signalSource
= NULL
;
2205 int useMainConnection
;
2207 // Set up SIGINT handler.
2209 signal( SIGINT
, SIG_IGN
);
2210 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
2211 require_noerr( err
, exit
);
2212 dispatch_resume( signalSource
);
2214 // Check command parameters.
2216 if( gGetAddrInfo_TimeLimitSecs
< 0 )
2218 FPrintF( stderr
, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs
);
2225 context
= (GetAddrInfoContext
*) calloc( 1, sizeof( *context
) );
2226 require_action( context
, exit
, err
= kNoMemoryErr
);
2228 // Create main connection.
2230 if( gConnectionOpt
)
2232 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
2233 require_noerr_quiet( err
, exit
);
2234 useMainConnection
= true;
2238 useMainConnection
= false;
2243 context
->flags
= GetDNSSDFlagsFromOpts();
2244 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
2248 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
2249 require_noerr_quiet( err
, exit
);
2251 // Set remaining parameters.
2253 context
->name
= gGetAddrInfo_Name
;
2254 context
->timeLimitSecs
= gGetAddrInfo_TimeLimitSecs
;
2255 if( gGetAddrInfo_ProtocolIPv4
) context
->protocols
|= kDNSServiceProtocol_IPv4
;
2256 if( gGetAddrInfo_ProtocolIPv6
) context
->protocols
|= kDNSServiceProtocol_IPv6
;
2257 if( gGetAddrInfo_OneShot
)
2259 context
->oneShotMode
= true;
2260 context
->needIPv4
= ( gGetAddrInfo_ProtocolIPv4
|| !gGetAddrInfo_ProtocolIPv6
) ? true : false;
2261 context
->needIPv6
= ( gGetAddrInfo_ProtocolIPv6
|| !gGetAddrInfo_ProtocolIPv4
) ? true : false;
2266 GetAddrInfoPrintPrologue( context
);
2270 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
2271 err
= DNSServiceGetAddrInfo( &sdRef
, context
->flags
, context
->ifIndex
, context
->protocols
, context
->name
,
2272 GetAddrInfoCallback
, context
);
2273 require_noerr( err
, exit
);
2275 context
->opRef
= sdRef
;
2276 if( !useMainConnection
)
2278 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
2279 require_noerr( err
, exit
);
2284 if( context
->timeLimitSecs
> 0 )
2286 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
2287 kExitReason_TimeLimit
, Exit
);
2292 dispatch_source_forget( &signalSource
);
2293 if( context
) GetAddrInfoContextFree( context
);
2294 if( err
) exit( 1 );
2297 //===========================================================================================================================
2298 // GetAddrInfoPrintPrologue
2299 //===========================================================================================================================
2301 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext
*inContext
)
2303 const int timeLimitSecs
= inContext
->timeLimitSecs
;
2304 char ifName
[ kInterfaceNameBufLen
];
2306 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
2308 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
2309 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
2310 FPrintF( stdout
, "Protocols: %#{flags}\n", inContext
->protocols
, kDNSServiceProtocolDescriptors
);
2311 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
2312 FPrintF( stdout
, "Mode: %s\n", inContext
->oneShotMode
? "one-shot" : "continuous" );
2313 FPrintF( stdout
, "Time limit: " );
2314 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
2315 else FPrintF( stdout
, "∞\n" );
2316 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
2317 FPrintF( stdout
, "---\n" );
2320 //===========================================================================================================================
2321 // GetAddrInfoContextFree
2322 //===========================================================================================================================
2324 static void GetAddrInfoContextFree( GetAddrInfoContext
*inContext
)
2326 DNSServiceForget( &inContext
->opRef
);
2327 DNSServiceForget( &inContext
->mainRef
);
2331 //===========================================================================================================================
2332 // GetAddrInfoCallback
2333 //===========================================================================================================================
2335 static void DNSSD_API
2336 GetAddrInfoCallback(
2337 DNSServiceRef inSDRef
,
2338 DNSServiceFlags inFlags
,
2339 uint32_t inInterfaceIndex
,
2340 DNSServiceErrorType inError
,
2341 const char * inHostname
,
2342 const struct sockaddr
* inSockAddr
,
2346 GetAddrInfoContext
* const context
= (GetAddrInfoContext
*) inContext
;
2349 const char * addrStr
;
2350 char addrStrBuf
[ kSockAddrStringMaxSize
];
2354 gettimeofday( &now
, NULL
);
2358 case kDNSServiceErr_NoError
:
2359 case kDNSServiceErr_NoSuchRecord
:
2363 case kDNSServiceErr_Timeout
:
2364 Exit( kExitReason_Timeout
);
2371 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
2373 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
2380 err
= SockAddrToString( inSockAddr
, kSockAddrStringFlagsNone
, addrStrBuf
);
2381 require_noerr( err
, exit
);
2382 addrStr
= addrStrBuf
;
2386 addrStr
= ( inSockAddr
->sa_family
== AF_INET
) ? "No Such Record (A)" : "No Such Record (AAAA)";
2389 if( !context
->printedHeader
)
2391 FPrintF( stdout
, "%-26s A/R Flags IF %-32s %-38s %6s\n", "Timestamp", "Hostname", "Address", "TTL" );
2392 context
->printedHeader
= true;
2394 FPrintF( stdout
, "%{du:time} %s %5X %2d %-32s %-38s %6u\n",
2395 &now
, AddRmvString( inFlags
), inFlags
, (int32_t) inInterfaceIndex
, inHostname
, addrStr
, inTTL
);
2397 if( context
->oneShotMode
)
2399 if( inFlags
& kDNSServiceFlagsAdd
)
2401 if( inSockAddr
->sa_family
== AF_INET
) context
->needIPv4
= false;
2402 else context
->needIPv6
= false;
2404 if( !( inFlags
& kDNSServiceFlagsMoreComing
) && !context
->needIPv4
&& !context
->needIPv6
)
2406 Exit( kExitReason_OneShotDone
);
2411 if( err
) exit( 1 );
2414 //===========================================================================================================================
2416 //===========================================================================================================================
2420 DNSServiceRef mainRef
; // Main sdRef for shared connection.
2421 DNSServiceRef opRef
; // sdRef for the DNSServiceQueryRecord operation.
2422 const char * recordName
; // Resource record name argument for DNSServiceQueryRecord().
2423 DNSServiceFlags flags
; // Flags argument for DNSServiceQueryRecord().
2424 uint32_t ifIndex
; // Interface index argument for DNSServiceQueryRecord().
2425 int timeLimitSecs
; // Time limit for the DNSServiceQueryRecord() operation in seconds.
2426 uint16_t recordType
; // Resource record type argument for DNSServiceQueryRecord().
2427 Boolean printedHeader
; // True if the results header was printed.
2428 Boolean oneShotMode
; // True if command is done after the first set of results (one-shot mode).
2429 Boolean gotRecord
; // True if in one-shot mode and received at least one record of the desired type.
2430 Boolean printRawRData
; // True if RDATA results are not to be formatted when printed.
2432 } QueryRecordContext
;
2434 static void QueryRecordPrintPrologue( const QueryRecordContext
*inContext
);
2435 static void QueryRecordContextFree( QueryRecordContext
*inContext
);
2436 static void DNSSD_API
2437 QueryRecordCallback(
2438 DNSServiceRef inSDRef
,
2439 DNSServiceFlags inFlags
,
2440 uint32_t inInterfaceIndex
,
2441 DNSServiceErrorType inError
,
2442 const char * inFullName
,
2445 uint16_t inRDataLen
,
2446 const void * inRDataPtr
,
2450 static void QueryRecordCmd( void )
2453 DNSServiceRef sdRef
;
2454 QueryRecordContext
* context
= NULL
;
2455 dispatch_source_t signalSource
= NULL
;
2456 int useMainConnection
;
2458 // Set up SIGINT handler.
2460 signal( SIGINT
, SIG_IGN
);
2461 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
2462 require_noerr( err
, exit
);
2463 dispatch_resume( signalSource
);
2467 context
= (QueryRecordContext
*) calloc( 1, sizeof( *context
) );
2468 require_action( context
, exit
, err
= kNoMemoryErr
);
2470 // Check command parameters.
2472 if( gQueryRecord_TimeLimitSecs
< 0 )
2474 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs
);
2479 // Create main connection.
2481 if( gConnectionOpt
)
2483 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
2484 require_noerr_quiet( err
, exit
);
2485 useMainConnection
= true;
2489 useMainConnection
= false;
2494 context
->flags
= GetDNSSDFlagsFromOpts();
2495 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
2499 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
2500 require_noerr_quiet( err
, exit
);
2504 err
= RecordTypeFromArgString( gQueryRecord_Type
, &context
->recordType
);
2505 require_noerr( err
, exit
);
2507 // Set remaining parameters.
2509 context
->recordName
= gQueryRecord_Name
;
2510 context
->timeLimitSecs
= gQueryRecord_TimeLimitSecs
;
2511 context
->oneShotMode
= gQueryRecord_OneShot
? true : false;
2512 context
->printRawRData
= gQueryRecord_RawRData
? true : false;
2516 QueryRecordPrintPrologue( context
);
2520 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
2521 err
= DNSServiceQueryRecord( &sdRef
, context
->flags
, context
->ifIndex
, context
->recordName
, context
->recordType
,
2522 kDNSServiceClass_IN
, QueryRecordCallback
, context
);
2523 require_noerr( err
, exit
);
2525 context
->opRef
= sdRef
;
2526 if( !useMainConnection
)
2528 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
2529 require_noerr( err
, exit
);
2534 if( context
->timeLimitSecs
> 0 )
2536 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_TimeLimit
,
2542 dispatch_source_forget( &signalSource
);
2543 if( context
) QueryRecordContextFree( context
);
2544 if( err
) exit( 1 );
2547 //===========================================================================================================================
2548 // QueryRecordContextFree
2549 //===========================================================================================================================
2551 static void QueryRecordContextFree( QueryRecordContext
*inContext
)
2553 DNSServiceForget( &inContext
->opRef
);
2554 DNSServiceForget( &inContext
->mainRef
);
2558 //===========================================================================================================================
2559 // QueryRecordPrintPrologue
2560 //===========================================================================================================================
2562 static void QueryRecordPrintPrologue( const QueryRecordContext
*inContext
)
2564 const int timeLimitSecs
= inContext
->timeLimitSecs
;
2565 char ifName
[ kInterfaceNameBufLen
];
2567 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
2569 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
2570 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
2571 FPrintF( stdout
, "Name: %s\n", inContext
->recordName
);
2572 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->recordType
), inContext
->recordType
);
2573 FPrintF( stdout
, "Mode: %s\n", inContext
->oneShotMode
? "one-shot" : "continuous" );
2574 FPrintF( stdout
, "Time limit: " );
2575 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
2576 else FPrintF( stdout
, "∞\n" );
2577 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
2578 FPrintF( stdout
, "---\n" );
2582 //===========================================================================================================================
2583 // QueryRecordCallback
2584 //===========================================================================================================================
2586 static void DNSSD_API
2587 QueryRecordCallback(
2588 DNSServiceRef inSDRef
,
2589 DNSServiceFlags inFlags
,
2590 uint32_t inInterfaceIndex
,
2591 DNSServiceErrorType inError
,
2592 const char * inFullName
,
2595 uint16_t inRDataLen
,
2596 const void * inRDataPtr
,
2600 QueryRecordContext
* const context
= (QueryRecordContext
*) inContext
;
2603 char * rdataStr
= NULL
;
2607 gettimeofday( &now
, NULL
);
2611 case kDNSServiceErr_NoError
:
2612 case kDNSServiceErr_NoSuchRecord
:
2616 case kDNSServiceErr_Timeout
:
2617 Exit( kExitReason_Timeout
);
2624 if( inError
== kDNSServiceErr_NoSuchRecord
)
2626 ASPrintF( &rdataStr
, "No Such Record" );
2630 if( !context
->printRawRData
) DNSRecordDataToString( inRDataPtr
, inRDataLen
, inType
, NULL
, 0, &rdataStr
);
2633 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, INT_MAX
);
2634 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
2638 if( !context
->printedHeader
)
2640 FPrintF( stdout
, "%-26s A/R Flags IF %-32s %-5s %-5s %6s RData\n", "Timestamp", "Name", "Type", "Class", "TTL" );
2641 context
->printedHeader
= true;
2643 FPrintF( stdout
, "%{du:time} %-3s %5X %2d %-32s %-5s %?-5s%?5u %6u %s\n",
2644 &now
, AddRmvString( inFlags
), inFlags
, (int32_t) inInterfaceIndex
, inFullName
, RecordTypeToString( inType
),
2645 ( inClass
== kDNSServiceClass_IN
), "IN", ( inClass
!= kDNSServiceClass_IN
), inClass
, inTTL
, rdataStr
);
2647 if( context
->oneShotMode
)
2649 if( ( inFlags
& kDNSServiceFlagsAdd
) &&
2650 ( ( context
->recordType
== kDNSServiceType_ANY
) || ( context
->recordType
== inType
) ) )
2652 context
->gotRecord
= true;
2654 if( !( inFlags
& kDNSServiceFlagsMoreComing
) && context
->gotRecord
) Exit( kExitReason_OneShotDone
);
2658 FreeNullSafe( rdataStr
);
2659 if( err
) exit( 1 );
2662 //===========================================================================================================================
2664 //===========================================================================================================================
2668 DNSRecordRef recordRef
; // Reference returned by DNSServiceAddRecord().
2669 uint8_t * dataPtr
; // Record data.
2670 size_t dataLen
; // Record data length.
2671 uint32_t ttl
; // Record TTL value.
2672 uint16_t type
; // Record type.
2678 DNSServiceRef opRef
; // sdRef for DNSServiceRegister operation.
2679 const char * name
; // Service name argument for DNSServiceRegister().
2680 const char * type
; // Service type argument for DNSServiceRegister().
2681 const char * domain
; // Domain in which advertise the service.
2682 uint8_t * txtPtr
; // Service TXT record data. (malloc'd)
2683 size_t txtLen
; // Service TXT record data len.
2684 ExtraRecord
* extraRecords
; // Array of extra records to add to registered service.
2685 size_t extraRecordsCount
; // Number of extra records.
2686 uint8_t * updateTXTPtr
; // Pointer to record data for TXT record update. (malloc'd)
2687 size_t updateTXTLen
; // Length of record data for TXT record update.
2688 uint32_t updateTTL
; // TTL of updated TXT record.
2689 int updateDelayMs
; // Post-registration TXT record update delay in milliseconds.
2690 DNSServiceFlags flags
; // Flags argument for DNSServiceRegister().
2691 uint32_t ifIndex
; // Interface index argument for DNSServiceRegister().
2692 int lifetimeMs
; // Lifetime of the record registration in milliseconds.
2693 uint16_t port
; // Service instance's port number.
2694 Boolean printedHeader
; // True if results header was printed.
2695 Boolean didRegister
; // True if service was registered.
2699 static void RegisterPrintPrologue( const RegisterContext
*inContext
);
2700 static void RegisterContextFree( RegisterContext
*inContext
);
2701 static void DNSSD_API
2703 DNSServiceRef inSDRef
,
2704 DNSServiceFlags inFlags
,
2705 DNSServiceErrorType inError
,
2706 const char * inName
,
2707 const char * inType
,
2708 const char * inDomain
,
2710 static void RegisterUpdate( void *inContext
);
2712 static void RegisterCmd( void )
2715 RegisterContext
* context
= NULL
;
2716 dispatch_source_t signalSource
= NULL
;
2718 // Set up SIGINT handler.
2720 signal( SIGINT
, SIG_IGN
);
2721 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
2722 require_noerr( err
, exit
);
2723 dispatch_resume( signalSource
);
2727 context
= (RegisterContext
*) calloc( 1, sizeof( *context
) );
2728 require_action( context
, exit
, err
= kNoMemoryErr
);
2730 // Check command parameters.
2732 if( ( gRegister_Port
< 0 ) || ( gRegister_Port
> UINT16_MAX
) )
2734 FPrintF( stderr
, "Port number %d is out-of-range.\n", gRegister_Port
);
2739 if( ( gAddRecord_DataCount
!= gAddRecord_TypesCount
) || ( gAddRecord_TTLsCount
!= gAddRecord_TypesCount
) )
2741 FPrintF( stderr
, "There are missing additional record parameters.\n" );
2748 context
->flags
= GetDNSSDFlagsFromOpts();
2750 // Get interface index.
2752 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
2753 require_noerr_quiet( err
, exit
);
2755 // Get TXT record data.
2759 err
= RecordDataFromArgString( gRegister_TXT
, &context
->txtPtr
, &context
->txtLen
);
2760 require_noerr_quiet( err
, exit
);
2763 // Set remaining parameters.
2765 context
->name
= gRegister_Name
;
2766 context
->type
= gRegister_Type
;
2767 context
->domain
= gRegister_Domain
;
2768 context
->port
= (uint16_t) gRegister_Port
;
2769 context
->lifetimeMs
= gRegister_LifetimeMs
;
2771 if( gAddRecord_TypesCount
> 0 )
2775 context
->extraRecords
= (ExtraRecord
*) calloc( gAddRecord_TypesCount
, sizeof( ExtraRecord
) );
2776 require_action( context
, exit
, err
= kNoMemoryErr
);
2777 context
->extraRecordsCount
= gAddRecord_TypesCount
;
2779 for( i
= 0; i
< gAddRecord_TypesCount
; ++i
)
2781 ExtraRecord
* const extraRecord
= &context
->extraRecords
[ i
];
2783 err
= RecordTypeFromArgString( gAddRecord_Types
[ i
], &extraRecord
->type
);
2784 require_noerr( err
, exit
);
2786 err
= StringToUInt32( gAddRecord_TTLs
[ i
], &extraRecord
->ttl
);
2789 FPrintF( stderr
, "Invalid TTL value: %s\n", gAddRecord_TTLs
[ i
] );
2794 err
= RecordDataFromArgString( gAddRecord_Data
[ i
], &extraRecord
->dataPtr
, &extraRecord
->dataLen
);
2795 require_noerr_quiet( err
, exit
);
2799 if( gUpdateRecord_Data
)
2801 err
= RecordDataFromArgString( gUpdateRecord_Data
, &context
->updateTXTPtr
, &context
->updateTXTLen
);
2802 require_noerr_quiet( err
, exit
);
2804 context
->updateTTL
= (uint32_t) gUpdateRecord_TTL
;
2805 context
->updateDelayMs
= gUpdateRecord_DelayMs
;
2810 RegisterPrintPrologue( context
);
2814 err
= DNSServiceRegister( &context
->opRef
, context
->flags
, context
->ifIndex
, context
->name
, context
->type
,
2815 context
->domain
, NULL
, htons( context
->port
), (uint16_t) context
->txtLen
, context
->txtPtr
,
2816 RegisterCallback
, context
);
2817 ForgetMem( &context
->txtPtr
);
2818 require_noerr( err
, exit
);
2820 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
2821 require_noerr( err
, exit
);
2826 dispatch_source_forget( &signalSource
);
2827 if( context
) RegisterContextFree( context
);
2828 if( err
) exit( 1 );
2831 //===========================================================================================================================
2832 // RegisterPrintPrologue
2833 //===========================================================================================================================
2835 static void RegisterPrintPrologue( const RegisterContext
*inContext
)
2839 char ifName
[ kInterfaceNameBufLen
];
2841 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
2843 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
2844 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
2845 FPrintF( stdout
, "Name: %s\n", inContext
->name
? inContext
->name
: "<NULL>" );
2846 FPrintF( stdout
, "Type: %s\n", inContext
->type
);
2847 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "<NULL> (default domains)" );
2848 FPrintF( stdout
, "Port: %u\n", inContext
->port
);
2849 FPrintF( stdout
, "TXT data: %#{txt}\n", inContext
->txtPtr
, inContext
->txtLen
);
2850 infinite
= ( inContext
->lifetimeMs
< 0 ) ? true : false;
2851 FPrintF( stdout
, "Lifetime: %?s%?d ms\n", infinite
, "∞", !infinite
, inContext
->lifetimeMs
);
2852 if( inContext
->updateTXTPtr
)
2854 FPrintF( stdout
, "\nUpdate record:\n" );
2855 FPrintF( stdout
, " Delay: %d ms\n", ( inContext
->updateDelayMs
> 0 ) ? inContext
->updateDelayMs
: 0 );
2856 FPrintF( stdout
, " TTL: %u%?s\n",
2857 inContext
->updateTTL
, inContext
->updateTTL
== 0, " (system will use a default value.)" );
2858 FPrintF( stdout
, " TXT data: %#{txt}\n", inContext
->updateTXTPtr
, inContext
->updateTXTLen
);
2860 if( inContext
->extraRecordsCount
> 0 ) FPrintF( stdout
, "\n" );
2861 for( i
= 0; i
< inContext
->extraRecordsCount
; ++i
)
2863 const ExtraRecord
* record
= &inContext
->extraRecords
[ i
];
2865 FPrintF( stdout
, "Extra record %zu:\n", i
+ 1 );
2866 FPrintF( stdout
, " Type: %s (%u)\n", RecordTypeToString( record
->type
), record
->type
);
2867 FPrintF( stdout
, " TTL: %u%?s\n", record
->ttl
, record
->ttl
== 0, " (system will use a default value.)" );
2868 FPrintF( stdout
, " RData: %#H\n\n", record
->dataPtr
, (int) record
->dataLen
, INT_MAX
);
2870 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
2871 FPrintF( stdout
, "---\n" );
2874 //===========================================================================================================================
2875 // RegisterContextFree
2876 //===========================================================================================================================
2878 static void RegisterContextFree( RegisterContext
*inContext
)
2880 ExtraRecord
* record
;
2881 const ExtraRecord
* const end
= inContext
->extraRecords
+ inContext
->extraRecordsCount
;
2883 DNSServiceForget( &inContext
->opRef
);
2884 ForgetMem( &inContext
->txtPtr
);
2885 for( record
= inContext
->extraRecords
; record
< end
; ++record
)
2887 check( !record
->recordRef
);
2888 ForgetMem( &record
->dataPtr
);
2890 ForgetMem( &inContext
->extraRecords
);
2891 ForgetMem( &inContext
->updateTXTPtr
);
2895 //===========================================================================================================================
2897 //===========================================================================================================================
2899 static void DNSSD_API
2901 DNSServiceRef inSDRef
,
2902 DNSServiceFlags inFlags
,
2903 DNSServiceErrorType inError
,
2904 const char * inName
,
2905 const char * inType
,
2906 const char * inDomain
,
2909 RegisterContext
* const context
= (RegisterContext
*) inContext
;
2915 gettimeofday( &now
, NULL
);
2917 if( !context
->printedHeader
)
2919 FPrintF( stdout
, "%-26s A/R Flags Service\n", "Timestamp" );
2920 context
->printedHeader
= true;
2922 FPrintF( stdout
, "%{du:time} %-3s %5X %s.%s%s %?#m\n",
2923 &now
, AddRmvString( inFlags
), inFlags
, inName
, inType
, inDomain
, inError
, inError
);
2925 require_noerr_action_quiet( inError
, exit
, err
= inError
);
2927 if( !context
->didRegister
&& ( inFlags
& kDNSServiceFlagsAdd
) )
2929 context
->didRegister
= true;
2930 if( context
->updateTXTPtr
)
2932 if( context
->updateDelayMs
> 0 )
2934 dispatch_after_f( dispatch_time_milliseconds( context
->updateDelayMs
), dispatch_get_main_queue(),
2935 context
, RegisterUpdate
);
2939 RegisterUpdate( context
);
2942 if( context
->extraRecordsCount
> 0 )
2944 ExtraRecord
* record
;
2945 const ExtraRecord
* const end
= context
->extraRecords
+ context
->extraRecordsCount
;
2947 for( record
= context
->extraRecords
; record
< end
; ++record
)
2949 err
= DNSServiceAddRecord( context
->opRef
, &record
->recordRef
, 0, record
->type
,
2950 (uint16_t) record
->dataLen
, record
->dataPtr
, record
->ttl
);
2951 require_noerr( err
, exit
);
2954 if( context
->lifetimeMs
== 0 )
2956 Exit( kExitReason_TimeLimit
);
2958 else if( context
->lifetimeMs
> 0 )
2960 dispatch_after_f( dispatch_time_milliseconds( context
->lifetimeMs
), dispatch_get_main_queue(),
2961 kExitReason_TimeLimit
, Exit
);
2967 if( err
) exit( 1 );
2970 //===========================================================================================================================
2972 //===========================================================================================================================
2974 static void RegisterUpdate( void *inContext
)
2977 RegisterContext
* const context
= (RegisterContext
*) inContext
;
2979 err
= DNSServiceUpdateRecord( context
->opRef
, NULL
, 0, (uint16_t) context
->updateTXTLen
, context
->updateTXTPtr
,
2980 context
->updateTTL
);
2981 require_noerr( err
, exit
);
2984 if( err
) exit( 1 );
2987 //===========================================================================================================================
2988 // RegisterRecordCmd
2989 //===========================================================================================================================
2993 DNSServiceRef conRef
; // sdRef to be initialized by DNSServiceCreateConnection().
2994 DNSRecordRef recordRef
; // Registered record reference.
2995 const char * recordName
; // Name of resource record.
2996 uint8_t * dataPtr
; // Pointer to resource record data.
2997 size_t dataLen
; // Length of resource record data.
2998 uint32_t ttl
; // TTL value of resource record in seconds.
2999 uint32_t ifIndex
; // Interface index argument for DNSServiceRegisterRecord().
3000 DNSServiceFlags flags
; // Flags argument for DNSServiceRegisterRecord().
3001 int lifetimeMs
; // Lifetime of the record registration in milliseconds.
3002 uint16_t recordType
; // Resource record type.
3003 uint8_t * updateDataPtr
; // Pointer to data for record update. (malloc'd)
3004 size_t updateDataLen
; // Length of data for record update.
3005 uint32_t updateTTL
; // TTL for updated record.
3006 int updateDelayMs
; // Post-registration record update delay in milliseconds.
3007 Boolean didRegister
; // True if the record was registered.
3009 } RegisterRecordContext
;
3011 static void RegisterRecordPrintPrologue( const RegisterRecordContext
*inContext
);
3012 static void RegisterRecordContextFree( RegisterRecordContext
*inContext
);
3013 static void DNSSD_API
3014 RegisterRecordCallback(
3015 DNSServiceRef inSDRef
,
3016 DNSRecordRef inRecordRef
,
3017 DNSServiceFlags inFlags
,
3018 DNSServiceErrorType inError
,
3020 static void RegisterRecordUpdate( void *inContext
);
3022 static void RegisterRecordCmd( void )
3025 RegisterRecordContext
* context
= NULL
;
3026 dispatch_source_t signalSource
= NULL
;
3028 // Set up SIGINT handler.
3030 signal( SIGINT
, SIG_IGN
);
3031 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3032 require_noerr( err
, exit
);
3033 dispatch_resume( signalSource
);
3037 context
= (RegisterRecordContext
*) calloc( 1, sizeof( *context
) );
3038 require_action( context
, exit
, err
= kNoMemoryErr
);
3040 // Create connection.
3042 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->conRef
, NULL
);
3043 require_noerr_quiet( err
, exit
);
3047 context
->flags
= GetDNSSDFlagsFromOpts();
3051 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3052 require_noerr_quiet( err
, exit
);
3056 err
= RecordTypeFromArgString( gRegisterRecord_Type
, &context
->recordType
);
3057 require_noerr( err
, exit
);
3061 if( gRegisterRecord_Data
)
3063 err
= RecordDataFromArgString( gRegisterRecord_Data
, &context
->dataPtr
, &context
->dataLen
);
3064 require_noerr_quiet( err
, exit
);
3067 // Set remaining parameters.
3069 context
->recordName
= gRegisterRecord_Name
;
3070 context
->ttl
= (uint32_t) gRegisterRecord_TTL
;
3071 context
->lifetimeMs
= gRegisterRecord_LifetimeMs
;
3075 if( gRegisterRecord_UpdateData
)
3077 err
= RecordDataFromArgString( gRegisterRecord_UpdateData
, &context
->updateDataPtr
, &context
->updateDataLen
);
3078 require_noerr_quiet( err
, exit
);
3080 context
->updateTTL
= (uint32_t) gRegisterRecord_UpdateTTL
;
3081 context
->updateDelayMs
= gRegisterRecord_UpdateDelayMs
;
3086 RegisterRecordPrintPrologue( context
);
3090 err
= DNSServiceRegisterRecord( context
->conRef
, &context
->recordRef
, context
->flags
, context
->ifIndex
,
3091 context
->recordName
, context
->recordType
, kDNSServiceClass_IN
, (uint16_t) context
->dataLen
, context
->dataPtr
,
3092 context
->ttl
, RegisterRecordCallback
, context
);
3095 FPrintF( stderr
, "DNSServiceRegisterRecord() returned %#m\n", err
);
3102 dispatch_source_forget( &signalSource
);
3103 if( context
) RegisterRecordContextFree( context
);
3104 if( err
) exit( 1 );
3107 //===========================================================================================================================
3108 // RegisterRecordPrintPrologue
3109 //===========================================================================================================================
3111 static void RegisterRecordPrintPrologue( const RegisterRecordContext
*inContext
)
3114 char ifName
[ kInterfaceNameBufLen
];
3116 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3118 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3119 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3120 FPrintF( stdout
, "Name: %s\n", inContext
->recordName
);
3121 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->recordType
), inContext
->recordType
);
3122 FPrintF( stdout
, "TTL: %u\n", inContext
->ttl
);
3123 FPrintF( stdout
, "Data: %#H\n", inContext
->dataPtr
, (int) inContext
->dataLen
, INT_MAX
);
3124 infinite
= ( inContext
->lifetimeMs
< 0 ) ? true : false;
3125 FPrintF( stdout
, "Lifetime: %?s%?d ms\n", infinite
, "∞", !infinite
, inContext
->lifetimeMs
);
3126 if( inContext
->updateDataPtr
)
3128 FPrintF( stdout
, "\nUpdate record:\n" );
3129 FPrintF( stdout
, " Delay: %d ms\n", ( inContext
->updateDelayMs
>= 0 ) ? inContext
->updateDelayMs
: 0 );
3130 FPrintF( stdout
, " TTL: %u%?s\n",
3131 inContext
->updateTTL
, inContext
->updateTTL
== 0, " (system will use a default value.)" );
3132 FPrintF( stdout
, " RData: %#H\n", inContext
->updateDataPtr
, (int) inContext
->updateDataLen
, INT_MAX
);
3134 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3135 FPrintF( stdout
, "---\n" );
3138 //===========================================================================================================================
3139 // RegisterRecordContextFree
3140 //===========================================================================================================================
3142 static void RegisterRecordContextFree( RegisterRecordContext
*inContext
)
3144 DNSServiceForget( &inContext
->conRef
);
3145 ForgetMem( &inContext
->dataPtr
);
3146 ForgetMem( &inContext
->updateDataPtr
);
3150 //===========================================================================================================================
3151 // RegisterRecordCallback
3152 //===========================================================================================================================
3155 RegisterRecordCallback(
3156 DNSServiceRef inSDRef
,
3157 DNSRecordRef inRecordRef
,
3158 DNSServiceFlags inFlags
,
3159 DNSServiceErrorType inError
,
3162 RegisterRecordContext
* context
= (RegisterRecordContext
*) inContext
;
3166 Unused( inRecordRef
);
3170 gettimeofday( &now
, NULL
);
3171 FPrintF( stdout
, "%{du:time} Record registration result (error %#m)\n", &now
, inError
);
3173 if( !context
->didRegister
&& !inError
)
3175 context
->didRegister
= true;
3176 if( context
->updateDataPtr
)
3178 if( context
->updateDelayMs
> 0 )
3180 dispatch_after_f( dispatch_time_milliseconds( context
->updateDelayMs
), dispatch_get_main_queue(),
3181 context
, RegisterRecordUpdate
);
3185 RegisterRecordUpdate( context
);
3188 if( context
->lifetimeMs
== 0 )
3190 Exit( kExitReason_TimeLimit
);
3192 else if( context
->lifetimeMs
> 0 )
3194 dispatch_after_f( dispatch_time_milliseconds( context
->lifetimeMs
), dispatch_get_main_queue(),
3195 kExitReason_TimeLimit
, Exit
);
3200 //===========================================================================================================================
3201 // RegisterRecordUpdate
3202 //===========================================================================================================================
3204 static void RegisterRecordUpdate( void *inContext
)
3207 RegisterRecordContext
* const context
= (RegisterRecordContext
*) inContext
;
3209 err
= DNSServiceUpdateRecord( context
->conRef
, context
->recordRef
, 0, (uint16_t) context
->updateDataLen
,
3210 context
->updateDataPtr
, context
->updateTTL
);
3211 require_noerr( err
, exit
);
3214 if( err
) exit( 1 );
3217 //===========================================================================================================================
3219 //===========================================================================================================================
3223 DNSServiceRef mainRef
; // Main sdRef for shared connections.
3224 DNSServiceRef opRef
; // sdRef for the DNSServiceResolve operation.
3225 DNSServiceFlags flags
; // Flags argument for DNSServiceResolve().
3226 const char * name
; // Service name argument for DNSServiceResolve().
3227 const char * type
; // Service type argument for DNSServiceResolve().
3228 const char * domain
; // Domain argument for DNSServiceResolve().
3229 uint32_t ifIndex
; // Interface index argument for DNSServiceResolve().
3230 int timeLimitSecs
; // Time limit for the DNSServiceResolve operation in seconds.
3234 static void ResolvePrintPrologue( const ResolveContext
*inContext
);
3235 static void ResolveContextFree( ResolveContext
*inContext
);
3236 static void DNSSD_API
3238 DNSServiceRef inSDRef
,
3239 DNSServiceFlags inFlags
,
3240 uint32_t inInterfaceIndex
,
3241 DNSServiceErrorType inError
,
3242 const char * inFullName
,
3243 const char * inHostname
,
3246 const unsigned char * inTXTPtr
,
3249 static void ResolveCmd( void )
3252 DNSServiceRef sdRef
;
3253 ResolveContext
* context
= NULL
;
3254 dispatch_source_t signalSource
= NULL
;
3255 int useMainConnection
;
3257 // Set up SIGINT handler.
3259 signal( SIGINT
, SIG_IGN
);
3260 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3261 require_noerr( err
, exit
);
3262 dispatch_resume( signalSource
);
3266 context
= (ResolveContext
*) calloc( 1, sizeof( *context
) );
3267 require_action( context
, exit
, err
= kNoMemoryErr
);
3269 // Check command parameters.
3271 if( gResolve_TimeLimitSecs
< 0 )
3273 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs
);
3278 // Create main connection.
3280 if( gConnectionOpt
)
3282 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
3283 require_noerr_quiet( err
, exit
);
3284 useMainConnection
= true;
3288 useMainConnection
= false;
3293 context
->flags
= GetDNSSDFlagsFromOpts();
3294 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
3296 // Get interface index.
3298 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3299 require_noerr_quiet( err
, exit
);
3301 // Set remaining parameters.
3303 context
->name
= gResolve_Name
;
3304 context
->type
= gResolve_Type
;
3305 context
->domain
= gResolve_Domain
;
3306 context
->timeLimitSecs
= gResolve_TimeLimitSecs
;
3310 ResolvePrintPrologue( context
);
3314 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
3315 err
= DNSServiceResolve( &sdRef
, context
->flags
, context
->ifIndex
, context
->name
, context
->type
, context
->domain
,
3316 ResolveCallback
, NULL
);
3317 require_noerr( err
, exit
);
3319 context
->opRef
= sdRef
;
3320 if( !useMainConnection
)
3322 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3323 require_noerr( err
, exit
);
3328 if( context
->timeLimitSecs
> 0 )
3330 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
3331 kExitReason_TimeLimit
, Exit
);
3336 dispatch_source_forget( &signalSource
);
3337 if( context
) ResolveContextFree( context
);
3338 if( err
) exit( 1 );
3341 //===========================================================================================================================
3343 //===========================================================================================================================
3345 static void ReconfirmCmd( void )
3348 uint8_t * rdataPtr
= NULL
;
3349 size_t rdataLen
= 0;
3350 DNSServiceFlags flags
;
3352 uint16_t type
, class;
3353 char ifName
[ kInterfaceNameBufLen
];
3357 flags
= GetDNSSDFlagsFromOpts();
3359 // Get interface index.
3361 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
3362 require_noerr_quiet( err
, exit
);
3366 err
= RecordTypeFromArgString( gReconfirmRecord_Type
, &type
);
3367 require_noerr( err
, exit
);
3371 if( gReconfirmRecord_Data
)
3373 err
= RecordDataFromArgString( gReconfirmRecord_Data
, &rdataPtr
, &rdataLen
);
3374 require_noerr_quiet( err
, exit
);
3377 // Get record class.
3379 if( gReconfirmRecord_Class
)
3381 err
= RecordClassFromArgString( gReconfirmRecord_Class
, &class );
3382 require_noerr( err
, exit
);
3386 class = kDNSServiceClass_IN
;
3391 FPrintF( stdout
, "Flags: %#{flags}\n", flags
, kDNSServiceFlagsDescriptors
);
3392 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
3393 FPrintF( stdout
, "Name: %s\n", gReconfirmRecord_Name
);
3394 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
3395 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
3396 FPrintF( stdout
, "Data: %#H\n", rdataPtr
, (int) rdataLen
, INT_MAX
);
3397 FPrintF( stdout
, "---\n" );
3399 err
= DNSServiceReconfirmRecord( flags
, ifIndex
, gReconfirmRecord_Name
, type
, class, (uint16_t) rdataLen
, rdataPtr
);
3400 FPrintF( stdout
, "Error: %#m\n", err
);
3403 FreeNullSafe( rdataPtr
);
3404 if( err
) exit( 1 );
3407 //===========================================================================================================================
3408 // ResolvePrintPrologue
3409 //===========================================================================================================================
3411 static void ResolvePrintPrologue( const ResolveContext
*inContext
)
3413 const int timeLimitSecs
= inContext
->timeLimitSecs
;
3414 char ifName
[ kInterfaceNameBufLen
];
3416 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3418 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3419 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3420 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
3421 FPrintF( stdout
, "Type: %s\n", inContext
->type
);
3422 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
);
3423 FPrintF( stdout
, "Time limit: " );
3424 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
3425 else FPrintF( stdout
, "∞\n" );
3426 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3427 FPrintF( stdout
, "---\n" );
3430 //===========================================================================================================================
3431 // ResolveContextFree
3432 //===========================================================================================================================
3434 static void ResolveContextFree( ResolveContext
*inContext
)
3436 DNSServiceForget( &inContext
->opRef
);
3437 DNSServiceForget( &inContext
->mainRef
);
3441 //===========================================================================================================================
3443 //===========================================================================================================================
3445 static void DNSSD_API
3447 DNSServiceRef inSDRef
,
3448 DNSServiceFlags inFlags
,
3449 uint32_t inInterfaceIndex
,
3450 DNSServiceErrorType inError
,
3451 const char * inFullName
,
3452 const char * inHostname
,
3455 const unsigned char * inTXTPtr
,
3459 char errorStr
[ 64 ];
3463 Unused( inContext
);
3465 gettimeofday( &now
, NULL
);
3467 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " error %#m", inError
);
3469 FPrintF( stdout
, "%{du:time}: %s can be reached at %s:%u (interface %d)%?s\n",
3470 &now
, inFullName
, inHostname
, ntohs( inPort
), (int32_t) inInterfaceIndex
, inError
, errorStr
);
3473 FPrintF( stdout
, " TXT record: %#H\n", inTXTPtr
, (int) inTXTLen
, INT_MAX
);
3477 FPrintF( stdout
, " TXT record: %#{txt}\n", inTXTPtr
, (size_t) inTXTLen
);
3481 //===========================================================================================================================
3482 // GetAddrInfoPOSIXCmd
3483 //===========================================================================================================================
3485 #define AddressFamilyStr( X ) ( \
3486 ( (X) == AF_INET ) ? "inet" : \
3487 ( (X) == AF_INET6 ) ? "inet6" : \
3488 ( (X) == AF_UNSPEC ) ? "unspec" : \
3498 #define CaseFlagStringify( X ) { (X), # X }
3500 const FlagStringPair kGAIPOSIXFlagStringPairs
[] =
3502 #if( defined( AI_UNUSABLE ) )
3503 CaseFlagStringify( AI_UNUSABLE
),
3505 CaseFlagStringify( AI_NUMERICSERV
),
3506 CaseFlagStringify( AI_V4MAPPED
),
3507 CaseFlagStringify( AI_ADDRCONFIG
),
3508 #if( defined( AI_V4MAPPED_CFG ) )
3509 CaseFlagStringify( AI_V4MAPPED_CFG
),
3511 CaseFlagStringify( AI_ALL
),
3512 CaseFlagStringify( AI_NUMERICHOST
),
3513 CaseFlagStringify( AI_CANONNAME
),
3514 CaseFlagStringify( AI_PASSIVE
),
3518 static void GetAddrInfoPOSIXCmd( void )
3521 struct addrinfo hints
;
3523 const struct addrinfo
* addrInfo
;
3524 struct addrinfo
* addrInfoList
= NULL
;
3525 const FlagStringPair
* pair
;
3527 memset( &hints
, 0, sizeof( hints
) );
3528 hints
.ai_socktype
= SOCK_STREAM
;
3530 // Set hints address family.
3532 if( !gGAIPOSIX_Family
) hints
.ai_family
= AF_UNSPEC
;
3533 else if( strcasecmp( gGAIPOSIX_Family
, "inet" ) == 0 ) hints
.ai_family
= AF_INET
;
3534 else if( strcasecmp( gGAIPOSIX_Family
, "inet6" ) == 0 ) hints
.ai_family
= AF_INET6
;
3535 else if( strcasecmp( gGAIPOSIX_Family
, "unspec" ) == 0 ) hints
.ai_family
= AF_UNSPEC
;
3538 FPrintF( stderr
, "Invalid address family: %s.\n", gGAIPOSIX_Family
);
3545 if( gGAIPOSIXFlag_AddrConfig
) hints
.ai_flags
|= AI_ADDRCONFIG
;
3546 if( gGAIPOSIXFlag_All
) hints
.ai_flags
|= AI_ALL
;
3547 if( gGAIPOSIXFlag_CanonName
) hints
.ai_flags
|= AI_CANONNAME
;
3548 if( gGAIPOSIXFlag_NumericHost
) hints
.ai_flags
|= AI_NUMERICHOST
;
3549 if( gGAIPOSIXFlag_NumericServ
) hints
.ai_flags
|= AI_NUMERICSERV
;
3550 if( gGAIPOSIXFlag_Passive
) hints
.ai_flags
|= AI_PASSIVE
;
3551 if( gGAIPOSIXFlag_V4Mapped
) hints
.ai_flags
|= AI_V4MAPPED
;
3552 #if( defined( AI_V4MAPPED_CFG ) )
3553 if( gGAIPOSIXFlag_V4MappedCFG
) hints
.ai_flags
|= AI_V4MAPPED_CFG
;
3555 #if( defined( AI_DEFAULT ) )
3556 if( gGAIPOSIXFlag_Default
) hints
.ai_flags
|= AI_DEFAULT
;
3558 #if( defined( AI_UNUSABLE ) )
3559 if( gGAIPOSIXFlag_Unusable
) hints
.ai_flags
|= AI_UNUSABLE
;
3564 FPrintF( stdout
, "Hostname: %s\n", gGAIPOSIX_HostName
);
3565 FPrintF( stdout
, "Servname: %s\n", gGAIPOSIX_ServName
);
3566 FPrintF( stdout
, "Address family: %s\n", AddressFamilyStr( hints
.ai_family
) );
3567 FPrintF( stdout
, "Flags: 0x%X < ", hints
.ai_flags
);
3568 for( pair
= kGAIPOSIXFlagStringPairs
; pair
->str
!= NULL
; ++pair
)
3570 if( ( (unsigned int) hints
.ai_flags
) & pair
->flag
) FPrintF( stdout
, "%s ", pair
->str
);
3572 FPrintF( stdout
, ">\n" );
3573 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3574 FPrintF( stdout
, "---\n" );
3576 // Call getaddrinfo().
3578 err
= getaddrinfo( gGAIPOSIX_HostName
, gGAIPOSIX_ServName
, &hints
, &addrInfoList
);
3579 gettimeofday( &now
, NULL
);
3582 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
3588 for( addrInfo
= addrInfoList
; addrInfo
; addrInfo
= addrInfo
->ai_next
) { ++addrCount
; }
3590 FPrintF( stdout
, "Addresses (%d total):\n", addrCount
);
3591 for( addrInfo
= addrInfoList
; addrInfo
; addrInfo
= addrInfo
->ai_next
)
3593 FPrintF( stdout
, "%##a\n", addrInfo
->ai_addr
);
3596 FPrintF( stdout
, "---\n" );
3597 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
3600 if( addrInfoList
) freeaddrinfo( addrInfoList
);
3601 if( err
) exit( 1 );
3604 //===========================================================================================================================
3606 //===========================================================================================================================
3608 static void ReverseLookupCmd( void )
3611 QueryRecordContext
* context
= NULL
;
3612 DNSServiceRef sdRef
;
3613 dispatch_source_t signalSource
= NULL
;
3615 uint8_t ipv6Addr
[ 16 ];
3616 char recordName
[ ( 16 * 4 ) + 9 + 1 ];
3617 int useMainConnection
;
3618 const char * endPtr
;
3620 // Set up SIGINT handler.
3622 signal( SIGINT
, SIG_IGN
);
3623 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3624 require_noerr( err
, exit
);
3625 dispatch_resume( signalSource
);
3629 context
= (QueryRecordContext
*) calloc( 1, sizeof( *context
) );
3630 require_action( context
, exit
, err
= kNoMemoryErr
);
3632 // Check command parameters.
3634 if( gReverseLookup_TimeLimitSecs
< 0 )
3636 FPrintF( stderr
, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs
);
3641 // Create main connection.
3643 if( gConnectionOpt
)
3645 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
3646 require_noerr_quiet( err
, exit
);
3647 useMainConnection
= true;
3651 useMainConnection
= false;
3656 context
->flags
= GetDNSSDFlagsFromOpts();
3657 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
3659 // Get interface index.
3661 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3662 require_noerr_quiet( err
, exit
);
3664 // Create reverse lookup record name.
3666 err
= StringToIPv4Address( gReverseLookup_IPAddr
, kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
,
3667 &ipv4Addr
, NULL
, NULL
, NULL
, &endPtr
);
3668 if( err
|| ( *endPtr
!= '\0' ) )
3673 err
= StringToIPv6Address( gReverseLookup_IPAddr
,
3674 kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
| kStringToIPAddressFlagsNoScope
,
3675 ipv6Addr
, NULL
, NULL
, NULL
, &endPtr
);
3676 if( err
|| ( *endPtr
!= '\0' ) )
3678 FPrintF( stderr
, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr
);
3683 for( i
= 15; i
>= 0; --i
)
3685 *dst
++ = kHexDigitsLowercase
[ ipv6Addr
[ i
] & 0x0F ];
3687 *dst
++ = kHexDigitsLowercase
[ ipv6Addr
[ i
] >> 4 ];
3690 strcpy( dst
, "ip6.arpa." );
3691 check( ( strlen( recordName
) + 1 ) <= sizeof( recordName
) );
3695 SNPrintF( recordName
, sizeof( recordName
), "%u.%u.%u.%u.in-addr.arpa.",
3697 ( ipv4Addr
>> 8 ) & 0xFF,
3698 ( ipv4Addr
>> 16 ) & 0xFF,
3699 ( ipv4Addr
>> 24 ) & 0xFF );
3702 // Set remaining parameters.
3704 context
->recordName
= recordName
;
3705 context
->recordType
= kDNSServiceType_PTR
;
3706 context
->timeLimitSecs
= gReverseLookup_TimeLimitSecs
;
3707 context
->oneShotMode
= gReverseLookup_OneShot
? true : false;
3711 QueryRecordPrintPrologue( context
);
3715 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
3716 err
= DNSServiceQueryRecord( &sdRef
, context
->flags
, context
->ifIndex
, context
->recordName
, context
->recordType
,
3717 kDNSServiceClass_IN
, QueryRecordCallback
, context
);
3718 require_noerr( err
, exit
);
3720 context
->opRef
= sdRef
;
3721 if( !useMainConnection
)
3723 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3724 require_noerr( err
, exit
);
3729 if( context
->timeLimitSecs
> 0 )
3731 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
3732 kExitReason_TimeLimit
, Exit
);
3737 dispatch_source_forget( &signalSource
);
3738 if( context
) QueryRecordContextFree( context
);
3739 if( err
) exit( 1 );
3742 //===========================================================================================================================
3744 //===========================================================================================================================
3748 DNSServiceRef mainRef
; // Main sdRef for shared connection.
3749 DNSServiceRef opRef
; // sdRef for the DNSServiceNATPortMappingCreate operation.
3750 DNSServiceFlags flags
; // Flags for DNSServiceNATPortMappingCreate operation.
3751 uint32_t ifIndex
; // Interface index argument for DNSServiceNATPortMappingCreate operation.
3752 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceNATPortMappingCreate operation.
3753 uint32_t ttl
; // TTL argument for DNSServiceNATPortMappingCreate operation.
3754 uint16_t internalPort
; // Internal port argument for DNSServiceNATPortMappingCreate operation.
3755 uint16_t externalPort
; // External port argument for DNSServiceNATPortMappingCreate operation.
3756 Boolean printedHeader
; // True if results header was printed.
3758 } PortMappingContext
;
3760 static void PortMappingPrintPrologue( const PortMappingContext
*inContext
);
3761 static void PortMappingContextFree( PortMappingContext
*inContext
);
3762 static void DNSSD_API
3763 PortMappingCallback(
3764 DNSServiceRef inSDRef
,
3765 DNSServiceFlags inFlags
,
3766 uint32_t inInterfaceIndex
,
3767 DNSServiceErrorType inError
,
3768 uint32_t inExternalIPv4Address
,
3769 DNSServiceProtocol inProtocol
,
3770 uint16_t inInternalPort
,
3771 uint16_t inExternalPort
,
3775 static void PortMappingCmd( void )
3778 PortMappingContext
* context
= NULL
;
3779 DNSServiceRef sdRef
;
3780 dispatch_source_t signalSource
= NULL
;
3781 int useMainConnection
;
3783 // Set up SIGINT handler.
3785 signal( SIGINT
, SIG_IGN
);
3786 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3787 require_noerr( err
, exit
);
3788 dispatch_resume( signalSource
);
3792 context
= (PortMappingContext
*) calloc( 1, sizeof( *context
) );
3793 require_action( context
, exit
, err
= kNoMemoryErr
);
3795 // Check command parameters.
3797 if( ( gPortMapping_InternalPort
< 0 ) || ( gPortMapping_InternalPort
> UINT16_MAX
) )
3799 FPrintF( stderr
, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort
);
3804 if( ( gPortMapping_ExternalPort
< 0 ) || ( gPortMapping_ExternalPort
> UINT16_MAX
) )
3806 FPrintF( stderr
, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort
);
3811 // Create main connection.
3813 if( gConnectionOpt
)
3815 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
3816 require_noerr_quiet( err
, exit
);
3817 useMainConnection
= true;
3821 useMainConnection
= false;
3826 context
->flags
= GetDNSSDFlagsFromOpts();
3827 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
3829 // Get interface index.
3831 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3832 require_noerr_quiet( err
, exit
);
3834 // Set remaining parameters.
3836 if( gPortMapping_ProtocolTCP
) context
->protocols
|= kDNSServiceProtocol_TCP
;
3837 if( gPortMapping_ProtocolUDP
) context
->protocols
|= kDNSServiceProtocol_UDP
;
3838 context
->ttl
= (uint32_t) gPortMapping_TTL
;
3839 context
->internalPort
= (uint16_t) gPortMapping_InternalPort
;
3840 context
->externalPort
= (uint16_t) gPortMapping_ExternalPort
;
3844 PortMappingPrintPrologue( context
);
3848 sdRef
= useMainConnection
? context
->mainRef
: kBadDNSServiceRef
;
3849 err
= DNSServiceNATPortMappingCreate( &sdRef
, context
->flags
, context
->ifIndex
, context
->protocols
,
3850 htons( context
->internalPort
), htons( context
->externalPort
), context
->ttl
, PortMappingCallback
, context
);
3851 require_noerr( err
, exit
);
3853 context
->opRef
= sdRef
;
3854 if( !useMainConnection
)
3856 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3857 require_noerr( err
, exit
);
3863 dispatch_source_forget( &signalSource
);
3864 if( context
) PortMappingContextFree( context
);
3865 if( err
) exit( 1 );
3868 //===========================================================================================================================
3869 // PortMappingPrintPrologue
3870 //===========================================================================================================================
3872 static void PortMappingPrintPrologue( const PortMappingContext
*inContext
)
3874 char ifName
[ kInterfaceNameBufLen
];
3876 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3878 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3879 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3880 FPrintF( stdout
, "Protocols: %#{flags}\n", inContext
->protocols
, kDNSServiceProtocolDescriptors
);
3881 FPrintF( stdout
, "Internal Port: %u\n", inContext
->internalPort
);
3882 FPrintF( stdout
, "External Port: %u\n", inContext
->externalPort
);
3883 FPrintF( stdout
, "TTL: %u%?s\n", inContext
->ttl
, !inContext
->ttl
,
3884 " (system will use a default value.)" );
3885 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
3886 FPrintF( stdout
, "---\n" );
3890 //===========================================================================================================================
3891 // PortMappingContextFree
3892 //===========================================================================================================================
3894 static void PortMappingContextFree( PortMappingContext
*inContext
)
3896 DNSServiceForget( &inContext
->opRef
);
3897 DNSServiceForget( &inContext
->mainRef
);
3901 //===========================================================================================================================
3902 // PortMappingCallback
3903 //===========================================================================================================================
3905 static void DNSSD_API
3906 PortMappingCallback(
3907 DNSServiceRef inSDRef
,
3908 DNSServiceFlags inFlags
,
3909 uint32_t inInterfaceIndex
,
3910 DNSServiceErrorType inError
,
3911 uint32_t inExternalIPv4Address
,
3912 DNSServiceProtocol inProtocol
,
3913 uint16_t inInternalPort
,
3914 uint16_t inExternalPort
,
3918 PortMappingContext
* const context
= (PortMappingContext
*) inContext
;
3920 char errorStr
[ 128 ];
3925 gettimeofday( &now
, NULL
);
3927 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " (error: %#m)", inError
);
3928 if( !context
->printedHeader
)
3930 FPrintF( stdout
, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" );
3931 context
->printedHeader
= true;
3933 FPrintF( stdout
, "%{du:time} %2u %7u %15.4a %7u %6u %#{flags}%?s\n",
3934 &now
, inInterfaceIndex
, ntohs( inInternalPort
), &inExternalIPv4Address
, ntohs( inExternalPort
), inTTL
,
3935 inProtocol
, kDNSServiceProtocolDescriptors
, inError
, errorStr
);
3938 //===========================================================================================================================
3940 //===========================================================================================================================
3942 typedef struct BrowseDomain BrowseDomain
;
3943 typedef struct BrowseType BrowseType
;
3944 typedef struct BrowseOp BrowseOp
;
3945 typedef struct BrowseInstance BrowseInstance
;
3946 typedef struct BrowseIPAddr BrowseIPAddr
;
3951 DNSServiceRef mainRef
;
3952 DNSServiceRef domainsQuery
;
3953 const char * domain
;
3954 BrowseDomain
* domainList
;
3955 char ** serviceTypes
;
3956 size_t serviceTypesCount
;
3957 dispatch_source_t exitTimer
;
3959 int pendingConnectCount
;
3961 int maxConnectTimeSecs
;
3962 Boolean includeAWDL
;
3963 Boolean useColoredText
;
3969 BrowseDomain
* next
;
3971 DNSServiceRef servicesQuery
;
3972 BrowseAllContext
* context
;
3973 BrowseType
* typeList
;
3980 BrowseOp
* browseList
;
3986 BrowseAllContext
* context
;
3987 DNSServiceRef browse
;
3988 uint64_t startTicks
;
3989 BrowseInstance
* instanceList
;
3994 struct BrowseInstance
3996 BrowseInstance
* next
;
3997 BrowseAllContext
* context
;
3999 uint64_t foundTicks
;
4000 DNSServiceRef resolve
;
4001 uint64_t resolveStartTicks
;
4002 uint64_t resolveDoneTicks
;
4003 DNSServiceRef getAddr
;
4004 uint64_t getAddrStartTicks
;
4005 BrowseIPAddr
* addrList
;
4016 kConnectStatus_None
= 0,
4017 kConnectStatus_Pending
= 1,
4018 kConnectStatus_Succeeded
= 2,
4019 kConnectStatus_Failed
= 3
4025 BrowseIPAddr
* next
;
4028 BrowseAllContext
* context
;
4029 uint64_t foundTicks
;
4030 AsyncConnectionRef connection
;
4031 ConnectStatus connectStatus
;
4032 CFTimeInterval connectTimeSecs
;
4033 OSStatus connectError
;
4036 static void BrowseAllPrintPrologue( const BrowseAllContext
*inContext
);
4037 static void DNSSD_API
4038 BrowseAllQueryDomainsCallback(
4039 DNSServiceRef inSDRef
,
4040 DNSServiceFlags inFlags
,
4041 uint32_t inInterfaceIndex
,
4042 DNSServiceErrorType inError
,
4043 const char * inFullName
,
4046 uint16_t inRDataLen
,
4047 const void * inRDataPtr
,
4050 static void DNSSD_API
4051 BrowseAllQueryCallback(
4052 DNSServiceRef inSDRef
,
4053 DNSServiceFlags inFlags
,
4054 uint32_t inInterfaceIndex
,
4055 DNSServiceErrorType inError
,
4056 const char * inFullName
,
4059 uint16_t inRDataLen
,
4060 const void * inRDataPtr
,
4063 static void DNSSD_API
4064 BrowseAllBrowseCallback(
4065 DNSServiceRef inSDRef
,
4066 DNSServiceFlags inFlags
,
4067 uint32_t inInterfaceIndex
,
4068 DNSServiceErrorType inError
,
4069 const char * inName
,
4070 const char * inRegType
,
4071 const char * inDomain
,
4073 static void DNSSD_API
4074 BrowseAllResolveCallback(
4075 DNSServiceRef inSDRef
,
4076 DNSServiceFlags inFlags
,
4077 uint32_t inInterfaceIndex
,
4078 DNSServiceErrorType inError
,
4079 const char * inFullName
,
4080 const char * inHostname
,
4083 const unsigned char * inTXTPtr
,
4085 static void DNSSD_API
4086 BrowseAllGAICallback(
4087 DNSServiceRef inSDRef
,
4088 DNSServiceFlags inFlags
,
4089 uint32_t inInterfaceIndex
,
4090 DNSServiceErrorType inError
,
4091 const char * inHostname
,
4092 const struct sockaddr
* inSockAddr
,
4095 static void BrowseAllConnectionProgress( int inPhase
, const void *inDetails
, void *inArg
);
4096 static void BrowseAllConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
);
4097 static void BrowseAllStop( void *inContext
);
4098 static void BrowseAllExit( void *inContext
);
4099 static OSStatus
BrowseAllAddDomain( BrowseAllContext
*inContext
, const char *inName
);
4100 static OSStatus
BrowseAllRemoveDomain( BrowseAllContext
*inContext
, const char *inName
);
4101 static void BrowseAllContextRelease( BrowseAllContext
*inContext
);
4103 BrowseAllAddServiceType(
4104 BrowseAllContext
* inContext
,
4105 BrowseDomain
* inDomain
,
4106 const char * inName
,
4108 Boolean inIncludeAWDL
);
4110 BrowseAllRemoveServiceType(
4111 BrowseAllContext
* inContext
,
4112 BrowseDomain
* inDomain
,
4113 const char * inName
,
4114 uint32_t inIfIndex
);
4116 BrowseAllAddServiceInstance(
4117 BrowseAllContext
* inContext
,
4118 BrowseOp
* inBrowse
,
4119 const char * inName
,
4120 const char * inRegType
,
4121 const char * inDomain
,
4122 uint32_t inIfIndex
);
4124 BrowseAllRemoveServiceInstance(
4125 BrowseAllContext
* inContext
,
4126 BrowseOp
* inBrowse
,
4127 const char * inName
,
4128 uint32_t inIfIndex
);
4130 BrowseAllAddIPAddress(
4131 BrowseAllContext
* inContext
,
4132 BrowseInstance
* inInstance
,
4133 const struct sockaddr
* inSockAddr
);
4135 BrowseAllRemoveIPAddress(
4136 BrowseAllContext
* inContext
,
4137 BrowseInstance
* inInstance
,
4138 const struct sockaddr
* inSockAddr
);
4139 static void BrowseDomainFree( BrowseDomain
*inDomain
);
4140 static void BrowseTypeFree( BrowseType
*inType
);
4141 static void BrowseOpFree( BrowseOp
*inBrowse
);
4142 static void BrowseInstanceFree( BrowseInstance
*inInstance
);
4143 static void BrowseIPAddrRelease( BrowseIPAddr
*inAddr
);
4144 static void BrowseIPAddrReleaseList( BrowseIPAddr
*inList
);
4146 #define ForgetIPAddressList( X ) ForgetCustom( X, BrowseIPAddrReleaseList )
4147 #define ForgetBrowseAllContext( X ) ForgetCustom( X, BrowseAllContextRelease )
4149 #define kBrowseAllOpenFileMin 4096
4151 static void BrowseAllCmd( void )
4154 BrowseAllContext
* context
= NULL
;
4156 // Check command parameters.
4158 if( gBrowseAll_BrowseTimeSecs
<= 0 )
4160 FPrintF( stdout
, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs
);
4165 #if( TARGET_OS_POSIX )
4166 // Set open file minimum.
4169 struct rlimit fdLimits
;
4171 err
= getrlimit( RLIMIT_NOFILE
, &fdLimits
);
4172 err
= map_global_noerr_errno( err
);
4173 require_noerr( err
, exit
);
4175 if( fdLimits
.rlim_cur
< kBrowseAllOpenFileMin
)
4177 fdLimits
.rlim_cur
= kBrowseAllOpenFileMin
;
4178 err
= setrlimit( RLIMIT_NOFILE
, &fdLimits
);
4179 err
= map_global_noerr_errno( err
);
4180 require_noerr( err
, exit
);
4185 context
= (BrowseAllContext
*) calloc( 1, sizeof( *context
) );
4186 require_action( context
, exit
, err
= kNoMemoryErr
);
4188 context
->refCount
= 1;
4189 context
->domain
= gBrowseAll_Domain
;
4190 context
->serviceTypes
= gBrowseAll_ServiceTypes
;
4191 context
->serviceTypesCount
= gBrowseAll_ServiceTypesCount
;
4192 gBrowseAll_ServiceTypes
= NULL
;
4193 gBrowseAll_ServiceTypesCount
= 0;
4194 context
->browseTimeSecs
= gBrowseAll_BrowseTimeSecs
;
4195 context
->maxConnectTimeSecs
= gBrowseAll_MaxConnectTimeSecs
;
4196 context
->includeAWDL
= gDNSSDFlag_IncludeAWDL
? true : false;
4197 #if( TARGET_OS_POSIX )
4198 context
->useColoredText
= isatty( STDOUT_FILENO
) ? true : false;
4201 err
= DNSServiceCreateConnection( &context
->mainRef
);
4202 require_noerr( err
, exit
);
4204 err
= DNSServiceSetDispatchQueue( context
->mainRef
, dispatch_get_main_queue() );
4205 require_noerr( err
, exit
);
4207 // Set interface index.
4209 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
4210 require_noerr_quiet( err
, exit
);
4212 BrowseAllPrintPrologue( context
);
4214 if( context
->domain
)
4216 err
= BrowseAllAddDomain( context
, context
->domain
);
4217 require_noerr( err
, exit
);
4221 DNSServiceRef sdRef
;
4223 sdRef
= context
->mainRef
;
4224 err
= DNSServiceQueryRecord( &sdRef
, kDNSServiceFlagsShareConnection
, kDNSServiceInterfaceIndexLocalOnly
,
4225 "b._dns-sd._udp.local.", kDNSServiceType_PTR
, kDNSServiceClass_IN
, BrowseAllQueryDomainsCallback
, context
);
4226 require_noerr( err
, exit
);
4228 context
->domainsQuery
= sdRef
;
4231 dispatch_after_f( dispatch_time_seconds( context
->browseTimeSecs
), dispatch_get_main_queue(), context
, BrowseAllStop
);
4235 if( context
) BrowseAllContextRelease( context
);
4236 if( err
) exit( 1 );
4239 //===========================================================================================================================
4240 // BrowseAllPrintPrologue
4241 //===========================================================================================================================
4243 static void BrowseAllPrintPrologue( const BrowseAllContext
*inContext
)
4246 char ifName
[ kInterfaceNameBufLen
];
4248 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
4250 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
4251 FPrintF( stdout
, "Service types: ");
4252 if( inContext
->serviceTypesCount
> 0 )
4254 FPrintF( stdout
, "%s", inContext
->serviceTypes
[ 0 ] );
4255 for( i
= 1; i
< inContext
->serviceTypesCount
; ++i
) FPrintF( stdout
, ", %s", inContext
->serviceTypes
[ i
] );
4256 FPrintF( stdout
, "\n" );
4260 FPrintF( stdout
, "all services\n" );
4262 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "default domains" );
4263 FPrintF( stdout
, "Browse time: %d second%?c\n", inContext
->browseTimeSecs
, inContext
->browseTimeSecs
!= 1, 's' );
4264 FPrintF( stdout
, "Max connect time: %d second%?c\n",
4265 inContext
->maxConnectTimeSecs
, inContext
->maxConnectTimeSecs
!= 1, 's' );
4266 FPrintF( stdout
, "IncludeAWDL: %s\n", inContext
->includeAWDL
? "YES" : "NO" );
4267 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
4268 FPrintF( stdout
, "---\n" );
4271 //===========================================================================================================================
4272 // BrowseAllQueryDomainsCallback
4273 //===========================================================================================================================
4275 static void DNSSD_API
4276 BrowseAllQueryDomainsCallback(
4277 DNSServiceRef inSDRef
,
4278 DNSServiceFlags inFlags
,
4279 uint32_t inInterfaceIndex
,
4280 DNSServiceErrorType inError
,
4281 const char * inFullName
,
4284 uint16_t inRDataLen
,
4285 const void * inRDataPtr
,
4290 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
4291 char domainStr
[ kDNSServiceMaxDomainName
];
4294 Unused( inInterfaceIndex
);
4295 Unused( inFullName
);
4301 require_noerr( err
, exit
);
4303 err
= DomainNameToString( inRDataPtr
, ( (const uint8_t *) inRDataPtr
) + inRDataLen
, domainStr
, NULL
);
4304 require_noerr( err
, exit
);
4306 if( inFlags
& kDNSServiceFlagsAdd
)
4308 err
= BrowseAllAddDomain( context
, domainStr
);
4309 if( err
== kDuplicateErr
) err
= kNoErr
;
4310 require_noerr( err
, exit
);
4314 err
= BrowseAllRemoveDomain( context
, domainStr
);
4315 if( err
== kNotFoundErr
) err
= kNoErr
;
4316 require_noerr( err
, exit
);
4320 if( err
) exit( 1 );
4323 //===========================================================================================================================
4324 // BrowseAllQueryCallback
4325 //===========================================================================================================================
4327 static void DNSSD_API
4328 BrowseAllQueryCallback(
4329 DNSServiceRef inSDRef
,
4330 DNSServiceFlags inFlags
,
4331 uint32_t inInterfaceIndex
,
4332 DNSServiceErrorType inError
,
4333 const char * inFullName
,
4336 uint16_t inRDataLen
,
4337 const void * inRDataPtr
,
4342 BrowseDomain
* const domain
= (BrowseDomain
*) inContext
;
4343 const uint8_t * firstLabel
;
4344 const uint8_t * secondLabel
;
4345 char * serviceTypeStr
= NULL
;
4346 const uint8_t * const end
= ( (uint8_t * ) inRDataPtr
) + inRDataLen
;
4349 Unused( inFullName
);
4355 require_noerr( err
, exit
);
4357 check( inType
== kDNSServiceType_PTR
);
4358 check( inClass
== kDNSServiceClass_IN
);
4359 require_action( inRDataLen
> 0, exit
, err
= kSizeErr
);
4361 firstLabel
= inRDataPtr
;
4362 require_action_quiet( ( firstLabel
+ 1 + firstLabel
[ 0 ] ) < end
, exit
, err
= kUnderrunErr
);
4364 secondLabel
= firstLabel
+ 1 + firstLabel
[ 0 ];
4365 require_action_quiet( ( secondLabel
+ 1 + secondLabel
[ 0 ] ) < end
, exit
, err
= kUnderrunErr
);
4367 ASPrintF( &serviceTypeStr
, "%#s.%#s", firstLabel
, secondLabel
);
4368 require_action( serviceTypeStr
, exit
, err
= kNoMemoryErr
);
4370 if( inFlags
& kDNSServiceFlagsAdd
)
4372 err
= BrowseAllAddServiceType( domain
->context
, domain
, serviceTypeStr
, inInterfaceIndex
, false );
4373 if( err
== kDuplicateErr
) err
= kNoErr
;
4374 require_noerr( err
, exit
);
4378 err
= BrowseAllRemoveServiceType( domain
->context
, domain
, serviceTypeStr
, inInterfaceIndex
);
4379 if( err
== kNotFoundErr
) err
= kNoErr
;
4380 require_noerr( err
, exit
);
4384 FreeNullSafe( serviceTypeStr
);
4387 //===========================================================================================================================
4388 // BrowseAllBrowseCallback
4389 //===========================================================================================================================
4391 static void DNSSD_API
4392 BrowseAllBrowseCallback(
4393 DNSServiceRef inSDRef
,
4394 DNSServiceFlags inFlags
,
4395 uint32_t inInterfaceIndex
,
4396 DNSServiceErrorType inError
,
4397 const char * inName
,
4398 const char * inRegType
,
4399 const char * inDomain
,
4403 BrowseOp
* const browse
= (BrowseOp
*) inContext
;
4408 require_noerr( err
, exit
);
4410 if( inFlags
& kDNSServiceFlagsAdd
)
4412 err
= BrowseAllAddServiceInstance( browse
->context
, browse
, inName
, inRegType
, inDomain
, inInterfaceIndex
);
4413 if( err
== kDuplicateErr
) err
= kNoErr
;
4414 require_noerr( err
, exit
);
4418 err
= BrowseAllRemoveServiceInstance( browse
->context
, browse
, inName
, inInterfaceIndex
);
4419 if( err
== kNotFoundErr
) err
= kNoErr
;
4420 require_noerr( err
, exit
);
4427 //===========================================================================================================================
4428 // BrowseAllResolveCallback
4429 //===========================================================================================================================
4431 static void DNSSD_API
4432 BrowseAllResolveCallback(
4433 DNSServiceRef inSDRef
,
4434 DNSServiceFlags inFlags
,
4435 uint32_t inInterfaceIndex
,
4436 DNSServiceErrorType inError
,
4437 const char * inFullName
,
4438 const char * inHostname
,
4441 const unsigned char * inTXTPtr
,
4445 const uint64_t nowTicks
= UpTicks();
4446 BrowseInstance
* const instance
= (BrowseInstance
*) inContext
;
4450 Unused( inInterfaceIndex
);
4451 Unused( inFullName
);
4454 require_noerr( err
, exit
);
4456 if( !MemEqual( instance
->txtPtr
, instance
->txtLen
, inTXTPtr
, inTXTLen
) )
4458 FreeNullSafe( instance
->txtPtr
);
4459 instance
->txtPtr
= malloc( inTXTLen
);
4460 require_action( instance
->txtPtr
, exit
, err
= kNoMemoryErr
);
4462 memcpy( instance
->txtPtr
, inTXTPtr
, inTXTLen
);
4463 instance
->txtLen
= inTXTLen
;
4466 instance
->port
= ntohs( inPort
);
4468 if( !instance
->hostname
|| ( strcasecmp( instance
->hostname
, inHostname
) != 0 ) )
4470 DNSServiceRef sdRef
;
4472 if( !instance
->hostname
) instance
->resolveDoneTicks
= nowTicks
;
4473 FreeNullSafe( instance
->hostname
);
4474 instance
->hostname
= strdup( inHostname
);
4475 require_action( instance
->hostname
, exit
, err
= kNoMemoryErr
);
4477 DNSServiceForget( &instance
->getAddr
);
4478 ForgetIPAddressList( &instance
->addrList
);
4480 sdRef
= instance
->context
->mainRef
;
4481 instance
->getAddrStartTicks
= UpTicks();
4482 err
= DNSServiceGetAddrInfo( &sdRef
, kDNSServiceFlagsShareConnection
, instance
->ifIndex
,
4483 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, instance
->hostname
, BrowseAllGAICallback
, instance
);
4484 require_noerr( err
, exit
);
4486 instance
->getAddr
= sdRef
;
4490 if( err
) exit( 1 );
4493 //===========================================================================================================================
4494 // BrowseAllGAICallback
4495 //===========================================================================================================================
4497 static void DNSSD_API
4498 BrowseAllGAICallback(
4499 DNSServiceRef inSDRef
,
4500 DNSServiceFlags inFlags
,
4501 uint32_t inInterfaceIndex
,
4502 DNSServiceErrorType inError
,
4503 const char * inHostname
,
4504 const struct sockaddr
* inSockAddr
,
4509 BrowseInstance
* const instance
= (BrowseInstance
*) inContext
;
4512 Unused( inInterfaceIndex
);
4513 Unused( inHostname
);
4517 require_noerr( err
, exit
);
4519 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
4521 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
4525 if( inFlags
& kDNSServiceFlagsAdd
)
4527 err
= BrowseAllAddIPAddress( instance
->context
, instance
, inSockAddr
);
4528 if( err
== kDuplicateErr
) err
= kNoErr
;
4529 require_noerr( err
, exit
);
4533 err
= BrowseAllRemoveIPAddress( instance
->context
, instance
, inSockAddr
);
4534 if( err
== kNotFoundErr
) err
= kNoErr
;
4535 require_noerr( err
, exit
);
4542 //===========================================================================================================================
4543 // BrowseAllConnectionProgress
4544 //===========================================================================================================================
4546 static void BrowseAllConnectionProgress( int inPhase
, const void *inDetails
, void *inArg
)
4548 BrowseIPAddr
* const addr
= (BrowseIPAddr
*) inArg
;
4550 if( inPhase
== kAsyncConnectionPhase_Connected
)
4552 const AsyncConnectedInfo
* const info
= (AsyncConnectedInfo
*) inDetails
;
4554 addr
->connectTimeSecs
= info
->connectSecs
;
4558 //===========================================================================================================================
4559 // BrowseAllConnectionHandler
4560 //===========================================================================================================================
4562 static void BrowseAllConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
)
4564 BrowseIPAddr
* const addr
= (BrowseIPAddr
*) inArg
;
4565 BrowseAllContext
* const context
= addr
->context
;
4569 addr
->connectStatus
= kConnectStatus_Failed
;
4570 addr
->connectError
= inError
;
4574 addr
->connectStatus
= kConnectStatus_Succeeded
;
4577 check( context
->pendingConnectCount
> 0 );
4578 if( --context
->pendingConnectCount
== 0 )
4580 if( context
->exitTimer
)
4582 dispatch_source_forget( &context
->exitTimer
);
4583 dispatch_async_f( dispatch_get_main_queue(), context
, BrowseAllExit
);
4587 ForgetSocket( &inSock
);
4588 BrowseIPAddrRelease( addr
);
4591 //===========================================================================================================================
4593 //===========================================================================================================================
4595 static void BrowseAllStop( void *inContext
)
4598 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
4599 BrowseDomain
* domain
;
4602 BrowseInstance
* instance
;
4604 DNSServiceForget( &context
->domainsQuery
);
4605 for( domain
= context
->domainList
; domain
; domain
= domain
->next
)
4607 DNSServiceForget( &domain
->servicesQuery
);
4608 for( type
= domain
->typeList
; type
; type
= type
->next
)
4610 for( browse
= type
->browseList
; browse
; browse
= browse
->next
)
4612 DNSServiceForget( &browse
->browse
);
4613 for( instance
= browse
->instanceList
; instance
; instance
= instance
->next
)
4615 DNSServiceForget( &instance
->resolve
);
4616 DNSServiceForget( &instance
->getAddr
);
4621 DNSServiceForget( &context
->mainRef
);
4623 if( ( context
->pendingConnectCount
> 0 ) && ( context
->maxConnectTimeSecs
> 0 ) )
4625 check( !context
->exitTimer
);
4626 err
= DispatchTimerCreate( dispatch_time_seconds( context
->maxConnectTimeSecs
), DISPATCH_TIME_FOREVER
,
4627 100 * kNanosecondsPerMillisecond
, NULL
, BrowseAllExit
, NULL
, context
, &context
->exitTimer
);
4628 require_noerr( err
, exit
);
4629 dispatch_resume( context
->exitTimer
);
4633 dispatch_async_f( dispatch_get_main_queue(), context
, BrowseAllExit
);
4638 if( err
) exit( 1 );
4641 //===========================================================================================================================
4643 //===========================================================================================================================
4645 #define kStatusStr_CouldConnect "connected"
4646 #define kStatusStr_CouldConnectColored kANSIGreen kStatusStr_CouldConnect kANSINormal
4647 #define kStatusStr_CouldNotConnect "could not connect"
4648 #define kStatusStr_CouldNotConnectColored kANSIRed kStatusStr_CouldNotConnect kANSINormal
4649 #define kStatusStr_NoConnectionAttempted "no connection attempted"
4650 #define kStatusStr_Unknown "unknown"
4652 #define Indent( X ) ( (X) * 4 ), ""
4654 static void BrowseAllExit( void *inContext
)
4656 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
4657 BrowseDomain
* domain
;
4660 BrowseInstance
* instance
;
4661 BrowseIPAddr
* addr
;
4663 dispatch_source_forget( &context
->exitTimer
);
4665 for( domain
= context
->domainList
; domain
; domain
= domain
->next
)
4667 FPrintF( stdout
, "%s\n\n", domain
->name
);
4669 for( type
= domain
->typeList
; type
; type
= type
->next
)
4673 desc
= ServiceTypeDescription( type
->name
);
4674 if( desc
) FPrintF( stdout
, "%*s" "%s (%s)\n\n", Indent( 1 ), desc
, type
->name
);
4675 else FPrintF( stdout
, "%*s" "%s\n\n", Indent( 1 ), type
->name
);
4677 for( browse
= type
->browseList
; browse
; browse
= browse
->next
)
4679 for( instance
= browse
->instanceList
; instance
; instance
= instance
->next
)
4681 char ifname
[ IF_NAMESIZE
+ 1 ];
4683 FPrintF( stdout
, "%*s" "%s via ", Indent( 2 ), instance
->name
);
4684 if( instance
->ifIndex
== 0 )
4686 FPrintF( stdout
, "the Internet" );
4688 else if( if_indextoname( instance
->ifIndex
, ifname
) )
4690 NetTransportType netType
;
4692 SocketGetInterfaceInfo( kInvalidSocketRef
, ifname
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
4694 FPrintF( stdout
, "%s (%s)",
4695 ( netType
== kNetTransportType_Ethernet
) ? "ethernet" : NetTransportTypeToString( netType
),
4700 FPrintF( stdout
, "interface index %u", instance
->ifIndex
);
4702 FPrintF( stdout
, "\n\n" );
4704 if( instance
->hostname
)
4708 SNPrintF( buffer
, sizeof( buffer
), "%s:%u", instance
->hostname
, instance
->port
);
4709 FPrintF( stdout
, "%*s" "%-51s %4llu ms\n", Indent( 3 ), buffer
,
4710 UpTicksToMilliseconds( instance
->resolveDoneTicks
- instance
->resolveStartTicks
) );
4714 FPrintF( stdout
, "%*s" "%s:%u\n", Indent( 3 ), instance
->hostname
, instance
->port
);
4717 for( addr
= instance
->addrList
; addr
; addr
= addr
->next
)
4719 AsyncConnection_Forget( &addr
->connection
);
4721 if( addr
->connectStatus
== kConnectStatus_Pending
)
4723 addr
->connectStatus
= kConnectStatus_Failed
;
4724 addr
->connectError
= kTimeoutErr
;
4727 FPrintF( stdout
, "%*s" "%-##47a %4llu ms", Indent( 4 ),
4728 &addr
->sip
.sa
, UpTicksToMilliseconds( addr
->foundTicks
- instance
->getAddrStartTicks
) );
4729 if( context
->maxConnectTimeSecs
<= 0 )
4731 FPrintF( stdout
, "\n" );
4734 switch( addr
->connectStatus
)
4736 case kConnectStatus_None
:
4737 FPrintF( stdout
, " (%s)\n", kStatusStr_NoConnectionAttempted
);
4740 case kConnectStatus_Succeeded
:
4741 FPrintF( stdout
, " (%s in %.2f ms)\n",
4742 context
->useColoredText
? kStatusStr_CouldConnectColored
: kStatusStr_CouldConnect
,
4743 addr
->connectTimeSecs
* 1000 );
4746 case kConnectStatus_Failed
:
4747 FPrintF( stdout
, " (%s: %m)\n",
4748 context
->useColoredText
? kStatusStr_CouldNotConnectColored
: kStatusStr_CouldNotConnect
,
4749 addr
->connectError
);
4753 FPrintF( stdout
, " (%s)\n", kStatusStr_Unknown
);
4758 FPrintF( stdout
, "\n" );
4759 if( instance
->txtLen
== 0 ) continue;
4761 FPrintF( stdout
, "%*s" "TXT record:\n", Indent( 3 ) );
4762 if( instance
->txtLen
> 1 )
4764 FPrintF( stdout
, "%3{txt}", instance
->txtPtr
, instance
->txtLen
);
4768 FPrintF( stdout
, "%*s" "%#H\n", Indent( 3 ), instance
->txtPtr
, (int) instance
->txtLen
, INT_MAX
);
4770 FPrintF( stdout
, "\n" );
4773 FPrintF( stdout
, "\n" );
4777 while( ( domain
= context
->domainList
) != NULL
)
4779 context
->domainList
= domain
->next
;
4780 BrowseDomainFree( domain
);
4783 BrowseAllContextRelease( context
);
4787 //===========================================================================================================================
4788 // BrowseAllAddDomain
4789 //===========================================================================================================================
4791 static OSStatus
BrowseAllAddDomain( BrowseAllContext
*inContext
, const char *inName
)
4794 BrowseDomain
* domain
;
4796 BrowseDomain
* newDomain
= NULL
;
4798 for( p
= &inContext
->domainList
; ( domain
= *p
) != NULL
; p
= &domain
->next
)
4800 if( strcasecmp( domain
->name
, inName
) == 0 ) break;
4802 require_action_quiet( !domain
, exit
, err
= kDuplicateErr
);
4804 newDomain
= (BrowseDomain
*) calloc( 1, sizeof( *newDomain
) );
4805 require_action( newDomain
, exit
, err
= kNoMemoryErr
);
4807 ++inContext
->refCount
;
4808 newDomain
->context
= inContext
;
4810 newDomain
->name
= strdup( inName
);
4811 require_action( newDomain
->name
, exit
, err
= kNoMemoryErr
);
4813 if( inContext
->serviceTypesCount
> 0 )
4817 for( i
= 0; i
< inContext
->serviceTypesCount
; ++i
)
4819 err
= BrowseAllAddServiceType( inContext
, newDomain
, inContext
->serviceTypes
[ i
], inContext
->ifIndex
,
4820 inContext
->includeAWDL
);
4821 if( err
== kDuplicateErr
) err
= kNoErr
;
4822 require_noerr( err
, exit
);
4828 DNSServiceFlags flags
;
4829 DNSServiceRef sdRef
;
4831 ASPrintF( &recordName
, "_services._dns-sd._udp.%s", newDomain
->name
);
4832 require_action( recordName
, exit
, err
= kNoMemoryErr
);
4834 flags
= kDNSServiceFlagsShareConnection
;
4835 if( inContext
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
4837 sdRef
= newDomain
->context
->mainRef
;
4838 err
= DNSServiceQueryRecord( &sdRef
, flags
, inContext
->ifIndex
, recordName
, kDNSServiceType_PTR
, kDNSServiceClass_IN
,
4839 BrowseAllQueryCallback
, newDomain
);
4841 require_noerr( err
, exit
);
4843 newDomain
->servicesQuery
= sdRef
;
4851 if( newDomain
) BrowseDomainFree( newDomain
);
4855 //===========================================================================================================================
4856 // BrowseAllRemoveDomain
4857 //===========================================================================================================================
4859 static OSStatus
BrowseAllRemoveDomain( BrowseAllContext
*inContext
, const char *inName
)
4862 BrowseDomain
* domain
;
4865 for( p
= &inContext
->domainList
; ( domain
= *p
) != NULL
; p
= &domain
->next
)
4867 if( strcasecmp( domain
->name
, inName
) == 0 ) break;
4873 BrowseDomainFree( domain
);
4884 //===========================================================================================================================
4885 // BrowseAllContextRelease
4886 //===========================================================================================================================
4888 static void BrowseAllContextRelease( BrowseAllContext
*inContext
)
4890 if( --inContext
->refCount
== 0 )
4892 check( !inContext
->domainsQuery
);
4893 check( !inContext
->domainList
);
4894 check( !inContext
->exitTimer
);
4895 check( !inContext
->pendingConnectCount
);
4896 DNSServiceForget( &inContext
->mainRef
);
4897 if( inContext
->serviceTypes
)
4899 StringArray_Free( inContext
->serviceTypes
, inContext
->serviceTypesCount
);
4900 inContext
->serviceTypes
= NULL
;
4901 inContext
->serviceTypesCount
= 0;
4907 //===========================================================================================================================
4908 // BrowseAllAddServiceType
4909 //===========================================================================================================================
4912 BrowseAllAddServiceType(
4913 BrowseAllContext
* inContext
,
4914 BrowseDomain
* inDomain
,
4915 const char * inName
,
4917 Boolean inIncludeAWDL
)
4920 DNSServiceRef sdRef
;
4921 DNSServiceFlags flags
;
4923 BrowseType
** typePtr
;
4924 BrowseType
* newType
= NULL
;
4926 BrowseOp
** browsePtr
;
4927 BrowseOp
* newBrowse
= NULL
;
4929 for( typePtr
= &inDomain
->typeList
; ( type
= *typePtr
) != NULL
; typePtr
= &type
->next
)
4931 if( strcasecmp( type
->name
, inName
) == 0 ) break;
4935 newType
= (BrowseType
*) calloc( 1, sizeof( *newType
) );
4936 require_action( newType
, exit
, err
= kNoMemoryErr
);
4938 newType
->name
= strdup( inName
);
4939 require_action( newType
->name
, exit
, err
= kNoMemoryErr
);
4944 for( browsePtr
= &type
->browseList
; ( browse
= *browsePtr
) != NULL
; browsePtr
= &browse
->next
)
4946 if( browse
->ifIndex
== inIfIndex
) break;
4948 require_action_quiet( !browse
, exit
, err
= kDuplicateErr
);
4950 newBrowse
= (BrowseOp
*) calloc( 1, sizeof( *newBrowse
) );
4951 require_action( newBrowse
, exit
, err
= kNoMemoryErr
);
4953 ++inContext
->refCount
;
4954 newBrowse
->context
= inContext
;
4955 newBrowse
->ifIndex
= inIfIndex
;
4956 if( stricmp_suffix( inName
, "._tcp" ) == 0 ) newBrowse
->isTCP
= true;
4958 flags
= kDNSServiceFlagsShareConnection
;
4959 if( inIncludeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
4961 newBrowse
->startTicks
= UpTicks();
4963 sdRef
= inContext
->mainRef
;
4964 err
= DNSServiceBrowse( &sdRef
, flags
, newBrowse
->ifIndex
, type
->name
, inDomain
->name
, BrowseAllBrowseCallback
,
4966 require_noerr( err
, exit
);
4968 newBrowse
->browse
= sdRef
;
4969 *browsePtr
= newBrowse
;
4979 if( newBrowse
) BrowseOpFree( newBrowse
);
4980 if( newType
) BrowseTypeFree( newType
);
4984 //===========================================================================================================================
4985 // BrowseAllRemoveServiceType
4986 //===========================================================================================================================
4989 BrowseAllRemoveServiceType(
4990 BrowseAllContext
* inContext
,
4991 BrowseDomain
* inDomain
,
4992 const char * inName
,
4993 uint32_t inIfIndex
)
4997 BrowseType
** typePtr
;
4999 BrowseOp
** browsePtr
;
5001 Unused( inContext
);
5003 for( typePtr
= &inDomain
->typeList
; ( type
= *typePtr
) != NULL
; typePtr
= &type
->next
)
5005 if( strcasecmp( type
->name
, inName
) == 0 ) break;
5007 require_action_quiet( type
, exit
, err
= kNotFoundErr
);
5009 for( browsePtr
= &type
->browseList
; ( browse
= *browsePtr
) != NULL
; browsePtr
= &browse
->next
)
5011 if( browse
->ifIndex
== inIfIndex
) break;
5013 require_action_quiet( browse
, exit
, err
= kNotFoundErr
);
5015 *browsePtr
= browse
->next
;
5016 BrowseOpFree( browse
);
5017 if( !type
->browseList
)
5019 *typePtr
= type
->next
;
5020 BrowseTypeFree( type
);
5028 //===========================================================================================================================
5029 // BrowseAllAddServiceInstance
5030 //===========================================================================================================================
5033 BrowseAllAddServiceInstance(
5034 BrowseAllContext
* inContext
,
5035 BrowseOp
* inBrowse
,
5036 const char * inName
,
5037 const char * inRegType
,
5038 const char * inDomain
,
5039 uint32_t inIfIndex
)
5042 DNSServiceRef sdRef
;
5043 BrowseInstance
* instance
;
5044 BrowseInstance
** p
;
5045 const uint64_t nowTicks
= UpTicks();
5046 BrowseInstance
* newInstance
= NULL
;
5048 for( p
= &inBrowse
->instanceList
; ( instance
= *p
) != NULL
; p
= &instance
->next
)
5050 if( ( instance
->ifIndex
== inIfIndex
) && ( strcasecmp( instance
->name
, inName
) == 0 ) ) break;
5052 require_action_quiet( !instance
, exit
, err
= kDuplicateErr
);
5054 newInstance
= (BrowseInstance
*) calloc( 1, sizeof( *newInstance
) );
5055 require_action( newInstance
, exit
, err
= kNoMemoryErr
);
5057 ++inContext
->refCount
;
5058 newInstance
->context
= inContext
;
5059 newInstance
->foundTicks
= nowTicks
;
5060 newInstance
->ifIndex
= inIfIndex
;
5061 newInstance
->isTCP
= inBrowse
->isTCP
;
5063 newInstance
->name
= strdup( inName
);
5064 require_action( newInstance
->name
, exit
, err
= kNoMemoryErr
);
5066 sdRef
= inContext
->mainRef
;
5067 newInstance
->resolveStartTicks
= UpTicks();
5068 err
= DNSServiceResolve( &sdRef
, kDNSServiceFlagsShareConnection
, newInstance
->ifIndex
, inName
, inRegType
, inDomain
,
5069 BrowseAllResolveCallback
, newInstance
);
5070 require_noerr( err
, exit
);
5072 newInstance
->resolve
= sdRef
;
5077 if( newInstance
) BrowseInstanceFree( newInstance
);
5081 //===========================================================================================================================
5082 // BrowseAllRemoveServiceInstance
5083 //===========================================================================================================================
5086 BrowseAllRemoveServiceInstance(
5087 BrowseAllContext
* inContext
,
5088 BrowseOp
* inBrowse
,
5089 const char * inName
,
5090 uint32_t inIfIndex
)
5093 BrowseInstance
* instance
;
5094 BrowseInstance
** p
;
5096 Unused( inContext
);
5098 for( p
= &inBrowse
->instanceList
; ( instance
= *p
) != NULL
; p
= &instance
->next
)
5100 if( ( instance
->ifIndex
== inIfIndex
) && ( strcasecmp( instance
->name
, inName
) == 0 ) ) break;
5102 require_action_quiet( instance
, exit
, err
= kNotFoundErr
);
5104 *p
= instance
->next
;
5105 BrowseInstanceFree( instance
);
5112 //===========================================================================================================================
5113 // BrowseAllAddIPAddress
5114 //===========================================================================================================================
5116 #define kDiscardProtocolPort 9
5119 BrowseAllAddIPAddress(
5120 BrowseAllContext
* inContext
,
5121 BrowseInstance
* inInstance
,
5122 const struct sockaddr
* inSockAddr
)
5125 BrowseIPAddr
* addr
;
5127 const uint64_t nowTicks
= UpTicks();
5128 BrowseIPAddr
* newAddr
= NULL
;
5130 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
5132 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
5137 for( p
= &inInstance
->addrList
; ( addr
= *p
) != NULL
; p
= &addr
->next
)
5139 if( SockAddrCompareAddr( &addr
->sip
, inSockAddr
) == 0 ) break;
5141 require_action_quiet( !addr
, exit
, err
= kDuplicateErr
);
5143 newAddr
= (BrowseIPAddr
*) calloc( 1, sizeof( *newAddr
) );
5144 require_action( newAddr
, exit
, err
= kNoMemoryErr
);
5146 ++inContext
->refCount
;
5147 newAddr
->refCount
= 1;
5148 newAddr
->context
= inContext
;
5149 newAddr
->foundTicks
= nowTicks
;
5150 SockAddrCopy( inSockAddr
, &newAddr
->sip
.sa
);
5152 if( ( inContext
->maxConnectTimeSecs
> 0 ) && inInstance
->isTCP
&& ( inInstance
->port
!= kDiscardProtocolPort
) )
5154 char destination
[ kSockAddrStringMaxSize
];
5156 err
= SockAddrToString( &newAddr
->sip
, kSockAddrStringFlagsNoPort
, destination
);
5157 require_noerr( err
, exit
);
5159 err
= AsyncConnection_Connect( &newAddr
->connection
, destination
, -inInstance
->port
, kAsyncConnectionFlag_P2P
,
5160 kAsyncConnectionNoTimeout
, kSocketBufferSize_DontSet
, kSocketBufferSize_DontSet
,
5161 BrowseAllConnectionProgress
, newAddr
, BrowseAllConnectionHandler
, newAddr
, dispatch_get_main_queue() );
5162 require_noerr( err
, exit
);
5164 ++newAddr
->refCount
;
5165 newAddr
->connectStatus
= kConnectStatus_Pending
;
5166 ++inContext
->pendingConnectCount
;
5174 if( newAddr
) BrowseIPAddrRelease( newAddr
);
5178 //===========================================================================================================================
5179 // BrowseAllRemoveIPAddress
5180 //===========================================================================================================================
5183 BrowseAllRemoveIPAddress(
5184 BrowseAllContext
* inContext
,
5185 BrowseInstance
* inInstance
,
5186 const struct sockaddr
* inSockAddr
)
5189 BrowseIPAddr
* addr
;
5192 Unused( inContext
);
5194 for( p
= &inInstance
->addrList
; ( addr
= *p
) != NULL
; p
= &addr
->next
)
5196 if( SockAddrCompareAddr( &addr
->sip
.sa
, inSockAddr
) == 0 ) break;
5198 require_action_quiet( addr
, exit
, err
= kNotFoundErr
);
5201 BrowseIPAddrRelease( addr
);
5208 //===========================================================================================================================
5210 //===========================================================================================================================
5212 static void BrowseDomainFree( BrowseDomain
*inDomain
)
5216 ForgetBrowseAllContext( &inDomain
->context
);
5217 ForgetMem( &inDomain
->name
);
5218 DNSServiceForget( &inDomain
->servicesQuery
);
5219 while( ( type
= inDomain
->typeList
) != NULL
)
5221 inDomain
->typeList
= type
->next
;
5222 BrowseTypeFree( type
);
5227 //===========================================================================================================================
5229 //===========================================================================================================================
5231 static void BrowseTypeFree( BrowseType
*inType
)
5235 ForgetMem( &inType
->name
);
5236 while( ( browse
= inType
->browseList
) != NULL
)
5238 inType
->browseList
= browse
->next
;
5239 BrowseOpFree( browse
);
5244 //===========================================================================================================================
5246 //===========================================================================================================================
5248 static void BrowseOpFree( BrowseOp
*inBrowse
)
5250 BrowseInstance
* instance
;
5252 ForgetBrowseAllContext( &inBrowse
->context
);
5253 DNSServiceForget( &inBrowse
->browse
);
5254 while( ( instance
= inBrowse
->instanceList
) != NULL
)
5256 inBrowse
->instanceList
= instance
->next
;
5257 BrowseInstanceFree( instance
);
5262 //===========================================================================================================================
5263 // BrowseInstanceFree
5264 //===========================================================================================================================
5266 static void BrowseInstanceFree( BrowseInstance
*inInstance
)
5268 ForgetBrowseAllContext( &inInstance
->context
);
5269 ForgetMem( &inInstance
->name
);
5270 DNSServiceForget( &inInstance
->resolve
);
5271 DNSServiceForget( &inInstance
->getAddr
);
5272 ForgetMem( &inInstance
->txtPtr
);
5273 ForgetMem( &inInstance
->hostname
);
5274 ForgetIPAddressList( &inInstance
->addrList
);
5278 //===========================================================================================================================
5279 // BrowseIPAddrRelease
5280 //===========================================================================================================================
5282 static void BrowseIPAddrRelease( BrowseIPAddr
*inAddr
)
5284 AsyncConnection_Forget( &inAddr
->connection
);
5285 if( --inAddr
->refCount
== 0 )
5287 ForgetBrowseAllContext( &inAddr
->context
);
5292 //===========================================================================================================================
5293 // BrowseIPAddrReleaseList
5294 //===========================================================================================================================
5296 static void BrowseIPAddrReleaseList( BrowseIPAddr
*inList
)
5298 BrowseIPAddr
* addr
;
5300 while( ( addr
= inList
) != NULL
)
5302 inList
= addr
->next
;
5303 BrowseIPAddrRelease( addr
);
5307 //===========================================================================================================================
5309 //===========================================================================================================================
5311 const FlagStringPair kGetNameInfoFlagStringPairs
[] =
5313 CaseFlagStringify( NI_NUMERICSCOPE
),
5314 CaseFlagStringify( NI_DGRAM
),
5315 CaseFlagStringify( NI_NUMERICSERV
),
5316 CaseFlagStringify( NI_NAMEREQD
),
5317 CaseFlagStringify( NI_NUMERICHOST
),
5318 CaseFlagStringify( NI_NOFQDN
),
5322 static void GetNameInfoCmd( void )
5328 const FlagStringPair
* pair
;
5330 char host
[ NI_MAXHOST
];
5331 char serv
[ NI_MAXSERV
];
5333 err
= StringToSockAddr( gGetNameInfo_IPAddress
, &sip
, sizeof( sip
), &sockAddrLen
);
5337 FPrintF( stderr
, "Failed to convert \"%s\" to a sockaddr.\n", gGetNameInfo_IPAddress
);
5342 if( gGetNameInfoFlag_DGram
) flags
|= NI_DGRAM
;
5343 if( gGetNameInfoFlag_NameReqd
) flags
|= NI_NAMEREQD
;
5344 if( gGetNameInfoFlag_NoFQDN
) flags
|= NI_NOFQDN
;
5345 if( gGetNameInfoFlag_NumericHost
) flags
|= NI_NUMERICHOST
;
5346 if( gGetNameInfoFlag_NumericScope
) flags
|= NI_NUMERICSCOPE
;
5347 if( gGetNameInfoFlag_NumericServ
) flags
|= NI_NUMERICSERV
;
5351 FPrintF( stdout
, "SockAddr: %##a\n", &sip
.sa
);
5352 FPrintF( stdout
, "Flags: 0x%X < ", flags
);
5353 for( pair
= kGetNameInfoFlagStringPairs
; pair
->str
!= NULL
; ++pair
)
5355 if( flags
& pair
->flag
) FPrintF( stdout
, "%s ", pair
->str
);
5357 FPrintF( stdout
, ">\n" );
5358 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5359 FPrintF( stdout
, "---\n" );
5361 // Call getnameinfo().
5363 err
= getnameinfo( &sip
.sa
, (socklen_t
) sockAddrLen
, host
, (socklen_t
) sizeof( host
), serv
, (socklen_t
) sizeof( serv
),
5365 gettimeofday( &now
, NULL
);
5368 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
5372 FPrintF( stdout
, "host: %s\n", host
);
5373 FPrintF( stdout
, "serv: %s\n", serv
);
5375 FPrintF( stdout
, "---\n" );
5376 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
5379 gExitCode
= err
? 1 : 0;
5382 //===========================================================================================================================
5383 // GetAddrInfoStressCmd
5384 //===========================================================================================================================
5388 DNSServiceRef mainRef
;
5389 DNSServiceRef sdRef
;
5390 DNSServiceFlags flags
;
5391 unsigned int interfaceIndex
;
5392 unsigned int connectionNumber
;
5393 unsigned int requestCount
;
5394 unsigned int requestCountMax
;
5395 unsigned int requestCountLimit
;
5396 unsigned int durationMinMs
;
5397 unsigned int durationMaxMs
;
5401 static void GetAddrInfoStressEvent( void *inContext
);
5402 static void DNSSD_API
5403 GetAddrInfoStressCallback(
5404 DNSServiceRef inSDRef
,
5405 DNSServiceFlags inFlags
,
5406 uint32_t inInterfaceIndex
,
5407 DNSServiceErrorType inError
,
5408 const char * inHostname
,
5409 const struct sockaddr
* inSockAddr
,
5413 static void GetAddrInfoStressCmd( void )
5416 GAIStressContext
* context
= NULL
;
5418 DNSServiceFlags flags
;
5420 char ifName
[ kInterfaceNameBufLen
];
5422 if( gGAIStress_TestDurationSecs
< 0 )
5424 FPrintF( stdout
, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs
);
5428 if( gGAIStress_ConnectionCount
<= 0 )
5430 FPrintF( stdout
, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount
);
5434 if( gGAIStress_DurationMinMs
<= 0 )
5436 FPrintF( stdout
, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs
);
5440 if( gGAIStress_DurationMaxMs
<= 0 )
5442 FPrintF( stdout
, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs
);
5446 if( gGAIStress_DurationMinMs
> gGAIStress_DurationMaxMs
)
5448 FPrintF( stdout
, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
5449 gGAIStress_DurationMinMs
, gGAIStress_DurationMaxMs
);
5453 if( gGAIStress_RequestCountMax
<= 0 )
5455 FPrintF( stdout
, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax
);
5462 flags
= GetDNSSDFlagsFromOpts();
5464 // Set interface index.
5466 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
5467 require_noerr_quiet( err
, exit
);
5469 for( i
= 0; i
< gGAIStress_ConnectionCount
; ++i
)
5471 context
= (GAIStressContext
*) calloc( 1, sizeof( *context
) );
5472 require_action( context
, exit
, err
= kNoMemoryErr
);
5474 context
->flags
= flags
;
5475 context
->interfaceIndex
= ifIndex
;
5476 context
->connectionNumber
= (unsigned int)( i
+ 1 );
5477 context
->requestCountMax
= (unsigned int) gGAIStress_RequestCountMax
;
5478 context
->durationMinMs
= (unsigned int) gGAIStress_DurationMinMs
;
5479 context
->durationMaxMs
= (unsigned int) gGAIStress_DurationMaxMs
;
5481 dispatch_async_f( dispatch_get_main_queue(), context
, GetAddrInfoStressEvent
);
5485 if( gGAIStress_TestDurationSecs
> 0 )
5487 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs
), dispatch_get_main_queue(), NULL
, Exit
);
5490 FPrintF( stdout
, "Flags: %#{flags}\n", flags
, kDNSServiceFlagsDescriptors
);
5491 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
5492 FPrintF( stdout
, "Test duration: " );
5493 if( gGAIStress_TestDurationSecs
== 0 )
5495 FPrintF( stdout
, "∞\n" );
5499 FPrintF( stdout
, "%d s\n", gGAIStress_TestDurationSecs
);
5501 FPrintF( stdout
, "Connection count: %d\n", gGAIStress_ConnectionCount
);
5502 FPrintF( stdout
, "Request duration min: %d ms\n", gGAIStress_DurationMinMs
);
5503 FPrintF( stdout
, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs
);
5504 FPrintF( stdout
, "Request count max: %d\n", gGAIStress_RequestCountMax
);
5505 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5506 FPrintF( stdout
, "---\n" );
5511 FreeNullSafe( context
);
5512 if( err
) exit( 1 );
5515 //===========================================================================================================================
5516 // GetAddrInfoStressEvent
5517 //===========================================================================================================================
5519 #define kStressRandStrLen 5
5521 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
5523 static void GetAddrInfoStressEvent( void *inContext
)
5525 GAIStressContext
* const context
= (GAIStressContext
*) inContext
;
5527 DNSServiceRef sdRef
;
5528 unsigned int nextMs
;
5529 char randomStr
[ kStressRandStrLen
+ 1 ];
5530 char hostname
[ kStressRandStrLen
+ 4 + 1 ];
5531 Boolean isConnectionNew
= false;
5532 static Boolean printedHeader
= false;
5534 if( !context
->mainRef
|| ( context
->requestCount
>= context
->requestCountLimit
) )
5536 DNSServiceForget( &context
->mainRef
);
5537 context
->sdRef
= NULL
;
5538 context
->requestCount
= 0;
5539 context
->requestCountLimit
= RandomRange( 1, context
->requestCountMax
);
5541 err
= DNSServiceCreateConnection( &context
->mainRef
);
5542 require_noerr( err
, exit
);
5544 err
= DNSServiceSetDispatchQueue( context
->mainRef
, dispatch_get_main_queue() );
5545 require_noerr( err
, exit
);
5547 isConnectionNew
= true;
5550 RandomString( kLowercaseAlphaCharSet
, sizeof_string( kLowercaseAlphaCharSet
), 2, kStressRandStrLen
, randomStr
);
5551 SNPrintF( hostname
, sizeof( hostname
), "%s.com", randomStr
);
5553 nextMs
= RandomRange( context
->durationMinMs
, context
->durationMaxMs
);
5555 if( !printedHeader
)
5557 FPrintF( stdout
, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
5558 printedHeader
= true;
5560 FPrintF( stdout
, "%{du:time} %3u%c %9s %8u\n",
5561 NULL
, context
->connectionNumber
, isConnectionNew
? '*': ' ', hostname
, nextMs
);
5563 DNSServiceForget( &context
->sdRef
);
5564 sdRef
= context
->mainRef
;
5565 err
= DNSServiceGetAddrInfo( &sdRef
, context
->flags
| kDNSServiceFlagsShareConnection
, context
->interfaceIndex
,
5566 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, hostname
, GetAddrInfoStressCallback
, NULL
);
5567 require_noerr( err
, exit
);
5568 context
->sdRef
= sdRef
;
5570 context
->requestCount
++;
5572 dispatch_after_f( dispatch_time_milliseconds( nextMs
), dispatch_get_main_queue(), context
, GetAddrInfoStressEvent
);
5575 if( err
) exit( 1 );
5578 //===========================================================================================================================
5579 // GetAddrInfoStressCallback
5580 //===========================================================================================================================
5582 static void DNSSD_API
5583 GetAddrInfoStressCallback(
5584 DNSServiceRef inSDRef
,
5585 DNSServiceFlags inFlags
,
5586 uint32_t inInterfaceIndex
,
5587 DNSServiceErrorType inError
,
5588 const char * inHostname
,
5589 const struct sockaddr
* inSockAddr
,
5595 Unused( inInterfaceIndex
);
5597 Unused( inHostname
);
5598 Unused( inSockAddr
);
5600 Unused( inContext
);
5603 //===========================================================================================================================
5605 //===========================================================================================================================
5609 sockaddr_ip serverAddr
;
5615 dispatch_source_t readSource
;
5622 Boolean printRawRData
; // True if RDATA results are not to be formatted.
5623 uint8_t msgBuf
[ 512 ];
5627 static void DNSQueryPrintPrologue( const DNSQueryContext
*inContext
);
5628 static void DNSQueryReadHandler( void *inContext
);
5629 static void DNSQueryCancelHandler( void *inContext
);
5631 static void DNSQueryCmd( void )
5634 DNSQueryContext
* context
= NULL
;
5636 size_t msgLen
, sendLen
;
5638 // Check command parameters.
5640 if( gDNSQuery_TimeLimitSecs
< -1 )
5642 FPrintF( stdout
, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs
);
5646 if( ( gDNSQuery_Flags
< INT16_MIN
) || ( gDNSQuery_Flags
> UINT16_MAX
) )
5648 FPrintF( stdout
, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags
);
5655 context
= (DNSQueryContext
*) calloc( 1, sizeof( *context
) );
5656 require_action( context
, exit
, err
= kNoMemoryErr
);
5658 context
->name
= gDNSQuery_Name
;
5659 context
->sock
= kInvalidSocketRef
;
5660 context
->timeLimitSecs
= gDNSQuery_TimeLimitSecs
;
5661 context
->queryID
= (uint16_t) Random32();
5662 context
->useTCP
= gDNSQuery_UseTCP
? true : false;
5663 context
->printRawRData
= gDNSQuery_RawRData
? true : false;
5665 #if( TARGET_OS_DARWIN )
5666 if( gDNSQuery_Server
)
5669 err
= StringToSockAddr( gDNSQuery_Server
, &context
->serverAddr
, sizeof( context
->serverAddr
), NULL
);
5670 require_noerr( err
, exit
);
5672 #if( TARGET_OS_DARWIN )
5675 err
= GetDefaultDNSServer( &context
->serverAddr
);
5676 require_noerr( err
, exit
);
5679 if( SockAddrGetPort( &context
->serverAddr
) == 0 ) SockAddrSetPort( &context
->serverAddr
, kDNSPort
);
5681 err
= RecordTypeFromArgString( gDNSQuery_Type
, &context
->type
);
5682 require_noerr( err
, exit
);
5684 // Write query message.
5686 check_compile_time_code( sizeof( context
->msgBuf
) >= ( kDNSQueryMessageMaxLen
+ 2 ) );
5688 msgPtr
= context
->useTCP
? &context
->msgBuf
[ 2 ] : context
->msgBuf
;
5689 err
= WriteDNSQueryMessage( msgPtr
, context
->queryID
, (uint16_t) gDNSQuery_Flags
, context
->name
, context
->type
,
5690 kDNSServiceClass_IN
, &msgLen
);
5691 require_noerr( err
, exit
);
5692 check( msgLen
<= UINT16_MAX
);
5694 if( context
->useTCP
)
5696 WriteBig16( context
->msgBuf
, msgLen
);
5697 sendLen
= 2 + msgLen
;
5704 DNSQueryPrintPrologue( context
);
5706 if( gDNSQuery_Verbose
)
5708 FPrintF( stdout
, "DNS message to send:\n\n%{du:dnsmsg}", msgPtr
, msgLen
);
5709 FPrintF( stdout
, "---\n" );
5712 if( context
->useTCP
)
5714 // Create TCP socket.
5716 context
->sock
= socket( context
->serverAddr
.sa
.sa_family
, SOCK_STREAM
, IPPROTO_TCP
);
5717 err
= map_socket_creation_errno( context
->sock
);
5718 require_noerr( err
, exit
);
5720 err
= SocketConnect( context
->sock
, &context
->serverAddr
, 5 );
5721 require_noerr( err
, exit
);
5725 // Create UDP socket.
5727 err
= UDPClientSocketOpen( AF_UNSPEC
, &context
->serverAddr
, 0, -1, NULL
, &context
->sock
);
5728 require_noerr( err
, exit
);
5731 context
->sendTicks
= UpTicks();
5732 err
= SocketWriteAll( context
->sock
, context
->msgBuf
, sendLen
, 5 );
5733 require_noerr( err
, exit
);
5735 if( context
->timeLimitSecs
== 0 ) goto exit
;
5737 err
= DispatchReadSourceCreate( context
->sock
, NULL
, DNSQueryReadHandler
, DNSQueryCancelHandler
, context
,
5738 &context
->readSource
);
5739 require_noerr( err
, exit
);
5740 dispatch_resume( context
->readSource
);
5742 if( context
->timeLimitSecs
> 0 )
5744 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
5752 dispatch_source_forget( &context
->readSource
);
5753 ForgetSocket( &context
->sock
);
5756 if( err
) exit( 1 );
5759 //===========================================================================================================================
5760 // DNSQueryPrintPrologue
5761 //===========================================================================================================================
5763 static void DNSQueryPrintPrologue( const DNSQueryContext
*inContext
)
5765 const int timeLimitSecs
= inContext
->timeLimitSecs
;
5767 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
5768 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->type
), inContext
->type
);
5769 FPrintF( stdout
, "Server: %##a\n", &inContext
->serverAddr
);
5770 FPrintF( stdout
, "Transport: %s\n", inContext
->useTCP
? "TCP" : "UDP" );
5771 FPrintF( stdout
, "Time limit: " );
5772 if( timeLimitSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
5773 else FPrintF( stdout
, "∞\n" );
5774 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
5775 FPrintF( stdout
, "---\n" );
5778 //===========================================================================================================================
5779 // DNSQueryReadHandler
5780 //===========================================================================================================================
5782 static void DNSQueryReadHandler( void *inContext
)
5786 const uint64_t nowTicks
= UpTicks();
5787 DNSQueryContext
* const context
= (DNSQueryContext
*) inContext
;
5789 gettimeofday( &now
, NULL
);
5791 if( context
->useTCP
)
5793 if( !context
->haveTCPLen
)
5795 err
= SocketReadData( context
->sock
, &context
->msgBuf
, 2, &context
->msgOffset
);
5796 if( err
== EWOULDBLOCK
) { err
= kNoErr
; goto exit
; }
5797 require_noerr( err
, exit
);
5799 context
->msgOffset
= 0;
5800 context
->msgLen
= ReadBig16( context
->msgBuf
);
5801 context
->haveTCPLen
= true;
5802 if( context
->msgLen
<= sizeof( context
->msgBuf
) )
5804 context
->msgPtr
= context
->msgBuf
;
5808 context
->msgPtr
= (uint8_t *) malloc( context
->msgLen
);
5809 require_action( context
->msgPtr
, exit
, err
= kNoMemoryErr
);
5813 err
= SocketReadData( context
->sock
, context
->msgPtr
, context
->msgLen
, &context
->msgOffset
);
5814 if( err
== EWOULDBLOCK
) { err
= kNoErr
; goto exit
; }
5815 require_noerr( err
, exit
);
5816 context
->msgOffset
= 0;
5817 context
->haveTCPLen
= false;
5821 sockaddr_ip fromAddr
;
5823 context
->msgPtr
= context
->msgBuf
;
5824 err
= SocketRecvFrom( context
->sock
, context
->msgPtr
, sizeof( context
->msgBuf
), &context
->msgLen
, &fromAddr
,
5825 sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
5826 require_noerr( err
, exit
);
5828 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
5831 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
5832 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
5833 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
5834 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
5835 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, context
->msgPtr
, context
->msgLen
);
5837 if( ( context
->msgLen
>= kDNSHeaderLength
) && ( DNSHeaderGetID( (DNSHeader
*) context
->msgPtr
) == context
->queryID
) )
5839 Exit( kExitReason_ReceivedResponse
);
5843 if( err
) dispatch_source_forget( &context
->readSource
);
5846 //===========================================================================================================================
5847 // DNSQueryCancelHandler
5848 //===========================================================================================================================
5850 static void DNSQueryCancelHandler( void *inContext
)
5852 DNSQueryContext
* const context
= (DNSQueryContext
*) inContext
;
5854 check( !context
->readSource
);
5855 ForgetSocket( &context
->sock
);
5856 if( context
->msgPtr
!= context
->msgBuf
) ForgetMem( &context
->msgPtr
);
5858 dispatch_async_f( dispatch_get_main_queue(), NULL
, Exit
);
5861 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
5862 //===========================================================================================================================
5864 //===========================================================================================================================
5866 #define kDNSCryptPort 443
5868 #define kDNSCryptMinPadLength 8
5869 #define kDNSCryptMaxPadLength 256
5870 #define kDNSCryptBlockSize 64
5871 #define kDNSCryptCertMinimumLength 124
5872 #define kDNSCryptClientMagicLength 8
5873 #define kDNSCryptResolverMagicLength 8
5874 #define kDNSCryptHalfNonceLength 12
5875 #define kDNSCryptCertMagicLength 4
5877 check_compile_time( ( kDNSCryptHalfNonceLength
* 2 ) == crypto_box_NONCEBYTES
);
5879 static const uint8_t kDNSCryptCertMagic
[ kDNSCryptCertMagicLength
] = { 'D', 'N', 'S', 'C' };
5880 static const uint8_t kDNSCryptResolverMagic
[ kDNSCryptResolverMagicLength
] =
5882 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
5887 uint8_t certMagic
[ kDNSCryptCertMagicLength
];
5888 uint8_t esVersion
[ 2 ];
5889 uint8_t minorVersion
[ 2 ];
5890 uint8_t signature
[ crypto_sign_BYTES
];
5891 uint8_t publicKey
[ crypto_box_PUBLICKEYBYTES
];
5892 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5893 uint8_t serial
[ 4 ];
5894 uint8_t startTime
[ 4 ];
5895 uint8_t endTime
[ 4 ];
5896 uint8_t extensions
[ 1 ]; // Variably-sized extension data.
5900 check_compile_time( offsetof( DNSCryptCert
, extensions
) == kDNSCryptCertMinimumLength
);
5904 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5905 uint8_t clientPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5906 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5907 uint8_t poly1305MAC
[ 16 ];
5909 } DNSCryptQueryHeader
;
5911 check_compile_time( sizeof( DNSCryptQueryHeader
) == 68 );
5912 check_compile_time( sizeof( DNSCryptQueryHeader
) >= crypto_box_ZEROBYTES
);
5913 check_compile_time( ( sizeof( DNSCryptQueryHeader
) - crypto_box_ZEROBYTES
+ crypto_box_BOXZEROBYTES
) ==
5914 offsetof( DNSCryptQueryHeader
, poly1305MAC
) );
5918 uint8_t resolverMagic
[ kDNSCryptResolverMagicLength
];
5919 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5920 uint8_t resolverNonce
[ kDNSCryptHalfNonceLength
];
5921 uint8_t poly1305MAC
[ 16 ];
5923 } DNSCryptResponseHeader
;
5925 check_compile_time( sizeof( DNSCryptResponseHeader
) == 48 );
5926 check_compile_time( offsetof( DNSCryptResponseHeader
, poly1305MAC
) >= crypto_box_BOXZEROBYTES
);
5927 check_compile_time( ( offsetof( DNSCryptResponseHeader
, poly1305MAC
) - crypto_box_BOXZEROBYTES
+ crypto_box_ZEROBYTES
) ==
5928 sizeof( DNSCryptResponseHeader
) );
5932 sockaddr_ip serverAddr
;
5934 const char * providerName
;
5936 const uint8_t * certPtr
;
5938 dispatch_source_t readSource
;
5943 Boolean printRawRData
;
5944 uint8_t serverPublicSignKey
[ crypto_sign_PUBLICKEYBYTES
];
5945 uint8_t serverPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5946 uint8_t clientPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5947 uint8_t clientSecretKey
[ crypto_box_SECRETKEYBYTES
];
5948 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5949 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5950 uint8_t nmKey
[ crypto_box_BEFORENMBYTES
];
5951 uint8_t msgBuf
[ 512 ];
5955 static void DNSCryptReceiveCertHandler( void *inContext
);
5956 static void DNSCryptReceiveResponseHandler( void *inContext
);
5957 static void DNSCryptProceed( void *inContext
);
5958 static OSStatus
DNSCryptProcessCert( DNSCryptContext
*inContext
);
5959 static OSStatus
DNSCryptBuildQuery( DNSCryptContext
*inContext
);
5960 static OSStatus
DNSCryptSendQuery( DNSCryptContext
*inContext
);
5961 static void DNSCryptPrintCertificate( const DNSCryptCert
*inCert
, size_t inLen
);
5963 static void DNSCryptCmd( void )
5966 DNSCryptContext
* context
= NULL
;
5967 size_t writtenBytes
;
5969 SocketContext
* sockCtx
;
5970 SocketRef sock
= kInvalidSocketRef
;
5973 // Check command parameters.
5975 if( gDNSCrypt_TimeLimitSecs
< -1 )
5977 FPrintF( stdout
, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs
);
5984 context
= (DNSCryptContext
*) calloc( 1, sizeof( *context
) );
5985 require_action( context
, exit
, err
= kNoMemoryErr
);
5987 context
->providerName
= gDNSCrypt_ProviderName
;
5988 context
->qname
= gDNSCrypt_Name
;
5989 context
->timeLimitSecs
= gDNSCrypt_TimeLimitSecs
;
5990 context
->printRawRData
= gDNSCrypt_RawRData
? true : false;
5992 err
= crypto_box_keypair( context
->clientPublicKey
, context
->clientSecretKey
);
5993 require_noerr( err
, exit
);
5995 err
= HexToData( gDNSCrypt_ProviderKey
, kSizeCString
, kHexToData_DefaultFlags
,
5996 context
->serverPublicSignKey
, sizeof( context
->serverPublicSignKey
), &writtenBytes
, &totalBytes
, &ptr
);
5997 if( err
|| ( *ptr
!= '\0' ) )
5999 FPrintF( stderr
, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey
);
6002 else if( totalBytes
!= sizeof( context
->serverPublicSignKey
) )
6004 FPrintF( stderr
, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
6005 totalBytes
, sizeof( context
->serverPublicSignKey
) );
6009 check( writtenBytes
== totalBytes
);
6011 err
= StringToSockAddr( gDNSCrypt_Server
, &context
->serverAddr
, sizeof( context
->serverAddr
), NULL
);
6012 require_noerr( err
, exit
);
6013 if( SockAddrGetPort( &context
->serverAddr
) == 0 ) SockAddrSetPort( &context
->serverAddr
, kDNSCryptPort
);
6015 err
= RecordTypeFromArgString( gDNSCrypt_Type
, &context
->qtype
);
6016 require_noerr( err
, exit
);
6018 // Write query message.
6020 context
->queryID
= (uint16_t) Random32();
6021 err
= WriteDNSQueryMessage( context
->msgBuf
, context
->queryID
, kDNSHeaderFlag_RecursionDesired
, context
->providerName
,
6022 kDNSServiceType_TXT
, kDNSServiceClass_IN
, &context
->msgLen
);
6023 require_noerr( err
, exit
);
6025 // Create UDP socket.
6027 err
= UDPClientSocketOpen( AF_UNSPEC
, &context
->serverAddr
, 0, -1, NULL
, &sock
);
6028 require_noerr( err
, exit
);
6032 context
->sendTicks
= UpTicks();
6033 err
= SocketWriteAll( sock
, context
->msgBuf
, context
->msgLen
, 5 );
6034 require_noerr( err
, exit
);
6036 err
= SocketContextCreate( sock
, context
, &sockCtx
);
6037 require_noerr( err
, exit
);
6038 sock
= kInvalidSocketRef
;
6040 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, DNSCryptReceiveCertHandler
, SocketContextCancelHandler
, sockCtx
,
6041 &context
->readSource
);
6042 if( err
) ForgetSocketContext( &sockCtx
);
6043 require_noerr( err
, exit
);
6045 dispatch_resume( context
->readSource
);
6047 if( context
->timeLimitSecs
> 0 )
6049 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
6055 if( context
) free( context
);
6056 ForgetSocket( &sock
);
6057 if( err
) exit( 1 );
6060 //===========================================================================================================================
6061 // DNSCryptReceiveCertHandler
6062 //===========================================================================================================================
6064 static void DNSCryptReceiveCertHandler( void *inContext
)
6068 const uint64_t nowTicks
= UpTicks();
6069 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6070 DNSCryptContext
* const context
= (DNSCryptContext
*) sockCtx
->userContext
;
6071 const DNSHeader
* hdr
;
6072 sockaddr_ip fromAddr
;
6073 const uint8_t * ptr
;
6074 const uint8_t * txtPtr
;
6076 unsigned int answerCount
, i
;
6077 uint8_t targetName
[ kDomainNameLengthMax
];
6079 gettimeofday( &now
, NULL
);
6081 dispatch_source_forget( &context
->readSource
);
6083 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &context
->msgLen
,
6084 &fromAddr
, sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6085 require_noerr( err
, exit
);
6086 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
6088 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6089 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
6090 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
6091 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
6092 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, context
->msgBuf
, context
->msgLen
);
6094 require_action_quiet( context
->msgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
6096 hdr
= (DNSHeader
*) context
->msgBuf
;
6097 require_action_quiet( DNSHeaderGetID( hdr
) == context
->queryID
, exit
, err
= kMismatchErr
);
6099 err
= DNSMessageGetAnswerSection( context
->msgBuf
, context
->msgLen
, &ptr
);
6100 require_noerr( err
, exit
);
6102 err
= DomainNameFromString( targetName
, context
->providerName
, NULL
);
6103 require_noerr( err
, exit
);
6105 answerCount
= DNSHeaderGetAnswerCount( hdr
);
6106 for( i
= 0; i
< answerCount
; ++i
)
6110 uint8_t name
[ kDomainNameLengthMax
];
6112 err
= DNSMessageExtractRecord( context
->msgBuf
, context
->msgLen
, ptr
, name
, &type
, &class, NULL
, &txtPtr
, &txtLen
,
6114 require_noerr( err
, exit
);
6116 if( ( type
== kDNSServiceType_TXT
) && ( class == kDNSServiceClass_IN
) && DomainNameEqual( name
, targetName
) )
6122 if( txtLen
< ( 1 + kDNSCryptCertMinimumLength
) )
6124 FPrintF( stderr
, "TXT record length is too short (%u < %u)\n", txtLen
, kDNSCryptCertMinimumLength
+ 1 );
6128 if( txtPtr
[ 0 ] < kDNSCryptCertMinimumLength
)
6130 FPrintF( stderr
, "TXT record value length is too short (%u < %u)\n", txtPtr
[ 0 ], kDNSCryptCertMinimumLength
);
6135 context
->certLen
= txtPtr
[ 0 ];
6136 context
->certPtr
= &txtPtr
[ 1 ];
6138 dispatch_async_f( dispatch_get_main_queue(), context
, DNSCryptProceed
);
6141 if( err
) Exit( NULL
);
6144 //===========================================================================================================================
6145 // DNSCryptReceiveResponseHandler
6146 //===========================================================================================================================
6148 static void DNSCryptReceiveResponseHandler( void *inContext
)
6152 const uint64_t nowTicks
= UpTicks();
6153 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6154 DNSCryptContext
* const context
= (DNSCryptContext
*) sockCtx
->userContext
;
6155 sockaddr_ip fromAddr
;
6156 DNSCryptResponseHeader
* hdr
;
6157 const uint8_t * end
;
6158 uint8_t * ciphertext
;
6159 uint8_t * plaintext
;
6160 const uint8_t * response
;
6161 uint8_t nonce
[ crypto_box_NONCEBYTES
];
6163 gettimeofday( &now
, NULL
);
6165 dispatch_source_forget( &context
->readSource
);
6167 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &context
->msgLen
,
6168 &fromAddr
, sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6169 require_noerr( err
, exit
);
6170 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
6172 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6173 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
6174 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
6175 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
6177 if( context
->msgLen
< sizeof( DNSCryptResponseHeader
) )
6179 FPrintF( stderr
, "DNSCrypt response is too short.\n" );
6184 hdr
= (DNSCryptResponseHeader
*) context
->msgBuf
;
6186 if( memcmp( hdr
->resolverMagic
, kDNSCryptResolverMagic
, kDNSCryptResolverMagicLength
) != 0 )
6188 FPrintF( stderr
, "DNSCrypt response resolver magic %#H != %#H\n",
6189 hdr
->resolverMagic
, kDNSCryptResolverMagicLength
, INT_MAX
,
6190 kDNSCryptResolverMagic
, kDNSCryptResolverMagicLength
, INT_MAX
);
6195 if( memcmp( hdr
->clientNonce
, context
->clientNonce
, kDNSCryptHalfNonceLength
) != 0 )
6197 FPrintF( stderr
, "DNSCrypt response client nonce mismatch.\n" );
6202 memcpy( nonce
, hdr
->clientNonce
, crypto_box_NONCEBYTES
);
6204 ciphertext
= hdr
->poly1305MAC
- crypto_box_BOXZEROBYTES
;
6205 memset( ciphertext
, 0, crypto_box_BOXZEROBYTES
);
6207 plaintext
= (uint8_t *)( hdr
+ 1 ) - crypto_box_ZEROBYTES
;
6208 check( plaintext
== ciphertext
);
6210 end
= context
->msgBuf
+ context
->msgLen
;
6212 err
= crypto_box_open_afternm( plaintext
, ciphertext
, (size_t)( end
- ciphertext
), nonce
, context
->nmKey
);
6213 require_noerr( err
, exit
);
6215 response
= plaintext
+ crypto_box_ZEROBYTES
;
6216 FPrintF( stdout
, "%.*{du:dnsmsg}", context
->printRawRData
? 1 : 0, response
, (size_t)( end
- response
) );
6217 Exit( kExitReason_ReceivedResponse
);
6220 if( err
) Exit( NULL
);
6223 //===========================================================================================================================
6225 //===========================================================================================================================
6227 static void DNSCryptProceed( void *inContext
)
6230 DNSCryptContext
* const context
= (DNSCryptContext
*) inContext
;
6232 err
= DNSCryptProcessCert( context
);
6233 require_noerr_quiet( err
, exit
);
6235 err
= DNSCryptBuildQuery( context
);
6236 require_noerr_quiet( err
, exit
);
6238 err
= DNSCryptSendQuery( context
);
6239 require_noerr_quiet( err
, exit
);
6242 if( err
) Exit( NULL
);
6245 //===========================================================================================================================
6246 // DNSCryptProcessCert
6247 //===========================================================================================================================
6249 static OSStatus
DNSCryptProcessCert( DNSCryptContext
*inContext
)
6252 const DNSCryptCert
* const cert
= (DNSCryptCert
*) inContext
->certPtr
;
6253 const uint8_t * const certEnd
= inContext
->certPtr
+ inContext
->certLen
;
6255 time_t startTimeSecs
, endTimeSecs
;
6258 unsigned long long tempLen
;
6260 DNSCryptPrintCertificate( cert
, inContext
->certLen
);
6262 if( memcmp( cert
->certMagic
, kDNSCryptCertMagic
, kDNSCryptCertMagicLength
) != 0 )
6264 FPrintF( stderr
, "DNSCrypt certificate magic %#H != %#H\n",
6265 cert
->certMagic
, kDNSCryptCertMagicLength
, INT_MAX
,
6266 kDNSCryptCertMagic
, kDNSCryptCertMagicLength
, INT_MAX
);
6271 startTimeSecs
= (time_t) ReadBig32( cert
->startTime
);
6272 endTimeSecs
= (time_t) ReadBig32( cert
->endTime
);
6274 gettimeofday( &now
, NULL
);
6275 if( now
.tv_sec
< startTimeSecs
)
6277 FPrintF( stderr
, "DNSCrypt certificate start time is in the future.\n" );
6281 if( now
.tv_sec
>= endTimeSecs
)
6283 FPrintF( stderr
, "DNSCrypt certificate has expired.\n" );
6288 signedLen
= (size_t)( certEnd
- cert
->signature
);
6289 tempBuf
= (uint8_t *) malloc( signedLen
);
6290 require_action( tempBuf
, exit
, err
= kNoMemoryErr
);
6291 err
= crypto_sign_open( tempBuf
, &tempLen
, cert
->signature
, signedLen
, inContext
->serverPublicSignKey
);
6295 FPrintF( stderr
, "DNSCrypt certificate failed verification.\n" );
6296 err
= kAuthenticationErr
;
6300 memcpy( inContext
->serverPublicKey
, cert
->publicKey
, crypto_box_PUBLICKEYBYTES
);
6301 memcpy( inContext
->clientMagic
, cert
->clientMagic
, kDNSCryptClientMagicLength
);
6303 err
= crypto_box_beforenm( inContext
->nmKey
, inContext
->serverPublicKey
, inContext
->clientSecretKey
);
6304 require_noerr( err
, exit
);
6306 inContext
->certPtr
= NULL
;
6307 inContext
->certLen
= 0;
6308 inContext
->msgLen
= 0;
6314 //===========================================================================================================================
6315 // DNSCryptBuildQuery
6316 //===========================================================================================================================
6318 static OSStatus
DNSCryptPadQuery( uint8_t *inMsgPtr
, size_t inMsgLen
, size_t inMaxLen
, size_t *outPaddedLen
);
6320 static OSStatus
DNSCryptBuildQuery( DNSCryptContext
*inContext
)
6323 DNSCryptQueryHeader
* const hdr
= (DNSCryptQueryHeader
*) inContext
->msgBuf
;
6324 uint8_t * const queryPtr
= (uint8_t *)( hdr
+ 1 );
6326 size_t paddedQueryLen
;
6327 const uint8_t * const msgLimit
= inContext
->msgBuf
+ sizeof( inContext
->msgBuf
);
6328 const uint8_t * padLimit
;
6329 uint8_t nonce
[ crypto_box_NONCEBYTES
];
6331 check_compile_time_code( sizeof( inContext
->msgBuf
) >= ( sizeof( DNSCryptQueryHeader
) + kDNSQueryMessageMaxLen
) );
6333 inContext
->queryID
= (uint16_t) Random32();
6334 err
= WriteDNSQueryMessage( queryPtr
, inContext
->queryID
, kDNSHeaderFlag_RecursionDesired
, inContext
->qname
,
6335 inContext
->qtype
, kDNSServiceClass_IN
, &queryLen
);
6336 require_noerr( err
, exit
);
6338 padLimit
= &queryPtr
[ queryLen
+ kDNSCryptMaxPadLength
];
6339 if( padLimit
> msgLimit
) padLimit
= msgLimit
;
6341 err
= DNSCryptPadQuery( queryPtr
, queryLen
, (size_t)( padLimit
- queryPtr
), &paddedQueryLen
);
6342 require_noerr( err
, exit
);
6344 memset( queryPtr
- crypto_box_ZEROBYTES
, 0, crypto_box_ZEROBYTES
);
6345 RandomBytes( inContext
->clientNonce
, kDNSCryptHalfNonceLength
);
6346 memcpy( nonce
, inContext
->clientNonce
, kDNSCryptHalfNonceLength
);
6347 memset( &nonce
[ kDNSCryptHalfNonceLength
], 0, kDNSCryptHalfNonceLength
);
6349 err
= crypto_box_afternm( queryPtr
- crypto_box_ZEROBYTES
, queryPtr
- crypto_box_ZEROBYTES
,
6350 paddedQueryLen
+ crypto_box_ZEROBYTES
, nonce
, inContext
->nmKey
);
6351 require_noerr( err
, exit
);
6353 memcpy( hdr
->clientMagic
, inContext
->clientMagic
, kDNSCryptClientMagicLength
);
6354 memcpy( hdr
->clientPublicKey
, inContext
->clientPublicKey
, crypto_box_PUBLICKEYBYTES
);
6355 memcpy( hdr
->clientNonce
, nonce
, kDNSCryptHalfNonceLength
);
6357 inContext
->msgLen
= (size_t)( &queryPtr
[ paddedQueryLen
] - inContext
->msgBuf
);
6363 static OSStatus
DNSCryptPadQuery( uint8_t *inMsgPtr
, size_t inMsgLen
, size_t inMaxLen
, size_t *outPaddedLen
)
6368 require_action_quiet( ( inMsgLen
+ kDNSCryptMinPadLength
) <= inMaxLen
, exit
, err
= kSizeErr
);
6370 paddedLen
= inMsgLen
+ kDNSCryptMinPadLength
+
6371 arc4random_uniform( (uint32_t)( inMaxLen
- ( inMsgLen
+ kDNSCryptMinPadLength
) + 1 ) );
6372 paddedLen
+= ( kDNSCryptBlockSize
- ( paddedLen
% kDNSCryptBlockSize
) );
6373 if( paddedLen
> inMaxLen
) paddedLen
= inMaxLen
;
6375 inMsgPtr
[ inMsgLen
] = 0x80;
6376 memset( &inMsgPtr
[ inMsgLen
+ 1 ], 0, paddedLen
- ( inMsgLen
+ 1 ) );
6378 if( outPaddedLen
) *outPaddedLen
= paddedLen
;
6385 //===========================================================================================================================
6386 // DNSCryptSendQuery
6387 //===========================================================================================================================
6389 static OSStatus
DNSCryptSendQuery( DNSCryptContext
*inContext
)
6392 SocketContext
* sockCtx
;
6393 SocketRef sock
= kInvalidSocketRef
;
6395 check( inContext
->msgLen
> 0 );
6396 check( !inContext
->readSource
);
6398 err
= UDPClientSocketOpen( AF_UNSPEC
, &inContext
->serverAddr
, 0, -1, NULL
, &sock
);
6399 require_noerr( err
, exit
);
6401 inContext
->sendTicks
= UpTicks();
6402 err
= SocketWriteAll( sock
, inContext
->msgBuf
, inContext
->msgLen
, 5 );
6403 require_noerr( err
, exit
);
6405 err
= SocketContextCreate( sock
, inContext
, &sockCtx
);
6406 require_noerr( err
, exit
);
6407 sock
= kInvalidSocketRef
;
6409 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, DNSCryptReceiveResponseHandler
, SocketContextCancelHandler
, sockCtx
,
6410 &inContext
->readSource
);
6411 if( err
) ForgetSocketContext( &sockCtx
);
6412 require_noerr( err
, exit
);
6414 dispatch_resume( inContext
->readSource
);
6417 ForgetSocket( &sock
);
6421 //===========================================================================================================================
6422 // DNSCryptPrintCertificate
6423 //===========================================================================================================================
6425 #define kCertTimeStrBufLen 32
6427 static char * CertTimeStr( time_t inTime
, char inBuffer
[ kCertTimeStrBufLen
] );
6429 static void DNSCryptPrintCertificate( const DNSCryptCert
*inCert
, size_t inLen
)
6431 time_t startTime
, endTime
;
6433 char timeBuf
[ kCertTimeStrBufLen
];
6435 check( inLen
>= kDNSCryptCertMinimumLength
);
6437 startTime
= (time_t) ReadBig32( inCert
->startTime
);
6438 endTime
= (time_t) ReadBig32( inCert
->endTime
);
6440 FPrintF( stdout
, "DNSCrypt certificate (%zu bytes):\n", inLen
);
6441 FPrintF( stdout
, "Cert Magic: %#H\n", inCert
->certMagic
, kDNSCryptCertMagicLength
, INT_MAX
);
6442 FPrintF( stdout
, "ES Version: %u\n", ReadBig16( inCert
->esVersion
) );
6443 FPrintF( stdout
, "Minor Version: %u\n", ReadBig16( inCert
->minorVersion
) );
6444 FPrintF( stdout
, "Signature: %H\n", inCert
->signature
, crypto_sign_BYTES
/ 2, INT_MAX
);
6445 FPrintF( stdout
, " %H\n", &inCert
->signature
[ crypto_sign_BYTES
/ 2 ], crypto_sign_BYTES
/ 2, INT_MAX
);
6446 FPrintF( stdout
, "Public Key: %H\n", inCert
->publicKey
, sizeof( inCert
->publicKey
), INT_MAX
);
6447 FPrintF( stdout
, "Client Magic: %H\n", inCert
->clientMagic
, kDNSCryptClientMagicLength
, INT_MAX
);
6448 FPrintF( stdout
, "Serial: %u\n", ReadBig32( inCert
->serial
) );
6449 FPrintF( stdout
, "Start Time: %u (%s)\n", (uint32_t) startTime
, CertTimeStr( startTime
, timeBuf
) );
6450 FPrintF( stdout
, "End Time: %u (%s)\n", (uint32_t) endTime
, CertTimeStr( endTime
, timeBuf
) );
6452 if( inLen
> kDNSCryptCertMinimumLength
)
6454 extLen
= (int)( inLen
- kDNSCryptCertMinimumLength
);
6455 FPrintF( stdout
, "Extensions: %.1H\n", inCert
->extensions
, extLen
, extLen
);
6457 FPrintF( stdout
, "\n" );
6460 static char * CertTimeStr( time_t inTime
, char inBuffer
[ kCertTimeStrBufLen
] )
6464 tm
= localtime( &inTime
);
6467 dlogassert( "localtime() returned a NULL pointer.\n" );
6472 strftime( inBuffer
, kCertTimeStrBufLen
, "%a %b %d %H:%M:%S %Z %Y", tm
);
6478 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
6480 //===========================================================================================================================
6482 //===========================================================================================================================
6486 const char * qnameStr
; // Name (QNAME) of the record being queried as a C string.
6487 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
6488 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
6489 int localPort
; // The port number to which the sockets are bound.
6490 int receiveSecs
; // After send, the amount of time to spend receiving.
6491 uint32_t ifIndex
; // Index of the interface over which to send the query.
6492 uint16_t qtype
; // The type (QTYPE) of the record being queried.
6493 Boolean isQU
; // True if the query is QU, i.e., requests unicast responses.
6494 Boolean allResponses
; // True if all mDNS messages received should be printed.
6495 Boolean printRawRData
; // True if RDATA should be printed as hexdumps.
6496 Boolean useIPv4
; // True if the query should be sent via IPv4 multicast.
6497 Boolean useIPv6
; // True if the query should be sent via IPv6 multicast.
6498 char ifName
[ IF_NAMESIZE
+ 1 ]; // Name of the interface over which to send the query.
6499 uint8_t qname
[ kDomainNameLengthMax
]; // Buffer to hold the QNAME in DNS label format.
6500 uint8_t msgBuf
[ 8940 ]; // Message buffer. 8940 is max size used by mDNSResponder.
6504 static void MDNSQueryPrintPrologue( const MDNSQueryContext
*inContext
);
6505 static void MDNSQueryReadHandler( void *inContext
);
6507 static void MDNSQueryCmd( void )
6510 MDNSQueryContext
* context
;
6511 struct sockaddr_in mcastAddr4
;
6512 struct sockaddr_in6 mcastAddr6
;
6513 SocketRef sockV4
= kInvalidSocketRef
;
6514 SocketRef sockV6
= kInvalidSocketRef
;
6516 const char * ifNamePtr
;
6518 unsigned int sendCount
;
6520 // Check command parameters.
6522 if( gMDNSQuery_ReceiveSecs
< -1 )
6524 FPrintF( stdout
, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs
);
6529 context
= (MDNSQueryContext
*) calloc( 1, sizeof( *context
) );
6530 require_action( context
, exit
, err
= kNoMemoryErr
);
6532 context
->qnameStr
= gMDNSQuery_Name
;
6533 context
->receiveSecs
= gMDNSQuery_ReceiveSecs
;
6534 context
->isQU
= gMDNSQuery_IsQU
? true : false;
6535 context
->allResponses
= gMDNSQuery_AllResponses
? true : false;
6536 context
->printRawRData
= gMDNSQuery_RawRData
? true : false;
6537 context
->useIPv4
= ( gMDNSQuery_UseIPv4
|| !gMDNSQuery_UseIPv6
) ? true : false;
6538 context
->useIPv6
= ( gMDNSQuery_UseIPv6
|| !gMDNSQuery_UseIPv4
) ? true : false;
6540 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
6541 require_noerr_quiet( err
, exit
);
6543 ifNamePtr
= if_indextoname( context
->ifIndex
, context
->ifName
);
6544 require_action( ifNamePtr
, exit
, err
= kNameErr
);
6546 err
= RecordTypeFromArgString( gMDNSQuery_Type
, &context
->qtype
);
6547 require_noerr( err
, exit
);
6549 // Set up IPv4 socket.
6551 if( context
->useIPv4
)
6553 err
= ServerSocketOpen( AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
,
6554 gMDNSQuery_SourcePort
? gMDNSQuery_SourcePort
: ( context
->isQU
? context
->localPort
: kMDNSPort
),
6555 &context
->localPort
, kSocketBufferSize_DontSet
, &sockV4
);
6556 require_noerr( err
, exit
);
6558 err
= SocketSetMulticastInterface( sockV4
, ifNamePtr
, context
->ifIndex
);
6559 require_noerr( err
, exit
);
6561 err
= setsockopt( sockV4
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *) &(uint8_t){ 1 }, (socklen_t
) sizeof( uint8_t ) );
6562 err
= map_socket_noerr_errno( sockV4
, err
);
6563 require_noerr( err
, exit
);
6565 memset( &mcastAddr4
, 0, sizeof( mcastAddr4
) );
6566 SIN_LEN_SET( &mcastAddr4
);
6567 mcastAddr4
.sin_family
= AF_INET
;
6568 mcastAddr4
.sin_port
= htons( kMDNSPort
);
6569 mcastAddr4
.sin_addr
.s_addr
= htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251
6571 if( !context
->isQU
&& ( context
->localPort
== kMDNSPort
) )
6573 err
= SocketJoinMulticast( sockV4
, &mcastAddr4
, ifNamePtr
, context
->ifIndex
);
6574 require_noerr( err
, exit
);
6578 // Set up IPv6 socket.
6580 if( context
->useIPv6
)
6582 err
= ServerSocketOpen( AF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
,
6583 gMDNSQuery_SourcePort
? gMDNSQuery_SourcePort
: ( context
->isQU
? context
->localPort
: kMDNSPort
),
6584 &context
->localPort
, kSocketBufferSize_DontSet
, &sockV6
);
6585 require_noerr( err
, exit
);
6587 err
= SocketSetMulticastInterface( sockV6
, ifNamePtr
, context
->ifIndex
);
6588 require_noerr( err
, exit
);
6590 err
= setsockopt( sockV6
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *) &(int){ 1 }, (socklen_t
) sizeof( int ) );
6591 err
= map_socket_noerr_errno( sockV6
, err
);
6592 require_noerr( err
, exit
);
6594 memset( &mcastAddr6
, 0, sizeof( mcastAddr6
) );
6595 SIN6_LEN_SET( &mcastAddr6
);
6596 mcastAddr6
.sin6_family
= AF_INET6
;
6597 mcastAddr6
.sin6_port
= htons( kMDNSPort
);
6598 mcastAddr6
.sin6_addr
.s6_addr
[ 0 ] = 0xFF; // mDNS IPv6 multicast address FF02::FB
6599 mcastAddr6
.sin6_addr
.s6_addr
[ 1 ] = 0x02;
6600 mcastAddr6
.sin6_addr
.s6_addr
[ 15 ] = 0xFB;
6602 if( !context
->isQU
&& ( context
->localPort
== kMDNSPort
) )
6604 err
= SocketJoinMulticast( sockV6
, &mcastAddr6
, ifNamePtr
, context
->ifIndex
);
6605 require_noerr( err
, exit
);
6609 // Craft mDNS query message.
6611 check_compile_time_code( sizeof( context
->msgBuf
) >= kDNSQueryMessageMaxLen
);
6612 err
= WriteDNSQueryMessage( context
->msgBuf
, kDefaultMDNSMessageID
, kDefaultMDNSQueryFlags
, context
->qnameStr
,
6613 context
->qtype
, context
->isQU
? ( kDNSServiceClass_IN
| kQClassUnicastResponseBit
) : kDNSServiceClass_IN
, &msgLen
);
6614 require_noerr( err
, exit
);
6618 MDNSQueryPrintPrologue( context
);
6620 // Send mDNS query message.
6623 if( IsValidSocket( sockV4
) )
6625 n
= sendto( sockV4
, context
->msgBuf
, msgLen
, 0, (struct sockaddr
*) &mcastAddr4
, (socklen_t
) sizeof( mcastAddr4
) );
6626 err
= map_socket_value_errno( sockV4
, n
== (ssize_t
) msgLen
, n
);
6629 FPrintF( stderr
, "*** Failed to send query on IPv4 socket with error %#m\n", err
);
6630 ForgetSocket( &sockV4
);
6637 if( IsValidSocket( sockV6
) )
6639 n
= sendto( sockV6
, context
->msgBuf
, msgLen
, 0, (struct sockaddr
*) &mcastAddr6
, (socklen_t
) sizeof( mcastAddr6
) );
6640 err
= map_socket_value_errno( sockV6
, n
== (ssize_t
) msgLen
, n
);
6643 FPrintF( stderr
, "*** Failed to send query on IPv6 socket with error %#m\n", err
);
6644 ForgetSocket( &sockV6
);
6651 require_action_quiet( sendCount
> 0, exit
, err
= kUnexpectedErr
);
6653 // If there's no wait period after the send, then exit.
6655 if( context
->receiveSecs
== 0 ) goto exit
;
6657 // Create dispatch read sources for socket(s).
6659 if( IsValidSocket( sockV4
) )
6661 SocketContext
* sockCtx
;
6663 err
= SocketContextCreate( sockV4
, context
, &sockCtx
);
6664 require_noerr( err
, exit
);
6665 sockV4
= kInvalidSocketRef
;
6667 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, MDNSQueryReadHandler
, SocketContextCancelHandler
, sockCtx
,
6668 &context
->readSourceV4
);
6669 if( err
) ForgetSocketContext( &sockCtx
);
6670 require_noerr( err
, exit
);
6672 dispatch_resume( context
->readSourceV4
);
6675 if( IsValidSocket( sockV6
) )
6677 SocketContext
* sockCtx
;
6679 err
= SocketContextCreate( sockV6
, context
, &sockCtx
);
6680 require_noerr( err
, exit
);
6681 sockV6
= kInvalidSocketRef
;
6683 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, MDNSQueryReadHandler
, SocketContextCancelHandler
, sockCtx
,
6684 &context
->readSourceV6
);
6685 if( err
) ForgetSocketContext( &sockCtx
);
6686 require_noerr( err
, exit
);
6688 dispatch_resume( context
->readSourceV6
);
6691 if( context
->receiveSecs
> 0 )
6693 dispatch_after_f( dispatch_time_seconds( context
->receiveSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
6699 ForgetSocket( &sockV4
);
6700 ForgetSocket( &sockV6
);
6701 if( err
) exit( 1 );
6704 //===========================================================================================================================
6705 // MDNSQueryPrintPrologue
6706 //===========================================================================================================================
6708 static void MDNSQueryPrintPrologue( const MDNSQueryContext
*inContext
)
6710 const int receiveSecs
= inContext
->receiveSecs
;
6712 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, inContext
->ifName
);
6713 FPrintF( stdout
, "Name: %s\n", inContext
->qnameStr
);
6714 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->qtype
), inContext
->qtype
);
6715 FPrintF( stdout
, "Class: IN (%s)\n", inContext
->isQU
? "QU" : "QM" );
6716 FPrintF( stdout
, "Local port: %d\n", inContext
->localPort
);
6717 FPrintF( stdout
, "IP protocols: %?s%?s%?s\n",
6718 inContext
->useIPv4
, "IPv4", ( inContext
->useIPv4
&& inContext
->useIPv6
), ", ", inContext
->useIPv6
, "IPv6" );
6719 FPrintF( stdout
, "Receive duration: " );
6720 if( receiveSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", receiveSecs
, receiveSecs
!= 1, 's' );
6721 else FPrintF( stdout
, "∞\n" );
6722 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
6725 //===========================================================================================================================
6726 // MDNSQueryReadHandler
6727 //===========================================================================================================================
6729 static void MDNSQueryReadHandler( void *inContext
)
6733 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
6734 MDNSQueryContext
* const context
= (MDNSQueryContext
*) sockCtx
->userContext
;
6736 sockaddr_ip fromAddr
;
6737 Boolean foundAnswer
= false;
6739 gettimeofday( &now
, NULL
);
6741 err
= SocketRecvFrom( sockCtx
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &msgLen
, &fromAddr
,
6742 sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6743 require_noerr( err
, exit
);
6745 if( !context
->allResponses
&& ( msgLen
>= kDNSHeaderLength
) )
6747 const uint8_t * ptr
;
6748 const DNSHeader
* const hdr
= (DNSHeader
*) context
->msgBuf
;
6749 unsigned int rrCount
, i
;
6750 uint16_t type
, class;
6751 uint8_t name
[ kDomainNameLengthMax
];
6753 err
= DNSMessageGetAnswerSection( context
->msgBuf
, msgLen
, &ptr
);
6754 require_noerr( err
, exit
);
6756 if( context
->qname
[ 0 ] == 0 )
6758 err
= DomainNameAppendString( context
->qname
, context
->qnameStr
, NULL
);
6759 require_noerr( err
, exit
);
6762 rrCount
= DNSHeaderGetAnswerCount( hdr
) + DNSHeaderGetAuthorityCount( hdr
) + DNSHeaderGetAdditionalCount( hdr
);
6763 for( i
= 0; i
< rrCount
; ++i
)
6765 err
= DNSMessageExtractRecord( context
->msgBuf
, msgLen
, ptr
, name
, &type
, &class, NULL
, NULL
, NULL
, &ptr
);
6766 require_noerr( err
, exit
);
6768 if( ( ( context
->qtype
== kDNSServiceType_ANY
) || ( type
== context
->qtype
) ) &&
6769 DomainNameEqual( name
, context
->qname
) )
6776 if( context
->allResponses
|| foundAnswer
)
6778 FPrintF( stdout
, "---\n" );
6779 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
6780 FPrintF( stdout
, "Source: %##a\n", &fromAddr
);
6781 FPrintF( stdout
, "Message size: %zu\n\n%#.*{du:dnsmsg}",
6782 msgLen
, context
->printRawRData
? 1 : 0, context
->msgBuf
, msgLen
);
6786 if( err
) exit( 1 );
6789 //===========================================================================================================================
6791 //===========================================================================================================================
6793 static void PIDToUUIDCmd( void )
6797 struct proc_uniqidentifierinfo info
;
6799 n
= proc_pidinfo( gPIDToUUID_PID
, PROC_PIDUNIQIDENTIFIERINFO
, 1, &info
, sizeof( info
) );
6800 require_action_quiet( n
== (int) sizeof( info
), exit
, err
= kUnknownErr
);
6802 FPrintF( stdout
, "%#U\n", info
.p_uuid
);
6806 if( err
) exit( 1 );
6809 //===========================================================================================================================
6811 //===========================================================================================================================
6813 typedef uint32_t DNSServerEventType
;
6814 #define kDNSServerEvent_Started 1
6815 #define kDNSServerEvent_Stopped 2
6817 typedef struct DNSServerPrivate
* DNSServerRef
;
6821 DNSServerRef server
; // Reference to the DNS server.
6822 dispatch_source_t sigIntSource
; // Dispatch SIGINT source.
6823 dispatch_source_t sigTermSource
; // Dispatch SIGTERM source.
6824 #if( TARGET_OS_DARWIN )
6825 dispatch_source_t processMonitor
; // Process monitor source for process being followed, if any.
6826 pid_t followPID
; // PID of process being followed (we exit when they exit), if any.
6827 Boolean resolverRegistered
; // True if system DNS settings contains a resolver entry for server.
6829 Boolean loopbackOnly
; // True if the server should be bound to the loopback interface.
6830 Boolean serverStarted
; // True if the server was successfully started.
6831 Boolean calledStop
; // True if the server was explicitly stopped.
6833 } DNSServerCmdContext
;
6835 typedef void ( *DNSServerEventHandler_f
)( DNSServerEventType inType
, void *inContext
);
6837 CFTypeID
DNSServerGetTypeID( void );
6840 dispatch_queue_t inQueue
,
6841 DNSServerEventHandler_f inEventHandler
,
6842 void * inEventContext
,
6843 int inResponseDelayMs
,
6844 Boolean inLoopbackOnly
,
6845 DNSServerRef
* outServer
);
6846 static void DNSServerStart( DNSServerRef inServer
);
6847 static void DNSServerStop( DNSServerRef inServer
);
6849 static void DNSServerCmdContextFree( DNSServerCmdContext
*inContext
);
6850 static void DNSServerCmdEventHandler( DNSServerEventType inType
, void *inContext
);
6851 static void DNSServerCmdSigIntHandler( void *inContext
);
6852 static void DNSServerCmdSigTermHandler( void *inContext
);
6853 #if( TARGET_OS_DARWIN )
6854 static void DNSServerCmdFollowedProcessHandler( void *inContext
);
6857 ulog_define_ex( "com.apple.dnssdutil", DNSServer
, kLogLevelInfo
, kLogFlags_None
, "DNSServer", NULL
);
6858 #define ds_ulog( LEVEL, ... ) ulog( &log_category_from_name( DNSServer ), (LEVEL), __VA_ARGS__ )
6860 static void DNSServerCmd( void )
6863 DNSServerCmdContext
* context
;
6865 context
= (DNSServerCmdContext
*) calloc( 1, sizeof( *context
) );
6866 require_action( context
, exit
, err
= kNoMemoryErr
);
6868 context
->loopbackOnly
= gDNSServer_LoopbackOnly
? true : false;
6870 #if( TARGET_OS_DARWIN )
6871 if( gDNSServer_FollowPID
)
6875 err
= StringToLongLong( gDNSServer_FollowPID
, &value
);
6876 if( !err
&& ( value
< 0 ) ) err
= kValueErr
;
6879 FPrintF( stderr
, "Invalid followPID argument \"%s\".\n", gDNSServer_FollowPID
);
6882 context
->followPID
= (pid_t
) value
;
6884 err
= DispatchProcessMonitorCreate( context
->followPID
, DISPATCH_PROC_EXIT
, dispatch_get_main_queue(),
6885 DNSServerCmdFollowedProcessHandler
, NULL
, context
, &context
->processMonitor
);
6886 require_noerr( err
, exit
);
6887 dispatch_resume( context
->processMonitor
);
6891 context
->followPID
= -1;
6895 signal( SIGINT
, SIG_IGN
);
6896 err
= DispatchSignalSourceCreate( SIGINT
, DNSServerCmdSigIntHandler
, context
, &context
->sigIntSource
);
6897 require_noerr( err
, exit
);
6898 dispatch_resume( context
->sigIntSource
);
6900 signal( SIGTERM
, SIG_IGN
);
6901 err
= DispatchSignalSourceCreate( SIGTERM
, DNSServerCmdSigTermHandler
, context
, &context
->sigTermSource
);
6902 require_noerr( err
, exit
);
6903 dispatch_resume( context
->sigTermSource
);
6905 if( gDNSServer_Foreground
)
6907 LogControl( "DNSServer:output=file;stdout,DNSServer:flags=time;prefix" );
6910 if( ( gDNSServer_DefaultTTL
< 0 ) || ( gDNSServer_DefaultTTL
> INT32_MAX
) )
6912 ds_ulog( kLogLevelError
, "The default TTL %d provided by user is out-of-range. Will use %d instead.\n",
6913 gDNSServer_DefaultTTL
, kDNSServerDefaultTTL
);
6914 gDNSServer_DefaultTTL
= kDNSServerDefaultTTL
;
6917 err
= DNSServerCreate( dispatch_get_main_queue(), DNSServerCmdEventHandler
, context
, gDNSServer_ResponseDelayMs
,
6918 context
->loopbackOnly
, &context
->server
);
6919 require_noerr( err
, exit
);
6921 DNSServerStart( context
->server
);
6925 ds_ulog( kLogLevelError
, "Failed to start DNS server: %#m\n", err
);
6926 if( context
) DNSServerCmdContextFree( context
);
6927 if( err
) exit( 1 );
6930 //===========================================================================================================================
6931 // DNSServerCmdContextFree
6932 //===========================================================================================================================
6934 static void DNSServerCmdContextFree( DNSServerCmdContext
*inContext
)
6936 ForgetCF( &inContext
->server
);
6937 dispatch_source_forget( &inContext
->sigIntSource
);
6938 dispatch_source_forget( &inContext
->sigTermSource
);
6939 dispatch_source_forget( &inContext
->processMonitor
);
6943 //===========================================================================================================================
6944 // DNSServerCmdEventHandler
6945 //===========================================================================================================================
6947 #if( TARGET_OS_DARWIN )
6948 static OSStatus
_DNSServerCmdRegisterResolver( void );
6949 static OSStatus
_DNSServerCmdUnregisterResolver( void );
6952 static void DNSServerCmdEventHandler( DNSServerEventType inType
, void *inContext
)
6954 DNSServerCmdContext
* const context
= (DNSServerCmdContext
*) inContext
;
6955 #if( TARGET_OS_DARWIN )
6959 if( inType
== kDNSServerEvent_Started
)
6961 context
->serverStarted
= true;
6962 #if( TARGET_OS_DARWIN )
6963 err
= _DNSServerCmdRegisterResolver();
6966 ds_ulog( kLogLevelError
, "Failed to add resolver to DNS configuration for \"d.test.\" domain: %#m\n", err
);
6967 if( context
->loopbackOnly
) exit( 1 );
6971 context
->resolverRegistered
= true;
6975 else if( inType
== kDNSServerEvent_Stopped
)
6977 #if( TARGET_OS_DARWIN )
6978 if( context
->resolverRegistered
)
6980 err
= _DNSServerCmdUnregisterResolver();
6983 ds_ulog( kLogLevelError
, "Failed to remove resolver from DNS configuration: %#m\n", err
);
6987 context
->resolverRegistered
= false;
6991 if( !context
->calledStop
)
6993 ds_ulog( kLogLevelError
, "The server stopped unexpectedly.\n" );
6997 DNSServerCmdContextFree( context
);
7001 #if( TARGET_OS_DARWIN )
7002 //===========================================================================================================================
7003 // _DNSServerCmdRegisterResolver
7004 //===========================================================================================================================
7006 static OSStatus
_DNSServerCmdRegisterResolver( void )
7009 SCDynamicStoreRef store
;
7010 CFPropertyListRef plist
= NULL
;
7011 CFStringRef key
= NULL
;
7012 const uint32_t loopbackV4
= htonl( INADDR_LOOPBACK
);
7015 store
= SCDynamicStoreCreate( NULL
, CFSTR( "com.apple.dnssdutil" ), NULL
, NULL
);
7016 err
= map_scerror( store
);
7017 require_noerr( err
, exit
);
7019 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
7031 kSCPropNetDNSSupplementalMatchDomains
, "d.test.",
7032 kSCPropNetDNSServerAddresses
, &loopbackV4
, in6addr_loopback
.s6_addr
);
7033 require_noerr( err
, exit
);
7035 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
,
7036 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS
);
7037 require_action( key
, exit
, err
= kUnknownErr
);
7039 success
= SCDynamicStoreSetValue( store
, key
, plist
);
7040 require_action( success
, exit
, err
= kUnknownErr
);
7043 CFReleaseNullSafe( store
);
7044 CFReleaseNullSafe( plist
);
7045 CFReleaseNullSafe( key
);
7049 //===========================================================================================================================
7050 // _DNSServerCmdUnregisterResolver
7051 //===========================================================================================================================
7053 static OSStatus
_DNSServerCmdUnregisterResolver( void )
7056 SCDynamicStoreRef store
;
7057 CFStringRef key
= NULL
;
7060 store
= SCDynamicStoreCreate( NULL
, CFSTR( "com.apple.dnssdutil" ), NULL
, NULL
);
7061 err
= map_scerror( store
);
7062 require_noerr( err
, exit
);
7064 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
,
7065 CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS
);
7066 require_action( key
, exit
, err
= kUnknownErr
);
7068 success
= SCDynamicStoreRemoveValue( store
, key
);
7069 require_action( success
, exit
, err
= kUnknownErr
);
7072 CFReleaseNullSafe( store
);
7073 CFReleaseNullSafe( key
);
7078 //===========================================================================================================================
7079 // DNSServerCmdSigIntHandler
7080 //===========================================================================================================================
7082 static void _DNSServerCmdExternalExit( DNSServerCmdContext
*inContext
, int inSignal
);
7084 static void DNSServerCmdSigIntHandler( void *inContext
)
7086 _DNSServerCmdExternalExit( (DNSServerCmdContext
*) inContext
, SIGINT
);
7089 //===========================================================================================================================
7090 // DNSServerCmdSigTermHandler
7091 //===========================================================================================================================
7093 static void DNSServerCmdSigTermHandler( void *inContext
)
7095 _DNSServerCmdExternalExit( (DNSServerCmdContext
*) inContext
, SIGTERM
);
7098 #if( TARGET_OS_DARWIN )
7099 //===========================================================================================================================
7100 // DNSServerCmdFollowedProcessHandler
7101 //===========================================================================================================================
7103 static void DNSServerCmdFollowedProcessHandler( void *inContext
)
7105 DNSServerCmdContext
* const context
= (DNSServerCmdContext
*) inContext
;
7107 if( dispatch_source_get_data( context
->processMonitor
) & DISPATCH_PROC_EXIT
)
7109 _DNSServerCmdExternalExit( context
, 0 );
7114 //===========================================================================================================================
7115 // _DNSServerCmdExternalExit
7116 //===========================================================================================================================
7118 #define SignalNumberToString( X ) ( \
7119 ( (X) == SIGINT ) ? "SIGINT" : \
7120 ( (X) == SIGTERM ) ? "SIGTERM" : \
7123 static void _DNSServerCmdExternalExit( DNSServerCmdContext
*inContext
, int inSignal
)
7127 #if( TARGET_OS_DARWIN )
7130 ds_ulog( kLogLevelNotice
, "Exiting: followed process (%lld) exited\n", (int64_t) inContext
->followPID
);
7135 ds_ulog( kLogLevelNotice
, "Exiting: received signal %d (%s)\n", inSignal
, SignalNumberToString( inSignal
) );
7138 #if( TARGET_OS_DARWIN )
7139 if( inContext
->resolverRegistered
)
7141 err
= _DNSServerCmdUnregisterResolver();
7144 ds_ulog( kLogLevelError
, "Failed to remove resolver from DNS configuration: %#m\n", err
);
7147 inContext
->resolverRegistered
= false;
7150 if( inContext
->serverStarted
)
7152 DNSServerStop( inContext
->server
);
7153 inContext
->calledStop
= true;
7158 exit( err
? 1 : 0 );
7161 //===========================================================================================================================
7163 //===========================================================================================================================
7165 typedef struct DNSDelayedResponse DNSDelayedResponse
;
7166 struct DNSDelayedResponse
7168 DNSDelayedResponse
* next
;
7169 sockaddr_ip clientAddr
;
7170 uint64_t targetTicks
;
7175 #define DNSScheduledResponseFree( X ) do { ForgetMem( &(X)->msgPtr ) ; free( X ); } while( 0 )
7177 struct DNSServerPrivate
7179 CFRuntimeBase base
; // CF object base.
7180 dispatch_queue_t queue
; // Queue for DNS server's events.
7181 dispatch_source_t readSourceUDPv4
; // Read source for IPv4 UDP socket.
7182 dispatch_source_t readSourceUDPv6
; // Read source for IPv6 UDP socket.
7183 dispatch_source_t readSourceTCPv4
; // Read source for IPv4 TCP socket.
7184 dispatch_source_t readSourceTCPv6
; // Read source for IPv6 TCP socket.
7185 DNSServerEventHandler_f eventHandler
;
7186 void * eventContext
;
7187 DNSDelayedResponse
* responseList
;
7188 int responseDelayMs
;
7189 dispatch_source_t responseTimer
;
7190 Boolean loopbackOnly
;
7194 CF_CLASS_DEFINE( DNSServer
);
7198 dispatch_queue_t inQueue
,
7199 DNSServerEventHandler_f inEventHandler
,
7200 void * inEventContext
,
7201 int inResponseDelayMs
,
7202 Boolean inLoopbackOnly
,
7203 DNSServerRef
* outServer
)
7206 DNSServerRef obj
= NULL
;
7208 CF_OBJECT_CREATE( DNSServer
, obj
, err
, exit
);
7210 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
7211 obj
->eventHandler
= inEventHandler
;
7212 obj
->eventContext
= inEventContext
;
7213 obj
->responseDelayMs
= inResponseDelayMs
;
7214 if( inLoopbackOnly
) obj
->loopbackOnly
= true;
7221 CFReleaseNullSafe( obj
);
7225 //===========================================================================================================================
7226 // _DNSServerFinalize
7227 //===========================================================================================================================
7229 static void _DNSServerFinalize( CFTypeRef inObj
)
7231 DNSServerRef
const me
= (DNSServerRef
) inObj
;
7233 check( !me
->readSourceUDPv4
);
7234 check( !me
->readSourceUDPv6
);
7235 check( !me
->readSourceTCPv4
);
7236 check( !me
->readSourceTCPv6
);
7237 check( !me
->responseTimer
);
7238 dispatch_forget( &me
->queue
);
7241 //===========================================================================================================================
7243 //===========================================================================================================================
7245 static void _DNSServerStart( void *inContext
);
7246 static void _DNSServerUDPReadHandler( void *inContext
);
7247 static void _DNSServerTCPReadHandler( void *inContext
);
7249 static void DNSServerStart( DNSServerRef me
)
7252 dispatch_async_f( me
->queue
, me
, _DNSServerStart
);
7255 static void _DNSServerStart( void *inContext
)
7258 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7259 SocketRef sock
= kInvalidSocketRef
;
7260 SocketContext
* sockCtx
= NULL
;
7261 const uint32_t loopbackV4
= htonl( INADDR_LOOPBACK
);
7263 // Create IPv4 UDP socket.
7265 err
= _ServerSocketOpenEx2( AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
, me
->loopbackOnly
? &loopbackV4
: NULL
,
7266 kDNSPort
, NULL
, kSocketBufferSize_DontSet
, me
->loopbackOnly
? true : false, &sock
);
7267 require_noerr( err
, exit
);
7269 // Create read source for IPv4 UDP socket.
7271 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7272 require_noerr( err
, exit
);
7273 sock
= kInvalidSocketRef
;
7275 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerUDPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7276 &me
->readSourceUDPv4
);
7277 require_noerr( err
, exit
);
7278 dispatch_resume( me
->readSourceUDPv4
);
7281 // Create IPv6 UDP socket.
7283 err
= _ServerSocketOpenEx2( AF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
, me
->loopbackOnly
? &in6addr_loopback
: NULL
,
7284 kDNSPort
, NULL
, kSocketBufferSize_DontSet
, me
->loopbackOnly
? true : false, &sock
);
7285 require_noerr( err
, exit
);
7287 // Create read source for IPv6 UDP socket.
7289 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7290 require_noerr( err
, exit
);
7291 sock
= kInvalidSocketRef
;
7293 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerUDPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7294 &me
->readSourceUDPv6
);
7295 require_noerr( err
, exit
);
7296 dispatch_resume( me
->readSourceUDPv6
);
7299 // Create IPv4 TCP socket.
7301 err
= _ServerSocketOpenEx2( AF_INET
, SOCK_STREAM
, IPPROTO_TCP
, me
->loopbackOnly
? &loopbackV4
: NULL
,
7302 kDNSPort
, NULL
, kSocketBufferSize_DontSet
, false, &sock
);
7303 require_noerr( err
, exit
);
7305 // Create read source for IPv4 TCP socket.
7307 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7308 require_noerr( err
, exit
);
7309 sock
= kInvalidSocketRef
;
7311 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerTCPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7312 &me
->readSourceTCPv4
);
7313 require_noerr( err
, exit
);
7314 dispatch_resume( me
->readSourceTCPv4
);
7317 // Create IPv6 TCP socket.
7319 err
= _ServerSocketOpenEx2( AF_INET6
, SOCK_STREAM
, IPPROTO_TCP
, me
->loopbackOnly
? &in6addr_loopback
: NULL
,
7320 kDNSPort
, NULL
, kSocketBufferSize_DontSet
, false, &sock
);
7321 require_noerr( err
, exit
);
7323 // Create read source for IPv6 TCP socket.
7325 err
= SocketContextCreate( sock
, me
, &sockCtx
);
7326 require_noerr( err
, exit
);
7327 sock
= kInvalidSocketRef
;
7329 err
= DispatchReadSourceCreate( sockCtx
->sock
, me
->queue
, _DNSServerTCPReadHandler
, SocketContextCancelHandler
, sockCtx
,
7330 &me
->readSourceTCPv6
);
7331 require_noerr( err
, exit
);
7332 dispatch_resume( me
->readSourceTCPv6
);
7336 if( me
->eventHandler
) me
->eventHandler( kDNSServerEvent_Started
, me
->eventContext
);
7339 ForgetSocket( &sock
);
7340 if( sockCtx
) SocketContextRelease( sockCtx
);
7341 if( err
) DNSServerStop( me
);
7345 //===========================================================================================================================
7347 //===========================================================================================================================
7349 static void _DNSServerStop( void *inContext
);
7350 static void _DNSServerStop2( void *inContext
);
7352 static void DNSServerStop( DNSServerRef me
)
7355 dispatch_async_f( me
->queue
, me
, _DNSServerStop
);
7358 static void _DNSServerStop( void *inContext
)
7360 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7361 DNSDelayedResponse
* resp
;
7363 dispatch_source_forget( &me
->readSourceUDPv4
);
7364 dispatch_source_forget( &me
->readSourceUDPv6
);
7365 dispatch_source_forget( &me
->readSourceTCPv4
);
7366 dispatch_source_forget( &me
->readSourceTCPv6
);
7367 dispatch_source_forget( &me
->responseTimer
);
7369 while( ( resp
= me
->responseList
) != NULL
)
7371 me
->responseList
= resp
->next
;
7372 DNSScheduledResponseFree( resp
);
7375 dispatch_async_f( me
->queue
, me
, _DNSServerStop2
);
7378 static void _DNSServerStop2( void *inContext
)
7380 DNSServerRef
const me
= (DNSServerRef
) inContext
;
7385 if( me
->eventHandler
) me
->eventHandler( kDNSServerEvent_Stopped
, me
->eventContext
);
7391 //===========================================================================================================================
7392 // _DNSServerUDPReadHandler
7393 //===========================================================================================================================
7396 _DNSServerAnswerQuery(
7397 const uint8_t * inQueryPtr
,
7400 uint8_t ** outResponsePtr
,
7401 size_t * outResponseLen
);
7403 #define _DNSServerAnswerQueryForUDP( IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7404 _DNSServerAnswerQuery( IN_QUERY_PTR, IN_QUERY_LEN, false, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7406 #define _DNSServerAnswerQueryForTCP( IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
7407 _DNSServerAnswerQuery( IN_QUERY_PTR, IN_QUERY_LEN, true, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
7409 static void _DNSServerUDPDelayedSend( void *inContext
);
7411 static void _DNSServerUDPReadHandler( void *inContext
)
7414 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
7415 DNSServerRef
const me
= (DNSServerRef
) sockCtx
->userContext
;
7418 sockaddr_ip clientAddr
;
7419 socklen_t clientAddrLen
;
7420 uint8_t * responsePtr
= NULL
; // malloc'd
7424 gettimeofday( &now
, NULL
);
7428 clientAddrLen
= (socklen_t
) sizeof( clientAddr
);
7429 n
= recvfrom( sockCtx
->sock
, (char *) msg
, sizeof( msg
), 0, &clientAddr
.sa
, &clientAddrLen
);
7430 err
= map_socket_value_errno( sockCtx
->sock
, n
>= 0, n
);
7431 require_noerr( err
, exit
);
7433 ds_ulog( kLogLevelInfo
, "UDP server received %zd bytes from %##a at %{du:time}.\n", n
, &clientAddr
, &now
);
7435 if( n
< kDNSHeaderLength
)
7437 ds_ulog( kLogLevelInfo
, "UDP DNS message is too small (%zd < %d).\n", n
, kDNSHeaderLength
);
7441 ds_ulog( kLogLevelInfo
, "UDP received message:\n\n%1{du:dnsmsg}", msg
, (size_t) n
);
7445 err
= _DNSServerAnswerQueryForUDP( msg
, (size_t) n
, &responsePtr
, &responseLen
);
7446 require_noerr_quiet( err
, exit
);
7448 // Schedule response.
7450 if( me
->responseDelayMs
> 0 )
7452 DNSDelayedResponse
* resp
;
7453 DNSDelayedResponse
** ptr
;
7454 DNSDelayedResponse
* newResp
;
7456 newResp
= (DNSDelayedResponse
*) calloc( 1, sizeof( *newResp
) );
7457 require_action( newResp
, exit
, err
= kNoMemoryErr
);
7459 SockAddrCopy( &clientAddr
, &newResp
->clientAddr
);
7460 newResp
->targetTicks
= UpTicks() + MillisecondsToUpTicks( (uint64_t) me
->responseDelayMs
);
7461 newResp
->msgLen
= responseLen
;
7462 newResp
->msgPtr
= responsePtr
;
7465 for( ptr
= &me
->responseList
; ( resp
= *ptr
) != NULL
; ptr
= &resp
->next
)
7467 if( newResp
->targetTicks
< resp
->targetTicks
) break;
7470 newResp
->next
= resp
;
7473 if( me
->responseList
== newResp
)
7475 dispatch_source_forget( &me
->responseTimer
);
7477 err
= DispatchTimerCreate( dispatch_time_milliseconds( me
->responseDelayMs
), DISPATCH_TIME_FOREVER
,
7478 ( (uint64_t) me
->responseDelayMs
) * kNanosecondsPerMillisecond
/ 10, me
->queue
,
7479 _DNSServerUDPDelayedSend
, NULL
, sockCtx
, &me
->responseTimer
);
7480 require_noerr( err
, exit
);
7481 dispatch_resume( me
->responseTimer
);
7486 ds_ulog( kLogLevelInfo
, "UDP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen
, responsePtr
, responseLen
);
7488 n
= sendto( sockCtx
->sock
, (char *) responsePtr
, responseLen
, 0, &clientAddr
.sa
, clientAddrLen
);
7489 err
= map_socket_value_errno( sockCtx
->sock
, n
== (ssize_t
) responseLen
, n
);
7490 require_noerr( err
, exit
);
7494 FreeNullSafe( responsePtr
);
7498 static void _DNSServerUDPDelayedSend( void *inContext
)
7501 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
7502 DNSServerRef
const me
= (DNSServerRef
) sockCtx
->userContext
;
7503 DNSDelayedResponse
* resp
;
7506 DNSDelayedResponse
* freeList
= NULL
;
7508 dispatch_source_forget( &me
->responseTimer
);
7510 nowTicks
= UpTicks();
7511 while( ( resp
= me
->responseList
) != NULL
)
7513 if( resp
->targetTicks
> nowTicks
) break;
7514 me
->responseList
= resp
->next
;
7516 ds_ulog( kLogLevelInfo
, "UDP sending %zu byte response (delayed):\n\n%1{du:dnsmsg}",
7517 resp
->msgLen
, resp
->msgPtr
, resp
->msgLen
);
7519 n
= sendto( sockCtx
->sock
, (char *) resp
->msgPtr
, resp
->msgLen
, 0, &resp
->clientAddr
.sa
,
7520 SockAddrGetSize( &resp
->clientAddr
) );
7521 err
= map_socket_value_errno( sockCtx
->sock
, n
== (ssize_t
) resp
->msgLen
, n
);
7524 resp
->next
= freeList
;
7526 nowTicks
= UpTicks();
7529 if( ( resp
= me
->responseList
) != NULL
)
7531 uint64_t remainingNs
;
7533 remainingNs
= UpTicksToNanoseconds( resp
->targetTicks
- nowTicks
);
7534 if( remainingNs
> INT64_MAX
) remainingNs
= INT64_MAX
;
7536 err
= DispatchTimerCreate( dispatch_time( DISPATCH_TIME_NOW
, (int64_t) remainingNs
), DISPATCH_TIME_FOREVER
, 0,
7537 me
->queue
, _DNSServerUDPDelayedSend
, NULL
, sockCtx
, &me
->responseTimer
);
7538 require_noerr( err
, exit
);
7539 dispatch_resume( me
->responseTimer
);
7543 while( ( resp
= freeList
) != NULL
)
7545 freeList
= resp
->next
;
7546 DNSScheduledResponseFree( resp
);
7550 //===========================================================================================================================
7551 // _DNSServerAnswerQuery
7552 //===========================================================================================================================
7554 #define kLabelPrefix_Alias "alias"
7555 #define kLabelPrefix_AliasTTL "alias-ttl"
7556 #define kLabelPrefix_Count "count"
7557 #define kLabelPrefix_TTL "ttl"
7558 #define kLabel_IPv4 "ipv4"
7559 #define kLabel_IPv6 "ipv6"
7561 #define kMaxAliasTTLCount ( ( kDomainLabelLengthMax - sizeof_string( kLabelPrefix_AliasTTL ) ) / 2 )
7564 _DNSServerInitializeResponseMessage(
7567 unsigned int inFlags
,
7568 const uint8_t * inQName
,
7569 unsigned int inQType
,
7570 unsigned int inQClass
);
7572 _DNSServerAnswerQueryDynamically(
7573 const uint8_t * inQName
,
7574 unsigned int inQType
,
7575 unsigned int inQClass
,
7577 DataBuffer
* inDB
);
7580 _DNSServerAnswerQuery(
7581 const uint8_t * const inQueryPtr
,
7582 const size_t inQueryLen
,
7584 uint8_t ** outResponsePtr
,
7585 size_t * outResponseLen
)
7589 const uint8_t * ptr
;
7590 const uint8_t * const queryEnd
= &inQueryPtr
[ inQueryLen
];
7591 const DNSHeader
* qhdr
;
7592 unsigned int msgID
, qflags
, qtype
, qclass
, rflags
;
7593 uint8_t qname
[ kDomainNameLengthMax
];
7595 DataBuffer_Init( &dataBuf
, NULL
, 0, kDNSMaxTCPMessageSize
);
7597 require_action_quiet( inQueryLen
>= kDNSHeaderLength
, exit
, err
= kUnderrunErr
);
7599 qhdr
= (const DNSHeader
*) inQueryPtr
;
7600 msgID
= DNSHeaderGetID( qhdr
);
7601 qflags
= DNSHeaderGetFlags( qhdr
);
7603 // Minimal checking of the query message's header.
7605 if( ( qflags
& kDNSHeaderFlag_Response
) || // The message must be a query, not a response.
7606 ( DNSFlagsGetOpCode( qflags
) != kDNSOpCode_Query
) || // OPCODE must be QUERY (standard query).
7607 ( DNSHeaderGetQuestionCount( qhdr
) != 1 ) ) // There should be a single question.
7615 ptr
= (const uint8_t *) &qhdr
[ 1 ];
7616 err
= DNSMessageExtractDomainName( inQueryPtr
, inQueryLen
, ptr
, qname
, &ptr
);
7617 require_noerr( err
, exit
);
7619 // Get QTYPE and QCLASS.
7621 require_action_quiet( ( queryEnd
- ptr
) >= 4, exit
, err
= kUnderrunErr
);
7622 qtype
= DNSQuestionFixedFieldsGetType( (const DNSQuestionFixedFields
*) ptr
);
7623 qclass
= DNSQuestionFixedFieldsGetClass( (const DNSQuestionFixedFields
*) ptr
);
7626 // Create a tentative response message.
7628 rflags
= kDNSHeaderFlag_Response
;
7629 if( qflags
& kDNSHeaderFlag_RecursionDesired
) rflags
|= kDNSHeaderFlag_RecursionDesired
;
7630 DNSFlagsSetOpCode( rflags
, kDNSOpCode_Query
);
7632 err
= _DNSServerInitializeResponseMessage( &dataBuf
, msgID
, rflags
, qname
, qtype
, qclass
);
7633 require_noerr( err
, exit
);
7635 err
= _DNSServerAnswerQueryDynamically( qname
, qtype
, qclass
, inForTCP
, &dataBuf
);
7638 DNSFlagsSetRCode( rflags
, kDNSRCode_ServerFailure
);
7639 err
= _DNSServerInitializeResponseMessage( &dataBuf
, msgID
, rflags
, qname
, qtype
, qclass
);
7640 require_noerr( err
, exit
);
7643 err
= DataBuffer_Detach( &dataBuf
, outResponsePtr
, outResponseLen
);
7644 require_noerr( err
, exit
);
7647 DataBuffer_Free( &dataBuf
);
7652 _DNSServerInitializeResponseMessage(
7655 unsigned int inFlags
,
7656 const uint8_t * inQName
,
7657 unsigned int inQType
,
7658 unsigned int inQClass
)
7662 DNSQuestionFixedFields fields
;
7664 DataBuffer_Reset( inDB
);
7666 memset( &header
, 0, sizeof( header
) );
7667 DNSHeaderSetID( &header
, inID
);
7668 DNSHeaderSetFlags( &header
, inFlags
);
7669 DNSHeaderSetQuestionCount( &header
, 1 );
7671 err
= DataBuffer_Append( inDB
, &header
, sizeof( header
) );
7672 require_noerr( err
, exit
);
7674 err
= DataBuffer_Append( inDB
, inQName
, DomainNameLength( inQName
) );
7675 require_noerr( err
, exit
);
7677 DNSQuestionFixedFieldsInit( &fields
, inQType
, inQClass
);
7678 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
7679 require_noerr( err
, exit
);
7686 _DNSServerAnswerQueryDynamically(
7687 const uint8_t * const inQName
,
7688 const unsigned int inQType
,
7689 const unsigned int inQClass
,
7690 const Boolean inForTCP
,
7691 DataBuffer
* const inDB
)
7693 OSStatus err
; // General-purpose error variable.
7694 const uint8_t * labelPtr
; // QNAME label pointer.
7695 size_t labelLen
; // QNAME label length.
7696 DNSHeader
* hdr
; // Response header pointer.
7697 unsigned int flags
; // Response header flags.
7698 unsigned int rcode
; // Response header response code.
7699 unsigned int answerCount
= 0; // Number of answers contained in response.
7700 int32_t aliasCount
= -1; // Arg from "alias" label. Valid values are in [2 .. 2^31 - 1].
7701 int count
= -1; // First arg from "count" label. Valid values are in [1 .. 255].
7702 int randCount
= -1; // Second arg from "count" label. Valid values are in [1 .. 255].
7703 int32_t ttl
= -1; // Arg from "ttl" label. Valid values are in [0 .. 2^31 - 1].
7704 uint32_t aliasTTLs
[ kMaxAliasTTLCount
]; // Args from "alias-ttl" label. Valid values are in [0 .. 2^31 - 1].
7705 int i
; // General-purpose array index.
7706 Boolean useAliasTTLs
= false; // True if QNAME contained a valid "alias-ttl" label.
7707 Boolean nameExists
= false; // True if name specified by QNAME exists.
7708 Boolean nameHasA
= false; // True if name specified by QNAME has an A record.
7709 Boolean nameHasAAAA
= false; // True if name specified by QNAME has a AAAA record.
7710 Boolean notImplemented
= false; // True if the kind of the query is not supported.
7711 Boolean truncated
= false; // True if the response message is truncated.
7712 uint8_t namePtr
[ 2 ]; // Name compression pointer.
7714 if( inQClass
!= kDNSServiceClass_IN
)
7716 notImplemented
= true;
7720 for( labelPtr
= inQName
; ( labelLen
= *labelPtr
) != 0; labelPtr
+= ( 1 + labelLen
) )
7722 const char * const labelStr
= (const char *) &labelPtr
[ 1 ];
7727 require_action( labelLen
<= kDomainNameLengthMax
, exit
, err
= kUnexpectedErr
);
7729 // Check if the first label is a valid alias TTL sequence label.
7731 if( ( labelPtr
== inQName
) && ( strnicmp_prefix( labelStr
, labelLen
, kLabelPrefix_AliasTTL
) == 0 ) )
7733 const char * src
= &labelStr
[ sizeof_string( kLabelPrefix_AliasTTL
) ];
7734 const char * const end
= &labelStr
[ labelLen
];
7739 n
= SNScanF( src
, (size_t)( end
- src
), "-%10lld%#n", &arg
, &next
);
7741 if( ( arg
< 0 ) || ( arg
> INT32_MAX
) ) break; // TTL must be >= 0 and <= (2^31 - 1).
7742 aliasTTLs
[ argCount
++ ] = (uint32_t) arg
;
7745 if( ( argCount
> 0 ) && ( src
== end
) )
7747 aliasCount
= argCount
;
7748 useAliasTTLs
= true;
7753 // Check if the first label is a valid alias label.
7755 if( ( labelPtr
== inQName
) && ( strnicmp_prefix( labelStr
, labelLen
, kLabelPrefix_Alias
) == 0 ) )
7757 const char * src
= &labelStr
[ sizeof_string( kLabelPrefix_Alias
) ];
7758 const char * const end
= &labelStr
[ labelLen
];
7766 n
= SNScanF( src
, (size_t)( end
- src
), "-%10lld%#n", &arg
, &next
);
7767 if( ( n
== 1 ) && ( next
== end
) )
7769 if( ( arg
< 2 ) || ( arg
> INT32_MAX
) ) break; // Alias count must be >= 2 and <= (2^31 - 1).
7770 aliasCount
= (int32_t) arg
;
7775 // Check if the label is a valid count label.
7777 if( strnicmp_prefix( labelStr
, labelLen
, kLabelPrefix_Count
) == 0 )
7779 const char * src
= &labelStr
[ sizeof_string( kLabelPrefix_Count
) ];
7780 const char * const end
= &labelStr
[ labelLen
];
7782 n
= SNScanF( src
, (size_t)( end
- src
), "-%3lld%#n", &arg
, &next
);
7785 if( count
> 0 ) break; // Count cannot be specified more than once.
7786 if( ( arg
< 1 ) || ( arg
> 255 ) ) break; // Count must be >= 1 and <= 255.
7792 n
= SNScanF( src
, (size_t)( end
- src
), "-%3lld%#n", &arg
, &next
);
7793 if( ( n
!= 1 ) || ( next
!= end
) ) break;
7794 if( ( arg
< count
) || ( arg
> 255 ) ) break; // Rand count must be >= count and <= 255.
7795 randCount
= (int) arg
;
7801 // Check if the label is a valid tag label.
7803 if( strnicmp_prefix( labelStr
, labelLen
, "tag-" ) == 0 ) continue;
7805 // Check if the label is a valid TTL label.
7807 if( strnicmp_prefix( labelStr
, labelLen
, kLabelPrefix_TTL
) == 0 )
7809 const char * src
= &labelStr
[ sizeof_string( kLabelPrefix_TTL
) ];
7810 const char * const end
= &labelStr
[ labelLen
];
7812 n
= SNScanF( src
, (size_t)( end
- src
), "-%10lld%#n", &arg
, &next
);
7813 if( ( n
== 1 ) && ( next
== end
) )
7815 if( ttl
>= 0 ) break; // TTL cannot be specified more than once.
7816 if( ( arg
< 0 ) || ( arg
> INT32_MAX
) ) break; // TTL must be >= 0 and <= (2^31 - 1).
7817 ttl
= (int32_t) arg
;
7822 // Check if the label is a valid IPv4 or IPv6 label.
7824 if( MemIEqual( labelStr
, labelLen
, kLabel_IPv4
, sizeof_string( kLabel_IPv4
) ) )
7826 if( nameHasA
|| nameHasAAAA
) break; // Valid names have at most one IPv4 or IPv6 label.
7830 if( MemIEqual( labelStr
, labelLen
, kLabel_IPv6
, sizeof_string( kLabel_IPv6
) ) )
7832 if( nameHasA
|| nameHasAAAA
) break; // Valid names have at most one IPv4 or IPv6 label.
7837 // If the remaining labels are equal to "d.test.", the name exists.
7839 if( DomainNameEqual( labelPtr
, (const uint8_t *) "\x01" "d" "\x04" "test" ) ) nameExists
= true;
7842 require_quiet( nameExists
, done
);
7844 // Set default values for count and TTL, if those labels were present.
7846 if( count
<= 0 ) count
= 1;
7847 check( ( gDNSServer_DefaultTTL
>= 0 ) && ( gDNSServer_DefaultTTL
<= INT32_MAX
) );
7848 if( ttl
< 0 ) ttl
= gDNSServer_DefaultTTL
;
7850 // Names that don't specify v4 or v6 have both A and AAAA records.
7852 if( !nameHasA
&& !nameHasAAAA
)
7858 check( ( count
>= 1 ) && ( count
<= 255 ) );
7859 check( ( randCount
<= 0 ) || ( ( randCount
>= count
) && ( randCount
<= 255 ) ) );
7861 if( aliasCount
> 0 )
7864 uint8_t rdataLabel
[ 1 + kDomainLabelLengthMax
+ 1 ];
7866 // If aliasCount is non-zero, then the first label of QNAME is either "alias" or "alias-<N>". superPtr is a name
7867 // compression pointer to the second label of QNAME, i.e., the immediate superdomain name of QNAME. It's used for
7868 // the RDATA of CNAME records whose canonical name ends with the superdomain name. It may also be used to construct
7869 // CNAME record names, when the offset to the previous CNAME's RDATA doesn't fit in a compression pointer.
7871 const uint8_t superPtr
[ 2 ] = { 0xC0, (uint8_t)( kDNSHeaderLength
+ 1 + inQName
[ 0 ] ) };
7873 // The name of the first CNAME record is equal to QNAME, so nameOffset is set to offset of QNAME.
7875 nameOffset
= kDNSHeaderLength
;
7877 for( i
= aliasCount
; i
>= 1; --i
)
7883 uint8_t nameLabel
[ 1 + kDomainLabelLengthMax
+ 1 ];
7884 DNSRecordFixedFields fields
;
7886 if( nameOffset
<= kDNSCompressionOffsetMax
)
7888 namePtr
[ 0 ] = (uint8_t)( ( ( nameOffset
>> 8 ) & 0x3F ) | 0xC0 );
7889 namePtr
[ 1 ] = (uint8_t)( nameOffset
& 0xFF );
7891 nameLen
= sizeof( namePtr
);
7895 memcpy( nameLabel
, rdataLabel
, 1 + rdataLabel
[ 0 ] );
7896 nameLen
= 1 + nameLabel
[ 0 ] + sizeof( superPtr
);
7901 char * dst
= (char *) &rdataLabel
[ 1 ];
7902 char * const end
= (char *) &rdataLabel
[ countof( rdataLabel
) ];
7906 err
= SNPrintF_Add( &dst
, end
, kLabelPrefix_AliasTTL
);
7907 require_noerr( err
, exit
);
7909 for( j
= aliasCount
- ( i
- 1 ); j
< aliasCount
; ++j
)
7911 err
= SNPrintF_Add( &dst
, end
, "-%u", aliasTTLs
[ j
] );
7912 require_noerr( err
, exit
);
7917 err
= SNPrintF_Add( &dst
, end
, kLabelPrefix_Alias
"%?{end}-%u", i
== 2, i
- 1 );
7918 require_noerr( err
, exit
);
7920 rdataLabel
[ 0 ] = (uint8_t)( dst
- (char *) &rdataLabel
[ 1 ] );
7921 rdataLen
= 1 + rdataLabel
[ 0 ] + sizeof( superPtr
);
7925 rdataLen
= sizeof( superPtr
);
7930 size_t recordLen
= nameLen
+ sizeof( fields
) + rdataLen
;
7932 if( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
)
7940 // Set CNAME record's NAME.
7942 if( nameOffset
<= kDNSCompressionOffsetMax
)
7944 err
= DataBuffer_Append( inDB
, namePtr
, sizeof( namePtr
) );
7945 require_noerr( err
, exit
);
7949 err
= DataBuffer_Append( inDB
, nameLabel
, 1 + nameLabel
[ 0 ] );
7950 require_noerr( err
, exit
);
7952 err
= DataBuffer_Append( inDB
, superPtr
, sizeof( superPtr
) );
7953 require_noerr( err
, exit
);
7956 // Set CNAME record's TYPE, CLASS, TTL, and RDLENGTH.
7958 aliasTTL
= useAliasTTLs
? aliasTTLs
[ aliasCount
- i
] : ( (uint32_t) gDNSServer_DefaultTTL
);
7959 DNSRecordFixedFieldsInit( &fields
, kDNSServiceType_CNAME
, kDNSServiceClass_IN
, aliasTTL
, rdataLen
);
7960 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
7961 require_noerr( err
, exit
);
7963 // Save offset of CNAME record's RDATA, which may be used for the name of the next CNAME record.
7965 nameOffset
= DataBuffer_GetLen( inDB
);
7967 // Set CNAME record's RDATA.
7971 err
= DataBuffer_Append( inDB
, rdataLabel
, 1 + rdataLabel
[ 0 ] );
7972 require_noerr( err
, exit
);
7974 err
= DataBuffer_Append( inDB
, superPtr
, sizeof( superPtr
) );
7975 require_noerr( err
, exit
);
7978 namePtr
[ 0 ] = superPtr
[ 0 ];
7979 namePtr
[ 1 ] = superPtr
[ 1 ];
7983 // There are no aliases, so initialize the name compression pointer to point to QNAME.
7985 namePtr
[ 0 ] = 0xC0;
7986 namePtr
[ 1 ] = kDNSHeaderLength
;
7989 if( ( ( inQType
== kDNSServiceType_A
) && nameHasA
) ||
7990 ( ( inQType
== kDNSServiceType_AAAA
) && nameHasAAAA
) )
7992 uint8_t * lsb
; // Pointer to the least significant byte of record data.
7993 size_t recordLen
; // Length of the entire record.
7994 size_t rdataLen
; // Length of record's RDATA.
7995 uint8_t rdata
[ 16 ]; // A buffer that's big enough for either A or AAAA RDATA.
7996 uint8_t randItegers
[ 255 ]; // Array for random integers in [1 .. 255].
7997 DNSRecordFixedFields fields
;
7999 if( inQType
== kDNSServiceType_A
)
8002 WriteBig32( rdata
, kTestDNSServerBaseAddrV4
);
8008 memcpy( rdata
, kTestDNSServerBaseAddrV6
, 16 );
8014 // Populate the array with all integers between 1 and <randCount>, inclusive.
8016 for( i
= 0; i
< randCount
; ++i
) randItegers
[ i
] = (uint8_t)( i
+ 1 );
8018 // Create a contiguous subarray starting at index 0 that contains <count> randomly chosen integers between
8019 // 1 and <randCount>, inclusive.
8020 // Loop invariant 1: Array elements with indexes in [0 .. i - 1] have been randomly chosen.
8021 // Loop invariant 2: Array elements with indexes in [i .. randCount - 1] are candidates for being chosen.
8023 for( i
= 0; i
< count
; ++i
)
8028 j
= (int) RandomRange( i
, randCount
- 1 );
8031 tmp
= randItegers
[ i
];
8032 randItegers
[ i
] = randItegers
[ j
];
8033 randItegers
[ j
] = tmp
;
8038 recordLen
= sizeof( namePtr
) + sizeof( fields
) + rdataLen
;
8039 for( i
= 0; i
< count
; ++i
)
8041 if( !inForTCP
&& ( ( DataBuffer_GetLen( inDB
) + recordLen
) > kDNSMaxUDPMessageSize
) )
8050 err
= DataBuffer_Append( inDB
, namePtr
, sizeof( namePtr
) );
8051 require_noerr( err
, exit
);
8053 // Set record TYPE, CLASS, TTL, and RDLENGTH.
8055 DNSRecordFixedFieldsInit( &fields
, inQType
, kDNSServiceClass_IN
, ttl
, rdataLen
);
8056 err
= DataBuffer_Append( inDB
, &fields
, sizeof( fields
) );
8057 require_noerr( err
, exit
);
8059 // Set record RDATA.
8061 *lsb
= ( randCount
> 0 ) ? randItegers
[ i
] : ( *lsb
+ 1 );
8063 err
= DataBuffer_Append( inDB
, rdata
, rdataLen
);
8064 require_noerr( err
, exit
);
8069 hdr
= (DNSHeader
*) DataBuffer_GetPtr( inDB
);
8070 flags
= DNSHeaderGetFlags( hdr
);
8071 if( truncated
) flags
|= kDNSHeaderFlag_Truncation
;
8072 if( notImplemented
)
8074 rcode
= kDNSRCode_NotImplemented
;
8078 flags
|= kDNSHeaderFlag_AuthAnswer
;
8079 rcode
= nameExists
? kDNSRCode_NoError
: kDNSRCode_NXDomain
;
8081 DNSFlagsSetRCode( flags
, rcode
);
8082 DNSHeaderSetFlags( hdr
, flags
);
8083 DNSHeaderSetAnswerCount( hdr
, answerCount
);
8090 //===========================================================================================================================
8091 // _DNSServerTCPReadHandler
8092 //===========================================================================================================================
8096 sockaddr_ip clientAddr
; // Client's address.
8097 dispatch_source_t readSource
; // Dispatch read source for client socket.
8098 dispatch_source_t writeSource
; // Dispatch write source for client socket.
8099 size_t offset
; // Offset into receive buffer.
8100 void * msgPtr
; // Pointer to dynamically allocated message buffer.
8101 size_t msgLen
; // Length of message buffer.
8102 Boolean readSuspended
; // True if the read source is currently suspended.
8103 Boolean writeSuspended
; // True if the write source is currently suspended.
8104 Boolean receivedLength
; // True if receiving DNS message as opposed to the message length.
8105 uint8_t lenBuf
[ 2 ]; // Buffer for two-octet message length field.
8106 iovec_t iov
[ 2 ]; // IO vector for writing response message.
8107 iovec_t
* iovPtr
; // Vector pointer for SocketWriteData().
8108 int iovCount
; // Vector count for SocketWriteData().
8110 } TCPConnectionContext
;
8112 static void TCPConnectionStop( TCPConnectionContext
*inContext
);
8113 static void TCPConnectionContextFree( TCPConnectionContext
*inContext
);
8114 static void TCPConnectionReadHandler( void *inContext
);
8115 static void TCPConnectionWriteHandler( void *inContext
);
8117 #define TCPConnectionForget( X ) ForgetCustomEx( X, TCPConnectionStop, TCPConnectionContextFree )
8119 static void _DNSServerTCPReadHandler( void *inContext
)
8122 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
8123 TCPConnectionContext
* connection
;
8124 socklen_t clientAddrLen
;
8125 SocketRef newSock
= kInvalidSocketRef
;
8126 SocketContext
* newSockCtx
= NULL
;
8128 connection
= (TCPConnectionContext
*) calloc( 1, sizeof( *connection
) );
8129 require_action( connection
, exit
, err
= kNoMemoryErr
);
8131 clientAddrLen
= (socklen_t
) sizeof( connection
->clientAddr
);
8132 newSock
= accept( sockCtx
->sock
, &connection
->clientAddr
.sa
, &clientAddrLen
);
8133 err
= map_socket_creation_errno( newSock
);
8134 require_noerr( err
, exit
);
8136 err
= SocketContextCreate( newSock
, connection
, &newSockCtx
);
8137 require_noerr( err
, exit
);
8138 newSock
= kInvalidSocketRef
;
8140 err
= DispatchReadSourceCreate( newSockCtx
->sock
, NULL
, TCPConnectionReadHandler
, SocketContextCancelHandler
,
8141 newSockCtx
, &connection
->readSource
);
8142 require_noerr( err
, exit
);
8143 SocketContextRetain( newSockCtx
);
8144 dispatch_resume( connection
->readSource
);
8146 err
= DispatchWriteSourceCreate( newSockCtx
->sock
, NULL
, TCPConnectionWriteHandler
, SocketContextCancelHandler
,
8147 newSockCtx
, &connection
->writeSource
);
8148 require_noerr( err
, exit
);
8149 SocketContextRetain( newSockCtx
);
8150 connection
->writeSuspended
= true;
8154 ForgetSocket( &newSock
);
8155 SocketContextRelease( newSockCtx
);
8156 TCPConnectionForget( &connection
);
8159 //===========================================================================================================================
8160 // TCPConnectionStop
8161 //===========================================================================================================================
8163 static void TCPConnectionStop( TCPConnectionContext
*inContext
)
8165 dispatch_source_forget_ex( &inContext
->readSource
, &inContext
->readSuspended
);
8166 dispatch_source_forget_ex( &inContext
->writeSource
, &inContext
->writeSuspended
);
8169 //===========================================================================================================================
8170 // TCPConnectionContextFree
8171 //===========================================================================================================================
8173 static void TCPConnectionContextFree( TCPConnectionContext
*inContext
)
8175 check( !inContext
->readSource
);
8176 check( !inContext
->writeSource
);
8177 ForgetMem( &inContext
->msgPtr
);
8181 //===========================================================================================================================
8182 // TCPConnectionReadHandler
8183 //===========================================================================================================================
8185 static void TCPConnectionReadHandler( void *inContext
)
8188 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
8189 TCPConnectionContext
* connection
= (TCPConnectionContext
*) sockCtx
->userContext
;
8191 uint8_t * responsePtr
= NULL
; // malloc'd
8194 // Receive message length.
8196 if( !connection
->receivedLength
)
8198 err
= SocketReadData( sockCtx
->sock
, connection
->lenBuf
, sizeof( connection
->lenBuf
), &connection
->offset
);
8199 if( err
== EWOULDBLOCK
) goto exit
;
8200 require_noerr( err
, exit
);
8202 connection
->offset
= 0;
8203 connection
->msgLen
= ReadBig16( connection
->lenBuf
);
8204 connection
->msgPtr
= malloc( connection
->msgLen
);
8205 require_action( connection
->msgPtr
, exit
, err
= kNoMemoryErr
);
8206 connection
->receivedLength
= true;
8211 err
= SocketReadData( sockCtx
->sock
, connection
->msgPtr
, connection
->msgLen
, &connection
->offset
);
8212 if( err
== EWOULDBLOCK
) goto exit
;
8213 require_noerr( err
, exit
);
8215 gettimeofday( &now
, NULL
);
8216 dispatch_suspend( connection
->readSource
);
8217 connection
->readSuspended
= true;
8219 ds_ulog( kLogLevelInfo
, "TCP server received %zu bytes from %##a at %{du:time}.\n",
8220 connection
->msgLen
, &connection
->clientAddr
, &now
);
8222 if( connection
->msgLen
< kDNSHeaderLength
)
8224 ds_ulog( kLogLevelInfo
, "TCP DNS message is too small (%zu < %d).\n", connection
->msgLen
, kDNSHeaderLength
);
8228 ds_ulog( kLogLevelInfo
, "TCP received message:\n\n%1{du:dnsmsg}", connection
->msgPtr
, connection
->msgLen
);
8232 err
= _DNSServerAnswerQueryForTCP( connection
->msgPtr
, connection
->msgLen
, &responsePtr
, &responseLen
);
8233 require_noerr_quiet( err
, exit
);
8237 ds_ulog( kLogLevelInfo
, "TCP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen
, responsePtr
, responseLen
);
8239 free( connection
->msgPtr
);
8240 connection
->msgPtr
= responsePtr
;
8241 connection
->msgLen
= responseLen
;
8244 check( connection
->msgLen
<= UINT16_MAX
);
8245 WriteBig16( connection
->lenBuf
, connection
->msgLen
);
8246 connection
->iov
[ 0 ].iov_base
= connection
->lenBuf
;
8247 connection
->iov
[ 0 ].iov_len
= sizeof( connection
->lenBuf
);
8248 connection
->iov
[ 1 ].iov_base
= connection
->msgPtr
;
8249 connection
->iov
[ 1 ].iov_len
= connection
->msgLen
;
8251 connection
->iovPtr
= connection
->iov
;
8252 connection
->iovCount
= 2;
8254 check( connection
->writeSuspended
);
8255 dispatch_resume( connection
->writeSource
);
8256 connection
->writeSuspended
= false;
8259 FreeNullSafe( responsePtr
);
8260 if( err
&& ( err
!= EWOULDBLOCK
) ) TCPConnectionForget( &connection
);
8263 //===========================================================================================================================
8264 // TCPConnectionWriteHandler
8265 //===========================================================================================================================
8267 static void TCPConnectionWriteHandler( void *inContext
)
8270 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
8271 TCPConnectionContext
* connection
= (TCPConnectionContext
*) sockCtx
->userContext
;
8273 err
= SocketWriteData( sockCtx
->sock
, &connection
->iovPtr
, &connection
->iovCount
);
8274 if( err
== EWOULDBLOCK
) goto exit
;
8277 TCPConnectionForget( &connection
);
8283 //===========================================================================================================================
8285 //===========================================================================================================================
8287 #define kGAIPerfStandardTTL ( 1 * kSecondsPerHour )
8289 typedef struct GAITesterPrivate
* GAITesterRef
;
8290 typedef struct GAITestCase GAITestCase
;
8292 typedef uint32_t GAITesterEventType
;
8293 #define kGAITesterEvent_Started 1
8294 #define kGAITesterEvent_Stopped 2
8298 const char * name
; // Domain name that was resolved.
8299 int64_t connectionTimeUs
; // Time in microseconds that it took to create a DNS-SD connection.
8300 int64_t firstTimeUs
; // Time in microseconds that it took to get the first address result.
8301 int64_t timeUs
; // Time in microseconds that it took to get all expected address results.
8303 } GAITestItemResult
;
8305 typedef void ( *GAITesterEventHandler_f
)( GAITesterEventType inType
, void *inContext
);
8307 ( *GAITesterResultsHandler_f
)(
8308 const char * inCaseTitle
,
8309 MicroTime64 inCaseStartTime
,
8310 MicroTime64 inCaseEndTime
,
8311 const GAITestItemResult
* inResults
,
8312 size_t inResultCount
,
8316 typedef unsigned int GAITestAddrType
;
8317 #define kGAITestAddrType_None 0
8318 #define kGAITestAddrType_IPv4 ( 1U << 0 )
8319 #define kGAITestAddrType_IPv6 ( 1U << 1 )
8320 #define kGAITestAddrType_Both ( kGAITestAddrType_IPv4 | kGAITestAddrType_IPv6 )
8322 #define GAITestAddrTypeIsValid( X ) \
8323 ( ( (X) & kGAITestAddrType_Both ) && ( ( (X) & ~kGAITestAddrType_Both ) == 0 ) )
8327 kGAIPerfOutputFormat_JSON
= 1,
8328 kGAIPerfOutputFormat_XML
= 2,
8329 kGAIPerfOutputFormat_Binary
= 3
8331 } GAIPerfOutputFormatType
;
8335 GAITesterRef tester
; // GAI tester object.
8336 CFMutableArrayRef caseResults
; // Array of test case results.
8337 char * outputFilePath
; // File to write test results to. If NULL, then write to stdout.
8338 GAIPerfOutputFormatType outputFormat
; // Format of test results output.
8339 unsigned int callDelayMs
; // Amount of time to wait before calling DNSServiceGetAddrInfo().
8340 unsigned int serverDelayMs
; // Amount of additional time to have server delay its responses.
8341 unsigned int defaultIterCount
; // Default test case iteration count.
8342 dispatch_source_t sigIntSource
; // Dispatch source for SIGINT.
8343 dispatch_source_t sigTermSource
; // Dispatch source for SIGTERM.
8344 Boolean gotSignal
; // True if SIGINT or SIGTERM was caught.
8345 Boolean testerStarted
; // True if the GAI tester was started.
8346 Boolean appendNewLine
; // True if a newline character should be appended to JSON output.
8350 static void GAIPerfContextFree( GAIPerfContext
*inContext
);
8351 static OSStatus
GAIPerfAddAdvancedTestCases( GAIPerfContext
*inContext
);
8352 static OSStatus
GAIPerfAddBasicTestCases( GAIPerfContext
*inContext
);
8353 static void GAIPerfEventHandler( GAITesterEventType inType
, void *inContext
);
8355 GAIPerfResultsHandler(
8356 const char * inCaseTitle
,
8357 MicroTime64 inCaseStartTime
,
8358 MicroTime64 inCaseEndTime
,
8359 const GAITestItemResult
* inResults
,
8360 size_t inResultCount
,
8363 static void GAIPerfSignalHandler( void *inContext
);
8365 CFTypeID
GAITesterGetTypeID( void );
8368 dispatch_queue_t inQueue
,
8370 int inServerDelayMs
,
8371 int inServerDefaultTTL
,
8372 GAITesterRef
* outTester
);
8373 static void GAITesterStart( GAITesterRef inTester
);
8374 static void GAITesterStop( GAITesterRef inTester
);
8375 static void GAITesterAddCase( GAITesterRef inTester
, GAITestCase
*inCase
);
8377 GAITesterSetEventHandler(
8378 GAITesterRef inTester
,
8379 GAITesterEventHandler_f inEventHandler
,
8380 void * inEventContext
);
8382 GAITesterSetResultsHandler(
8383 GAITesterRef inTester
,
8384 GAITesterResultsHandler_f inResultsHandler
,
8385 void * inResultsContext
);
8387 static OSStatus
GAITestCaseCreate( const char *inTitle
, unsigned int inTimeLimitMs
, GAITestCase
**outSet
);
8388 static void GAITestCaseFree( GAITestCase
*inCase
);
8391 GAITestCase
* inCase
,
8392 unsigned int inAliasCount
,
8393 unsigned int inAddressCount
,
8395 GAITestAddrType inHasAddrs
,
8396 GAITestAddrType inWantAddrs
,
8397 unsigned int inItemCount
);
8398 static OSStatus
GAITestCaseAddLocalHostItem( GAITestCase
*inCase
, GAITestAddrType inWantAddrs
, unsigned int inItemCount
);
8400 #define kGAIPerfTestSuite_Basic 1
8401 #define kGAIPerfTestSuite_Advanced 2
8403 static void GAIPerfCmd( void )
8406 GAIPerfContext
* context
;
8409 context
= (GAIPerfContext
*) calloc( 1, sizeof( *context
) );
8410 require_action( context
, exit
, err
= kNoMemoryErr
);
8412 context
->caseResults
= CFArrayCreateMutable( NULL
, 0, &kCFTypeArrayCallBacks
);
8413 require_action( context
->caseResults
, exit
, err
= kNoMemoryErr
);
8415 context
->outputFormat
= (GAIPerfOutputFormatType
) CLIArgToValue( "format", gGAIPerf_OutputFormat
, &err
,
8416 "json", kGAIPerfOutputFormat_JSON
,
8417 "xml", kGAIPerfOutputFormat_XML
,
8418 "binary", kGAIPerfOutputFormat_Binary
,
8420 require_noerr_quiet( err
, exit
);
8422 context
->callDelayMs
= ( gGAIPerf_CallDelayMs
>= 0 ) ? (unsigned int) gGAIPerf_CallDelayMs
: 0;
8423 context
->serverDelayMs
= ( gGAIPerf_ServerDelayMs
>= 0 ) ? (unsigned int) gGAIPerf_ServerDelayMs
: 0;
8424 context
->defaultIterCount
= ( gGAIPerf_DefaultIterCount
>= 0 ) ? (unsigned int) gGAIPerf_DefaultIterCount
: 0;
8425 context
->appendNewLine
= gGAIPerf_OutputAppendNewLine
? true : false;
8427 if( gGAIPerf_OutputFilePath
)
8429 context
->outputFilePath
= strdup( gGAIPerf_OutputFilePath
);
8430 require_action( context
->outputFilePath
, exit
, err
= kNoMemoryErr
);
8433 err
= GAITesterCreate( dispatch_get_main_queue(), (int) context
->callDelayMs
, (int) context
->serverDelayMs
,
8434 kGAIPerfStandardTTL
, &context
->tester
);
8435 require_noerr( err
, exit
);
8437 check( gGAIPerf_TestSuite
);
8438 suiteValue
= CLIArgToValue( "suite", gGAIPerf_TestSuite
, &err
,
8439 "basic", kGAIPerfTestSuite_Basic
,
8440 "advanced", kGAIPerfTestSuite_Advanced
,
8442 require_noerr_quiet( err
, exit
);
8444 switch( suiteValue
)
8446 case kGAIPerfTestSuite_Basic
:
8447 err
= GAIPerfAddBasicTestCases( context
);
8448 require_noerr( err
, exit
);
8451 case kGAIPerfTestSuite_Advanced
:
8452 err
= GAIPerfAddAdvancedTestCases( context
);
8453 require_noerr( err
, exit
);
8461 GAITesterSetEventHandler( context
->tester
, GAIPerfEventHandler
, context
);
8462 GAITesterSetResultsHandler( context
->tester
, GAIPerfResultsHandler
, context
);
8464 signal( SIGINT
, SIG_IGN
);
8465 err
= DispatchSignalSourceCreate( SIGINT
, GAIPerfSignalHandler
, context
, &context
->sigIntSource
);
8466 require_noerr( err
, exit
);
8467 dispatch_resume( context
->sigIntSource
);
8469 signal( SIGTERM
, SIG_IGN
);
8470 err
= DispatchSignalSourceCreate( SIGTERM
, GAIPerfSignalHandler
, context
, &context
->sigTermSource
);
8471 require_noerr( err
, exit
);
8472 dispatch_resume( context
->sigTermSource
);
8474 GAITesterStart( context
->tester
);
8478 if( context
) GAIPerfContextFree( context
);
8479 if( err
) exit( 1 );
8482 //===========================================================================================================================
8483 // GAIPerfContextFree
8484 //===========================================================================================================================
8486 static void GAIPerfContextFree( GAIPerfContext
*inContext
)
8488 ForgetCF( &inContext
->tester
);
8489 ForgetCF( &inContext
->caseResults
);
8490 ForgetMem( &inContext
->outputFilePath
);
8491 dispatch_source_forget( &inContext
->sigIntSource
);
8492 dispatch_source_forget( &inContext
->sigTermSource
);
8496 //===========================================================================================================================
8497 // GAIPerfAddAdvancedTestCases
8498 //===========================================================================================================================
8500 #define kTestCaseTitleBufferSize 128
8503 _GAIPerfWriteTestCaseTitle(
8504 char inBuffer
[ kTestCaseTitleBufferSize
],
8505 unsigned int inCNAMERecordCount
,
8506 unsigned int inARecordCount
,
8507 unsigned int inAAAARecordCount
,
8508 GAITestAddrType inRequested
,
8509 unsigned int inIterationCount
,
8510 Boolean inIterationsAreUnique
);
8512 _GAIPerfWriteLocalHostTestCaseTitle(
8513 char inBuffer
[ kTestCaseTitleBufferSize
],
8514 GAITestAddrType inRequested
,
8515 unsigned int inIterationCount
);
8517 _GAIPerfTimeLimitMs(
8518 unsigned int inCallDelayMs
,
8519 unsigned int inServerDelayMs
,
8520 unsigned int inIterationCount
);
8522 #define kGAIPerfAdvancedTestSuite_MaxAliasCount 4
8523 #define kGAIPerfAdvancedTestSuite_MaxAddrCount 8
8525 static OSStatus
GAIPerfAddAdvancedTestCases( GAIPerfContext
*inContext
)
8528 unsigned int aliasCount
, addressCount
, timeLimitMs
, i
;
8529 GAITestCase
* testCase
= NULL
;
8530 char title
[ kTestCaseTitleBufferSize
];
8533 while( aliasCount
<= kGAIPerfAdvancedTestSuite_MaxAliasCount
)
8535 for( addressCount
= 1; addressCount
<= kGAIPerfAdvancedTestSuite_MaxAddrCount
; addressCount
*= 2 )
8537 // Add a test case to resolve a domain name with
8539 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
8541 // to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which
8542 // requires server queries.
8544 _GAIPerfWriteTestCaseTitle( title
, aliasCount
, addressCount
, addressCount
, kGAITestAddrType_Both
,
8545 inContext
->defaultIterCount
, true );
8547 timeLimitMs
= _GAIPerfTimeLimitMs( inContext
->callDelayMs
, inContext
->serverDelayMs
,
8548 inContext
->defaultIterCount
);
8549 err
= GAITestCaseCreate( title
, timeLimitMs
, &testCase
);
8550 require_noerr( err
, exit
);
8552 for( i
= 0; i
< inContext
->defaultIterCount
; ++i
)
8554 err
= GAITestCaseAddItem( testCase
, aliasCount
, addressCount
, kGAIPerfStandardTTL
,
8555 kGAITestAddrType_Both
, kGAITestAddrType_Both
, 1 );
8556 require_noerr( err
, exit
);
8559 GAITesterAddCase( inContext
->tester
, testCase
);
8562 // Add a test case to resolve a domain name with
8564 // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
8566 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique domain name, which requires a server
8567 // query. The subsequent iterations resolve the same domain name as the preliminary iteration, which should
8568 // ideally require no server queries, i.e., the results should come from the cache.
8570 _GAIPerfWriteTestCaseTitle( title
, aliasCount
, addressCount
, addressCount
, kGAITestAddrType_Both
,
8571 inContext
->defaultIterCount
, false );
8573 timeLimitMs
= _GAIPerfTimeLimitMs( inContext
->callDelayMs
, inContext
->serverDelayMs
, 1 ) +
8574 _GAIPerfTimeLimitMs( inContext
->callDelayMs
, 0, inContext
->defaultIterCount
);
8575 err
= GAITestCaseCreate( title
, timeLimitMs
, &testCase
);
8576 require_noerr( err
, exit
);
8578 err
= GAITestCaseAddItem( testCase
, aliasCount
, addressCount
, kGAIPerfStandardTTL
,
8579 kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->defaultIterCount
+ 1 );
8580 require_noerr( err
, exit
);
8582 GAITesterAddCase( inContext
->tester
, testCase
);
8586 if( aliasCount
== 0 ) aliasCount
= 1;
8587 else aliasCount
*= 2;
8590 // Finally, add a test case to resolve localhost to its IPv4 and IPv6 addresses.
8592 _GAIPerfWriteLocalHostTestCaseTitle( title
, kGAITestAddrType_Both
, inContext
->defaultIterCount
);
8594 timeLimitMs
= _GAIPerfTimeLimitMs( inContext
->callDelayMs
, 0, inContext
->defaultIterCount
);
8595 err
= GAITestCaseCreate( title
, timeLimitMs
, &testCase
);
8596 require_noerr( err
, exit
);
8598 err
= GAITestCaseAddLocalHostItem( testCase
, kGAITestAddrType_Both
, inContext
->defaultIterCount
);
8599 require_noerr( err
, exit
);
8601 GAITesterAddCase( inContext
->tester
, testCase
);
8605 if( testCase
) GAITestCaseFree( testCase
);
8609 //===========================================================================================================================
8610 // _GAIPerfWriteTestCaseTitle
8611 //===========================================================================================================================
8613 #define GAITestAddrTypeToRequestKeyValue( X ) ( \
8614 ( (X) == kGAITestAddrType_Both ) ? "ipv4\\,ipv6" : \
8615 ( (X) == kGAITestAddrType_IPv4 ) ? "ipv4" : \
8616 ( (X) == kGAITestAddrType_IPv6 ) ? "ipv6" : \
8620 _GAIPerfWriteTestCaseTitle(
8621 char inBuffer
[ kTestCaseTitleBufferSize
],
8622 unsigned int inCNAMERecordCount
,
8623 unsigned int inARecordCount
,
8624 unsigned int inAAAARecordCount
,
8625 GAITestAddrType inRequested
,
8626 unsigned int inIterationCount
,
8627 Boolean inIterationsAreUnique
)
8629 SNPrintF( inBuffer
, kTestCaseTitleBufferSize
, "name=dynamic,cname=%u,a=%u,aaaa=%u,req=%s,iterations=%u%?s",
8630 inCNAMERecordCount
, inARecordCount
, inAAAARecordCount
, GAITestAddrTypeToRequestKeyValue( inRequested
),
8631 inIterationCount
, inIterationsAreUnique
, ",unique" );
8634 //===========================================================================================================================
8635 // _GAIPerfWriteLocalHostTestCaseTitle
8636 //===========================================================================================================================
8639 _GAIPerfWriteLocalHostTestCaseTitle(
8640 char inBuffer
[ kTestCaseTitleBufferSize
],
8641 GAITestAddrType inRequested
,
8642 unsigned int inIterationCount
)
8644 SNPrintF( inBuffer
, kTestCaseTitleBufferSize
, "name=localhost,req=%s,iterations=%u",
8645 GAITestAddrTypeToRequestKeyValue( inRequested
), inIterationCount
);
8648 //===========================================================================================================================
8649 // _GAIPerfTimeLimitMs
8650 //===========================================================================================================================
8653 _GAIPerfTimeLimitMs(
8654 unsigned int inCallDelayMs
,
8655 unsigned int inServerDelayMs
,
8656 unsigned int inIterationCount
)
8658 // Allow each iteration 20 ms to complete (in addition to the call and server delay times).
8660 return( ( inCallDelayMs
+ inServerDelayMs
+ 20 ) * inIterationCount
);
8663 //===========================================================================================================================
8664 // GAIPerfAddBasicTestCases
8665 //===========================================================================================================================
8667 #define kGAIPerfBasicTestSuite_AliasCount 2
8668 #define kGAIPerfBasicTestSuite_AddrCount 4
8670 static OSStatus
GAIPerfAddBasicTestCases( GAIPerfContext
*inContext
)
8673 GAITestCase
* testCase
= NULL
;
8674 char title
[ kTestCaseTitleBufferSize
];
8675 unsigned int timeLimitMs
, i
;
8678 // Resolve a domain name with
8680 // 2 CNAME records, 4 A records, and 4 AAAA records
8682 // to its IPv4 and IPv6 addresses. Each of the iterations resolves a unique domain name, which requires server
8685 _GAIPerfWriteTestCaseTitle( title
, kGAIPerfBasicTestSuite_AliasCount
,
8686 kGAIPerfBasicTestSuite_AddrCount
, kGAIPerfBasicTestSuite_AddrCount
, kGAITestAddrType_Both
,
8687 inContext
->defaultIterCount
, true );
8689 timeLimitMs
= _GAIPerfTimeLimitMs( inContext
->callDelayMs
, inContext
->serverDelayMs
, inContext
->defaultIterCount
);
8690 err
= GAITestCaseCreate( title
, timeLimitMs
, &testCase
);
8691 require_noerr( err
, exit
);
8693 for( i
= 0; i
< inContext
->defaultIterCount
; ++i
)
8695 err
= GAITestCaseAddItem( testCase
, kGAIPerfBasicTestSuite_AliasCount
, kGAIPerfBasicTestSuite_AddrCount
,
8696 kGAIPerfStandardTTL
, kGAITestAddrType_Both
, kGAITestAddrType_Both
, 1 );
8697 require_noerr( err
, exit
);
8700 GAITesterAddCase( inContext
->tester
, testCase
);
8704 // Resolve a domain name with
8706 // 2 CNAME records, 4 A records, and 4 AAAA records
8708 // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which
8709 // requires server queries. Each of the subsequent iterations resolves the same domain name as the preliminary
8710 // iteration, which should ideally require no additional server queries, i.e., the results should come from the cache.
8712 _GAIPerfWriteTestCaseTitle( title
, kGAIPerfBasicTestSuite_AliasCount
,
8713 kGAIPerfBasicTestSuite_AddrCount
, kGAIPerfBasicTestSuite_AddrCount
, kGAITestAddrType_Both
,
8714 inContext
->defaultIterCount
, false );
8716 timeLimitMs
= _GAIPerfTimeLimitMs( inContext
->callDelayMs
, inContext
->serverDelayMs
, 1 ) +
8717 _GAIPerfTimeLimitMs( inContext
->callDelayMs
, 0, inContext
->defaultIterCount
);
8718 err
= GAITestCaseCreate( title
, timeLimitMs
, &testCase
);
8719 require_noerr( err
, exit
);
8721 err
= GAITestCaseAddItem( testCase
, kGAIPerfBasicTestSuite_AliasCount
, kGAIPerfBasicTestSuite_AddrCount
,
8722 kGAIPerfStandardTTL
, kGAITestAddrType_Both
, kGAITestAddrType_Both
, inContext
->defaultIterCount
+ 1 );
8723 require_noerr( err
, exit
);
8725 GAITesterAddCase( inContext
->tester
, testCase
);
8729 // Each iteration resolves localhost to its IPv4 and IPv6 addresses.
8731 _GAIPerfWriteLocalHostTestCaseTitle( title
, kGAITestAddrType_Both
, inContext
->defaultIterCount
);
8733 timeLimitMs
= _GAIPerfTimeLimitMs( inContext
->callDelayMs
, 0, inContext
->defaultIterCount
);
8734 err
= GAITestCaseCreate( title
, timeLimitMs
, &testCase
);
8735 require_noerr( err
, exit
);
8737 err
= GAITestCaseAddLocalHostItem( testCase
, kGAITestAddrType_Both
, inContext
->defaultIterCount
);
8738 require_noerr( err
, exit
);
8740 GAITesterAddCase( inContext
->tester
, testCase
);
8744 if( testCase
) GAITestCaseFree( testCase
);
8748 //===========================================================================================================================
8749 // GAIPerfEventHandler
8750 //===========================================================================================================================
8752 static void _GAIPerfOutputResultsAndExit( GAIPerfContext
*inContext
) ATTRIBUTE_NORETURN
;
8754 static void GAIPerfEventHandler( GAITesterEventType inType
, void *inContext
)
8756 GAIPerfContext
* const context
= (GAIPerfContext
*) inContext
;
8758 if( inType
== kGAITesterEvent_Started
)
8760 context
->testerStarted
= true;
8762 else if( inType
== kGAITesterEvent_Stopped
)
8764 if( context
->gotSignal
) exit( 1 );
8765 _GAIPerfOutputResultsAndExit( context
);
8769 //===========================================================================================================================
8770 // _GAIPerfOutputResultsAndExit
8771 //===========================================================================================================================
8773 #define kGAIPerfResultsKey_TestCases CFSTR( "testCases" )
8774 #define kGAIPerfResultsKey_Info CFSTR( "info" )
8776 #define kGAIPerfInfoKey_CallDelay CFSTR( "callDelayMs" )
8777 #define kGAIPerfInfoKey_ServerDelay CFSTR( "serverDelayMs" )
8779 static void _GAIPerfOutputResultsAndExit( GAIPerfContext
*inContext
)
8782 CFPropertyListRef plist
= NULL
;
8783 CFDataRef results
= NULL
;
8786 err
= CFPropertyListCreateFormatted( kCFAllocatorDefault
, &plist
,
8795 kGAIPerfResultsKey_TestCases
, inContext
->caseResults
,
8796 kGAIPerfResultsKey_Info
,
8797 kGAIPerfInfoKey_CallDelay
, (int64_t) inContext
->callDelayMs
,
8798 kGAIPerfInfoKey_ServerDelay
, (int64_t) inContext
->serverDelayMs
);
8799 require_noerr( err
, exit
);
8801 // Convert results to a specific format.
8803 switch( inContext
->outputFormat
)
8805 case kGAIPerfOutputFormat_JSON
:
8806 results
= CFCreateJSONData( plist
, kJSONFlags_None
, NULL
);
8807 require_action( results
, exit
, err
= kUnknownErr
);
8810 case kGAIPerfOutputFormat_XML
:
8811 results
= CFPropertyListCreateData( NULL
, plist
, kCFPropertyListXMLFormat_v1_0
, 0, NULL
);
8812 require_action( results
, exit
, err
= kUnknownErr
);
8815 case kGAIPerfOutputFormat_Binary
:
8816 results
= CFPropertyListCreateData( NULL
, plist
, kCFPropertyListBinaryFormat_v1_0
, 0, NULL
);
8817 require_action( results
, exit
, err
= kUnknownErr
);
8825 // Write formatted results to file or stdout.
8827 if( inContext
->outputFilePath
)
8829 file
= fopen( inContext
->outputFilePath
, "wb" );
8830 err
= map_global_value_errno( file
, file
);
8831 require_noerr( err
, exit
);
8838 err
= WriteANSIFile( file
, CFDataGetBytePtr( results
), (size_t) CFDataGetLength( results
) );
8839 require_noerr( err
, exit
);
8841 // Write a trailing newline for JSON-formatted results if requested.
8843 if( ( inContext
->outputFormat
== kGAIPerfOutputFormat_JSON
) && inContext
->appendNewLine
)
8845 err
= WriteANSIFile( file
, "\n", 1 );
8846 require_noerr( err
, exit
);
8850 CFReleaseNullSafe( plist
);
8851 CFReleaseNullSafe( results
);
8852 if( file
&& ( file
!= stdout
) ) fclose( file
);
8853 GAIPerfContextFree( inContext
);
8854 exit( err
? 1 : 0 );
8857 //===========================================================================================================================
8858 // GAIPerfResultsHandler
8859 //===========================================================================================================================
8861 // Keys for test case dictionary
8863 #define kGAIPerfTestCaseKey_Title CFSTR( "title" )
8864 #define kGAIPerfTestCaseKey_StartTime CFSTR( "startTimeUs" )
8865 #define kGAIPerfTestCaseKey_EndTime CFSTR( "endTimeUs" )
8866 #define kGAIPerfTestCaseKey_Results CFSTR( "results" )
8867 #define kGAIPerfTestCaseKey_FirstStats CFSTR( "firstStats" )
8868 #define kGAIPerfTestCaseKey_ConnectionStats CFSTR( "connectionStats" )
8869 #define kGAIPerfTestCaseKey_Stats CFSTR( "stats" )
8870 #define kGAIPerfTestCaseKey_TimedOut CFSTR( "timedOut" )
8872 // Keys for test case results array entry dictionaries
8874 #define kGAIPerfTestCaseResultKey_Name CFSTR( "name" )
8875 #define kGAIPerfTestCaseResultKey_ConnectionTime CFSTR( "connectionTimeUs" )
8876 #define kGAIPerfTestCaseResultKey_FirstTime CFSTR( "firstTimeUs" )
8877 #define kGAIPerfTestCaseResultKey_Time CFSTR( "timeUs" )
8879 // Keys for test case stats dictionaries
8881 #define kGAIPerfTestCaseStatsKey_Count CFSTR( "count" )
8882 #define kGAIPerfTestCaseStatsKey_Min CFSTR( "min" )
8883 #define kGAIPerfTestCaseStatsKey_Max CFSTR( "max" )
8884 #define kGAIPerfTestCaseStatsKey_Mean CFSTR( "mean" )
8885 #define kGAIPerfTestCaseStatsKey_StdDev CFSTR( "sd" )
8896 #define GAIPerfStatsInit( X ) \
8897 do { (X)->min = DBL_MAX; (X)->max = DBL_MIN; (X)->mean = 0.0; (X)->stdDev = 0.0; } while( 0 )
8900 GAIPerfResultsHandler(
8901 const char * inCaseTitle
,
8902 MicroTime64 inCaseStartTime
,
8903 MicroTime64 inCaseEndTime
,
8904 const GAITestItemResult
* inResults
,
8905 size_t inResultCount
,
8910 GAIPerfContext
* const context
= (GAIPerfContext
*) inContext
;
8911 int namesAreDynamic
, namesAreUnique
;
8913 size_t count
, startIndex
;
8914 CFMutableArrayRef results
= NULL
;
8915 GAIPerfStats stats
, firstStats
, connStats
;
8916 double sum
, firstSum
, connSum
, value
, diff
;
8917 size_t keyValueLen
, i
;
8918 char keyValue
[ 16 ]; // Size must be at least strlen( "name=dynamic" ) + 1 bytes.
8920 // If this test case resolves the same "d.test." name in each iteration (title contains the "name=dynamic" key-value
8921 // pair, but not the "unique" key), then don't count the first iteration, whose purpose is to populate the cache with the
8922 // domain name's CNAME, A, and AAAA records.
8924 namesAreDynamic
= false;
8925 namesAreUnique
= false;
8927 while( ParseQuotedEscapedString( ptr
, NULL
, ",", keyValue
, sizeof( keyValue
), &keyValueLen
, NULL
, &ptr
) )
8929 if( strnicmpx( keyValue
, keyValueLen
, "name=dynamic" ) == 0 )
8931 namesAreDynamic
= true;
8933 else if( strnicmpx( keyValue
, keyValueLen
, "unique" ) == 0 )
8935 namesAreUnique
= true;
8937 if( namesAreDynamic
&& namesAreUnique
) break;
8940 if( namesAreDynamic
&& !namesAreUnique
&& ( inItemCount
> 0 ) )
8942 count
= ( inResultCount
> 0 ) ? ( inResultCount
- 1 ) : 0;
8947 count
= inResultCount
;
8951 results
= CFArrayCreateMutable( NULL
, (CFIndex
) count
, &kCFTypeArrayCallBacks
);
8952 require_action( results
, exit
, err
= kNoMemoryErr
);
8954 GAIPerfStatsInit( &stats
);
8955 GAIPerfStatsInit( &firstStats
);
8956 GAIPerfStatsInit( &connStats
);
8961 for( i
= startIndex
; i
< count
; ++i
)
8963 value
= (double) inResults
[ i
].timeUs
;
8964 if( value
< stats
.min
) stats
.min
= value
;
8965 if( value
> stats
.max
) stats
.max
= value
;
8968 value
= (double) inResults
[ i
].firstTimeUs
;
8969 if( value
< firstStats
.min
) firstStats
.min
= value
;
8970 if( value
> firstStats
.max
) firstStats
.max
= value
;
8973 value
= (double) inResults
[ i
].connectionTimeUs
;
8974 if( value
< connStats
.min
) connStats
.min
= value
;
8975 if( value
> connStats
.max
) connStats
.max
= value
;
8978 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, results
,
8985 kGAIPerfTestCaseResultKey_Name
, inResults
[ i
].name
,
8986 kGAIPerfTestCaseResultKey_ConnectionTime
, inResults
[ i
].connectionTimeUs
,
8987 kGAIPerfTestCaseResultKey_FirstTime
, inResults
[ i
].firstTimeUs
,
8988 kGAIPerfTestCaseResultKey_Time
, inResults
[ i
].timeUs
);
8989 require_noerr( err
, exit
);
8994 stats
.mean
= sum
/ count
;
8995 firstStats
.mean
= firstSum
/ count
;
8996 connStats
.mean
= connSum
/ count
;
9001 for( i
= startIndex
; i
< count
; ++i
)
9003 diff
= stats
.mean
- (double) inResults
[ i
].timeUs
;
9004 sum
+= ( diff
* diff
);
9006 diff
= firstStats
.mean
- (double) inResults
[ i
].firstTimeUs
;
9007 firstSum
+= ( diff
* diff
);
9009 diff
= connStats
.mean
- (double) inResults
[ i
].connectionTimeUs
;
9010 connSum
+= ( diff
* diff
);
9012 stats
.stdDev
= sqrt( sum
/ count
);
9013 firstStats
.stdDev
= sqrt( firstSum
/ count
);
9014 connStats
.stdDev
= sqrt( connSum
/ count
);
9017 err
= CFPropertyListAppendFormatted( kCFAllocatorDefault
, context
->caseResults
,
9049 kGAIPerfTestCaseKey_Title
, inCaseTitle
,
9050 kGAIPerfTestCaseKey_StartTime
, (int64_t) inCaseStartTime
,
9051 kGAIPerfTestCaseKey_EndTime
, (int64_t) inCaseEndTime
,
9052 kGAIPerfTestCaseKey_Results
, results
,
9053 kGAIPerfTestCaseKey_Stats
,
9054 kGAIPerfTestCaseStatsKey_Count
, (int64_t) count
,
9055 kGAIPerfTestCaseStatsKey_Min
, stats
.min
,
9056 kGAIPerfTestCaseStatsKey_Max
, stats
.max
,
9057 kGAIPerfTestCaseStatsKey_Mean
, stats
.mean
,
9058 kGAIPerfTestCaseStatsKey_StdDev
, stats
.stdDev
,
9059 kGAIPerfTestCaseKey_FirstStats
,
9060 kGAIPerfTestCaseStatsKey_Count
, (int64_t) count
,
9061 kGAIPerfTestCaseStatsKey_Min
, firstStats
.min
,
9062 kGAIPerfTestCaseStatsKey_Max
, firstStats
.max
,
9063 kGAIPerfTestCaseStatsKey_Mean
, firstStats
.mean
,
9064 kGAIPerfTestCaseStatsKey_StdDev
, firstStats
.stdDev
,
9065 kGAIPerfTestCaseKey_ConnectionStats
,
9066 kGAIPerfTestCaseStatsKey_Count
, (int64_t) count
,
9067 kGAIPerfTestCaseStatsKey_Min
, connStats
.min
,
9068 kGAIPerfTestCaseStatsKey_Max
, connStats
.max
,
9069 kGAIPerfTestCaseStatsKey_Mean
, connStats
.mean
,
9070 kGAIPerfTestCaseStatsKey_StdDev
, connStats
.stdDev
,
9071 kGAIPerfTestCaseKey_TimedOut
, ( inResultCount
< inItemCount
) ? true : false );
9072 require_noerr( err
, exit
);
9075 CFReleaseNullSafe( results
);
9078 //===========================================================================================================================
9079 // GAIPerfSignalHandler
9080 //===========================================================================================================================
9082 static void GAIPerfSignalHandler( void *inContext
)
9084 GAIPerfContext
* const context
= (GAIPerfContext
*) inContext
;
9086 context
->gotSignal
= true;
9087 if( context
->tester
&& context
->testerStarted
)
9089 GAITesterStop( context
->tester
);
9097 //===========================================================================================================================
9099 //===========================================================================================================================
9103 kGAITestConnType_UseMainConnection
= 1,
9104 kGAITestConnType_OwnSharedConnection
= 2
9108 typedef struct GAITestItem GAITestItem
;
9111 GAITestItem
* next
; // Next test item in list.
9112 char * name
; // Domain name to resolve.
9113 int64_t connectionTimeUs
; // Time in microseconds that it took to create a DNS-SD connection.
9114 int64_t firstTimeUs
; // Time in microseconds that it took to get the first address result.
9115 int64_t timeUs
; // Time in microseconds that it took to get all expected address results.
9116 unsigned int addressCount
; // Address count of the domain name, i.e., the Count label argument.
9117 Boolean hasV4
; // True if the domain name has one or more IPv4 addresses.
9118 Boolean hasV6
; // True if the domain name has one or more IPv6 addresses.
9119 Boolean wantV4
; // True if DNSServiceGetAddrInfo() should be called to get IPv4 addresses.
9120 Boolean wantV6
; // True if DNSServiceGetAddrInfo() should be called to get IPv6 addresses.
9125 GAITestCase
* next
; // Next test case in list.
9126 GAITestItem
* itemList
; // List of test items.
9127 char * title
; // Title of the test case.
9128 unsigned int timeLimitMs
; // Time limit in milliseconds for the test case's completion.
9131 struct GAITesterPrivate
9133 CFRuntimeBase base
; // CF object base.
9134 dispatch_queue_t queue
; // Serial work queue.
9135 DNSServiceRef mainRef
; // Reference to the main shared DNS-SD connection.
9136 DNSServiceRef opRef
; // Reference to the current DNSServiceGetAddrInfo operation.
9137 GAITestCase
* caseList
; // List of test cases.
9138 GAITestCase
* currentCase
; // Pointer to the current test case.
9139 GAITestItem
* currentItem
; // Pointer to the current test item.
9140 MicroTime64 caseStartTime
; // Start time of current test case in Unix time as microseconds.
9141 MicroTime64 caseEndTime
; // End time of current test case in Unix time as microseconds.
9142 Boolean started
; // True if the tester has been successfully started.
9143 Boolean stopped
; // True if the tester has been stopped.
9144 int callDelayMs
; // Amount of time to wait before calling DNSServiceGetAddrInfo().
9145 dispatch_source_t caseTimer
; // Timer for enforcing a test case time limits.
9146 pcap_t
* pcap
; // Captures traffic between mDNSResponder and test DNS server.
9147 pid_t serverPID
; // PID of the test DNS server.
9148 int serverDelayMs
; // Additional time to have the server delay its responses by.
9149 int serverDefaultTTL
; // Default TTL for the server's records.
9150 GAITesterEventHandler_f eventHandler
; // User's event handler.
9151 void * eventContext
; // User's event handler context.
9152 GAITesterResultsHandler_f resultsHandler
; // User's results handler.
9153 void * resultsContext
; // User's results handler context.
9155 // Variables for current test item.
9157 uint64_t bitmapV4
; // Bitmap of IPv4 results that have yet to be received.
9158 uint64_t bitmapV6
; // Bitmap of IPv6 results that have yet to be received.
9159 uint64_t startTicks
; // Start ticks of DNSServiceGetAddrInfo().
9160 uint64_t connTicks
; // Ticks when the connection was created.
9161 uint64_t firstTicks
; // Ticks when the first DNSServiceGetAddrInfo result was received.
9162 uint64_t endTicks
; // Ticks when the last DNSServiceGetAddrInfo result was received.
9163 Boolean gotFirstResult
; // True if the first result has been received.
9166 CF_CLASS_DEFINE( GAITester
);
9168 static void _GAITesterRun( void *inContext
);
9169 static OSStatus
_GAITesterCreatePacketCapture( pcap_t
**outPCap
);
9170 static void _GAITesterTimeout( void *inContext
);
9171 static void _GAITesterAdvanceCurrentItem( GAITesterRef inTester
);
9172 static void _GAITesterAdvanceCurrentSet( GAITesterRef inTester
);
9173 static void _GAITesterInitializeCurrentTest( GAITesterRef inTester
);
9174 static void DNSSD_API
9175 _GAITesterGetAddrInfoCallback(
9176 DNSServiceRef inSDRef
,
9177 DNSServiceFlags inFlags
,
9178 uint32_t inInterfaceIndex
,
9179 DNSServiceErrorType inError
,
9180 const char * inHostname
,
9181 const struct sockaddr
* inSockAddr
,
9184 static void _GAITesterCompleteCurrentTest( GAITesterRef inTester
, Boolean inTimedOut
);
9186 #define ForgetPacketCapture( X ) ForgetCustom( X, pcap_close )
9190 const char * inName
,
9191 unsigned int inAddressCount
,
9192 GAITestAddrType inHasAddrs
,
9193 GAITestAddrType inWantAddrs
,
9194 GAITestItem
** outItem
);
9195 static OSStatus
GAITestItemDuplicate( const GAITestItem
*inItem
, GAITestItem
**outItem
);
9196 static void GAITestItemFree( GAITestItem
*inItem
);
9200 dispatch_queue_t inQueue
,
9202 int inServerDelayMs
,
9203 int inServerDefaultTTL
,
9204 GAITesterRef
* outTester
)
9207 GAITesterRef obj
= NULL
;
9209 CF_OBJECT_CREATE( GAITester
, obj
, err
, exit
);
9211 ReplaceDispatchQueue( &obj
->queue
, inQueue
);
9212 obj
->callDelayMs
= inCallDelayMs
;
9213 obj
->serverPID
= -1;
9214 obj
->serverDelayMs
= inServerDelayMs
;
9215 obj
->serverDefaultTTL
= inServerDefaultTTL
;
9222 CFReleaseNullSafe( obj
);
9226 //===========================================================================================================================
9227 // _GAITesterFinalize
9228 //===========================================================================================================================
9230 static void _GAITesterFinalize( CFTypeRef inObj
)
9232 GAITesterRef
const me
= (GAITesterRef
) inObj
;
9233 GAITestCase
* testCase
;
9235 check( !me
->opRef
);
9236 check( !me
->mainRef
);
9237 check( !me
->caseTimer
);
9238 dispatch_forget( &me
->queue
);
9239 while( ( testCase
= me
->caseList
) != NULL
)
9241 me
->caseList
= testCase
->next
;
9242 GAITestCaseFree( testCase
);
9246 //===========================================================================================================================
9248 //===========================================================================================================================
9250 static void _GAITesterStart( void *inContext
);
9251 static void _GAITesterStop( GAITesterRef me
);
9253 static void GAITesterStart( GAITesterRef me
)
9256 dispatch_async_f( me
->queue
, me
, _GAITesterStart
);
9259 extern char ** environ
;
9261 static void _GAITesterStart( void *inContext
)
9264 GAITesterRef
const me
= (GAITesterRef
) inContext
;
9268 char command
[ 128 ];
9270 ptr
= &command
[ 0 ];
9271 end
= &command
[ countof( command
) ];
9272 SNPrintF_Add( &ptr
, end
, "dnssdutil server --loopback --followPID %lld", (int64_t) getpid() );
9273 if( me
->serverDefaultTTL
>= 0 ) SNPrintF_Add( &ptr
, end
, " --defaultTTL %d", me
->serverDefaultTTL
);
9274 if( me
->serverDelayMs
>= 0 ) SNPrintF_Add( &ptr
, end
, " --responseDelay %d", me
->serverDelayMs
);
9276 argv
[ 0 ] = "/bin/sh";
9278 argv
[ 2 ] = command
;
9280 err
= posix_spawn( &me
->serverPID
, argv
[ 0 ], NULL
, NULL
, argv
, environ
);
9281 require_noerr( err
, exit
);
9283 me
->currentCase
= me
->caseList
;
9284 me
->currentItem
= me
->currentCase
? me
->currentCase
->itemList
: NULL
;
9285 _GAITesterInitializeCurrentTest( me
);
9287 // Hack: The first tester run is delayed for three seconds to allow the test DNS server to start up.
9288 // A better way to handle this is to issue an asynchronous query for something in the d.test. domain. As soon as an
9289 // expected response is received, the server can be considered to be up and running.
9292 dispatch_after_f( dispatch_time_seconds( 3 ), me
->queue
, me
, _GAITesterRun
);
9296 if( me
->eventHandler
) me
->eventHandler( kGAITesterEvent_Started
, me
->eventContext
);
9299 if( err
) _GAITesterStop( me
);
9303 //===========================================================================================================================
9305 //===========================================================================================================================
9307 static void _GAITesterUserStop( void *inContext
);
9309 static void GAITesterStop( GAITesterRef me
)
9312 dispatch_async_f( me
->queue
, me
, _GAITesterUserStop
);
9315 static void _GAITesterUserStop( void *inContext
)
9317 GAITesterRef
const me
= (GAITesterRef
) inContext
;
9319 _GAITesterStop( me
);
9323 static void _GAITesterStop( GAITesterRef me
)
9327 DNSServiceForget( &me
->opRef
);
9328 DNSServiceForget( &me
->mainRef
);
9329 ForgetPacketCapture( &me
->pcap
);
9330 dispatch_source_forget( &me
->caseTimer
);
9331 if( me
->serverPID
!= -1 )
9333 err
= kill( me
->serverPID
, SIGTERM
);
9334 err
= map_global_noerr_errno( err
);
9341 if( me
->eventHandler
) me
->eventHandler( kGAITesterEvent_Stopped
, me
->eventContext
);
9342 if( me
->started
) CFRelease( me
);
9346 //===========================================================================================================================
9348 //===========================================================================================================================
9350 static void GAITesterAddCase( GAITesterRef me
, GAITestCase
*inCase
)
9354 for( ptr
= &me
->caseList
; *ptr
!= NULL
; ptr
= &( *ptr
)->next
) {}
9358 //===========================================================================================================================
9359 // GAITesterSetEventHandler
9360 //===========================================================================================================================
9362 static void GAITesterSetEventHandler( GAITesterRef me
, GAITesterEventHandler_f inEventHandler
, void *inEventContext
)
9364 me
->eventHandler
= inEventHandler
;
9365 me
->eventContext
= inEventContext
;
9368 //===========================================================================================================================
9369 // GAITesterSetResultsHandler
9370 //===========================================================================================================================
9372 static void GAITesterSetResultsHandler( GAITesterRef me
, GAITesterResultsHandler_f inResultsHandler
, void *inResultsContext
)
9374 me
->resultsHandler
= inResultsHandler
;
9375 me
->resultsContext
= inResultsContext
;
9378 //===========================================================================================================================
9380 //===========================================================================================================================
9382 static void _GAITesterRun( void *inContext
)
9385 GAITesterRef
const me
= (GAITesterRef
) inContext
;
9387 GAITestItemResult
* results
= NULL
;
9389 require_action_quiet( !me
->stopped
, exit
, err
= kNoErr
);
9393 item
= me
->currentItem
;
9396 DNSServiceProtocol protocols
;
9398 check( !me
->opRef
);
9399 check( ( me
->bitmapV4
!= 0 ) || ( me
->bitmapV6
!= 0 ) );
9401 // Perform preliminary tasks if this is the start of a new test case.
9403 if( item
== me
->currentCase
->itemList
)
9405 // Flush mDNSResponder's cache.
9407 err
= systemf( NULL
, "killall -HUP mDNSResponder" );
9408 require_noerr( err
, exit
);
9409 usleep( kMicrosecondsPerSecond
);
9411 // Start a packet capture.
9414 err
= _GAITesterCreatePacketCapture( &me
->pcap
);
9415 require_noerr( err
, exit
);
9417 // Start the test case time limit timer.
9419 check( !me
->caseTimer
);
9420 if( me
->currentCase
->timeLimitMs
> 0 )
9422 const int64_t timeLimitSecs
= ( me
->currentCase
->timeLimitMs
+ 999 ) / 1000;
9424 err
= DispatchTimerCreate( dispatch_time_seconds( timeLimitSecs
), DISPATCH_TIME_FOREVER
,
9425 ( (uint64_t) timeLimitSecs
) * kNanosecondsPerSecond
/ 10,
9426 me
->queue
, _GAITesterTimeout
, NULL
, me
, &me
->caseTimer
);
9427 require_noerr( err
, exit
);
9428 dispatch_resume( me
->caseTimer
);
9431 me
->caseStartTime
= GetCurrentMicroTime();
9434 // Call DNSServiceGetAddrInfo().
9436 if( me
->callDelayMs
> 0 ) usleep( ( (useconds_t
) me
->callDelayMs
) * kMicrosecondsPerMillisecond
);
9439 if( item
->wantV4
) protocols
|= kDNSServiceProtocol_IPv4
;
9440 if( item
->wantV6
) protocols
|= kDNSServiceProtocol_IPv6
;
9442 check( !me
->mainRef
);
9443 me
->startTicks
= UpTicks();
9445 err
= DNSServiceCreateConnection( &me
->mainRef
);
9446 require_noerr( err
, exit
);
9448 err
= DNSServiceSetDispatchQueue( me
->mainRef
, me
->queue
);
9449 require_noerr( err
, exit
);
9451 me
->connTicks
= UpTicks();
9453 me
->opRef
= me
->mainRef
;
9454 err
= DNSServiceGetAddrInfo( &me
->opRef
, kDNSServiceFlagsShareConnection
| kDNSServiceFlagsReturnIntermediates
,
9455 kDNSServiceInterfaceIndexAny
, protocols
, item
->name
, _GAITesterGetAddrInfoCallback
, me
);
9456 require_noerr( err
, exit
);
9461 // No more test items means that this test case has completed (or timed out).
9463 me
->caseEndTime
= GetCurrentMicroTime();
9464 dispatch_source_forget( &me
->caseTimer
);
9465 ForgetPacketCapture( &me
->pcap
);
9467 if( me
->resultsHandler
)
9469 size_t resultCount
, itemCount
, i
;
9475 for( item
= me
->currentCase
->itemList
; item
; item
= item
->next
)
9479 if( item
->timeUs
< 0 )
9490 if( resultCount
> 0 )
9492 results
= (GAITestItemResult
*) calloc( resultCount
, sizeof( *results
) );
9493 require_action( results
, exit
, err
= kNoMemoryErr
);
9495 item
= me
->currentCase
->itemList
;
9496 for( i
= 0; i
< resultCount
; ++i
)
9498 results
[ i
].name
= item
->name
;
9499 results
[ i
].connectionTimeUs
= item
->connectionTimeUs
;
9500 results
[ i
].firstTimeUs
= item
->firstTimeUs
;
9501 results
[ i
].timeUs
= item
->timeUs
;
9505 me
->resultsHandler( me
->currentCase
->title
, me
->caseStartTime
, me
->caseEndTime
, results
, resultCount
,
9506 itemCount
, me
->resultsContext
);
9507 ForgetMem( &results
);
9510 _GAITesterAdvanceCurrentSet( me
);
9511 require_action_quiet( me
->currentCase
, exit
, err
= kEndingErr
);
9516 FreeNullSafe( results
);
9517 if( err
) _GAITesterStop( me
);
9521 //===========================================================================================================================
9522 // _GAITesterCreatePacketCapture
9523 //===========================================================================================================================
9525 static OSStatus
_GAITesterCreatePacketCapture( pcap_t
**outPCap
)
9529 struct bpf_program program
;
9530 char errBuf
[ PCAP_ERRBUF_SIZE
];
9532 pcap
= pcap_create( "lo0", errBuf
);
9533 require_action_string( pcap
, exit
, err
= kUnknownErr
, errBuf
);
9535 err
= pcap_set_buffer_size( pcap
, 512 * kBytesPerKiloByte
);
9536 require_noerr_action( err
, exit
, err
= kUnknownErr
);
9538 err
= pcap_set_snaplen( pcap
, 512 );
9539 require_noerr_action( err
, exit
, err
= kUnknownErr
);
9541 err
= pcap_set_immediate_mode( pcap
, 0 );
9542 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
9544 err
= pcap_activate( pcap
);
9545 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
9547 err
= pcap_setdirection( pcap
, PCAP_D_INOUT
);
9548 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
9550 err
= pcap_setnonblock( pcap
, 1, errBuf
);
9551 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
9553 err
= pcap_compile( pcap
, &program
, "udp port 53", 1, PCAP_NETMASK_UNKNOWN
);
9554 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
9556 err
= pcap_setfilter( pcap
, &program
);
9557 pcap_freecode( &program
);
9558 require_noerr_action_string( err
, exit
, err
= kUnknownErr
, pcap_geterr( pcap
) );
9564 if( pcap
) pcap_close( pcap
);
9568 //===========================================================================================================================
9569 // _GAITesterTimeout
9570 //===========================================================================================================================
9572 static void _GAITesterTimeout( void *inContext
)
9574 GAITesterRef
const me
= (GAITesterRef
) inContext
;
9576 dispatch_source_forget( &me
->caseTimer
);
9578 _GAITesterCompleteCurrentTest( me
, true );
9581 //===========================================================================================================================
9582 // _GAITesterAdvanceCurrentItem
9583 //===========================================================================================================================
9585 static void _GAITesterAdvanceCurrentItem( GAITesterRef me
)
9587 if( me
->currentItem
)
9589 me
->currentItem
= me
->currentItem
->next
;
9590 _GAITesterInitializeCurrentTest( me
);
9594 //===========================================================================================================================
9595 // _GAITesterAdvanceCurrentSet
9596 //===========================================================================================================================
9598 static void _GAITesterAdvanceCurrentSet( GAITesterRef me
)
9600 if( me
->currentCase
)
9602 me
->caseStartTime
= 0;
9603 me
->caseEndTime
= 0;
9604 me
->currentCase
= me
->currentCase
->next
;
9605 if( me
->currentCase
)
9607 me
->currentItem
= me
->currentCase
->itemList
;
9608 _GAITesterInitializeCurrentTest( me
);
9613 //===========================================================================================================================
9614 // _GAITesterInitializeCurrentTest
9615 //===========================================================================================================================
9617 static void _GAITesterInitializeCurrentTest( GAITesterRef me
)
9619 GAITestItem
* const item
= me
->currentItem
;
9623 check( item
->addressCount
> 0 );
9626 me
->bitmapV4
= item
->hasV4
? ( ( UINT64_C( 1 ) << item
->addressCount
) - 1 ) : 1;
9635 me
->bitmapV6
= item
->hasV6
? ( ( UINT64_C( 1 ) << item
->addressCount
) - 1 ) : 1;
9641 me
->gotFirstResult
= false;
9645 //===========================================================================================================================
9646 // _GAITesterGetAddrInfoCallback
9647 //===========================================================================================================================
9649 static void DNSSD_API
9650 _GAITesterGetAddrInfoCallback(
9651 DNSServiceRef inSDRef
,
9652 DNSServiceFlags inFlags
,
9653 uint32_t inInterfaceIndex
,
9654 DNSServiceErrorType inError
,
9655 const char * inHostname
,
9656 const struct sockaddr
* inSockAddr
,
9660 GAITesterRef
const me
= (GAITesterRef
) inContext
;
9661 GAITestItem
* const item
= me
->currentItem
;
9662 const sockaddr_ip
* const sip
= (const sockaddr_ip
*) inSockAddr
;
9664 uint64_t * bitmapPtr
;
9666 unsigned int addrOffset
;
9669 Unused( inInterfaceIndex
);
9670 Unused( inHostname
);
9673 nowTicks
= UpTicks();
9675 require_quiet( inFlags
& kDNSServiceFlagsAdd
, exit
);
9676 require_quiet( !inError
|| ( inError
== kDNSServiceErr_NoSuchRecord
), exit
);
9680 if( ( sip
->sa
.sa_family
== AF_INET
) && item
->wantV4
)
9686 const uint32_t addrV4
= ntohl( sip
->v4
.sin_addr
.s_addr
);
9688 if( strcasecmp( item
->name
, "localhost." ) == 0 )
9690 if( addrV4
== INADDR_LOOPBACK
)
9693 bitmapPtr
= &me
->bitmapV4
;
9698 addrOffset
= addrV4
- kTestDNSServerBaseAddrV4
;
9699 if( ( addrOffset
>= 1 ) && ( addrOffset
<= item
->addressCount
) )
9701 bitmask
= UINT64_C( 1 ) << ( addrOffset
- 1 );
9702 bitmapPtr
= &me
->bitmapV4
;
9707 else if( inError
== kDNSServiceErr_NoSuchRecord
)
9710 bitmapPtr
= &me
->bitmapV4
;
9713 else if( ( sip
->sa
.sa_family
== AF_INET6
) && item
->wantV6
)
9719 const uint8_t * const addrV6
= sip
->v6
.sin6_addr
.s6_addr
;
9721 if( strcasecmp( item
->name
, "localhost." ) == 0 )
9723 if( memcmp( addrV6
, in6addr_loopback
.s6_addr
, 16 ) == 0 )
9726 bitmapPtr
= &me
->bitmapV6
;
9729 else if( memcmp( addrV6
, kTestDNSServerBaseAddrV6
, 15 ) == 0 )
9731 addrOffset
= addrV6
[ 15 ];
9732 if( ( addrOffset
>= 1 ) && ( addrOffset
<= item
->addressCount
) )
9734 bitmask
= UINT64_C( 1 ) << ( addrOffset
- 1 );
9735 bitmapPtr
= &me
->bitmapV6
;
9740 else if( inError
== kDNSServiceErr_NoSuchRecord
)
9743 bitmapPtr
= &me
->bitmapV6
;
9747 if( bitmapPtr
&& ( *bitmapPtr
& bitmask
) )
9749 *bitmapPtr
&= ~bitmask
;
9750 if( !me
->gotFirstResult
)
9752 me
->firstTicks
= nowTicks
;
9753 me
->gotFirstResult
= true;
9756 if( ( me
->bitmapV4
== 0 ) && ( me
->bitmapV6
== 0 ) )
9758 me
->endTicks
= nowTicks
;
9759 _GAITesterCompleteCurrentTest( me
, false );
9767 //===========================================================================================================================
9768 // _GAITesterCompleteCurrentTest
9769 //===========================================================================================================================
9772 _GAITesterGetDNSMessageFromPacket(
9773 const uint8_t * inPacketPtr
,
9775 const uint8_t ** outMsgPtr
,
9776 size_t * outMsgLen
);
9778 static void _GAITesterCompleteCurrentTest( GAITesterRef me
, Boolean inTimedOut
)
9782 struct timeval
* tsQA
= NULL
;
9783 struct timeval
* tsQAAAA
= NULL
;
9784 struct timeval
* tsRA
= NULL
;
9785 struct timeval
* tsRAAAA
= NULL
;
9786 struct timeval timeStamps
[ 4 ];
9787 struct timeval
* tsPtr
= &timeStamps
[ 0 ];
9788 struct timeval
* tsQ
;
9789 struct timeval
* tsR
;
9791 uint8_t name
[ kDomainNameLengthMax
];
9793 DNSServiceForget( &me
->opRef
);
9794 DNSServiceForget( &me
->mainRef
);
9798 for( item
= me
->currentItem
; item
; item
= item
->next
)
9800 item
->firstTimeUs
= -1;
9803 me
->currentItem
= NULL
;
9806 dispatch_async_f( me
->queue
, me
, _GAITesterRun
);
9810 item
= me
->currentItem
;
9811 err
= DomainNameFromString( name
, item
->name
, NULL
);
9812 require_noerr( err
, exit
);
9817 struct pcap_pkthdr
* pktHdr
;
9818 const uint8_t * packet
;
9819 const uint8_t * msgPtr
;
9821 const DNSHeader
* hdr
;
9823 const uint8_t * ptr
;
9824 const DNSQuestionFixedFields
* qfields
;
9826 uint8_t qname
[ kDomainNameLengthMax
];
9828 status
= pcap_next_ex( me
->pcap
, &pktHdr
, &packet
);
9829 if( status
!= 1 ) break;
9830 if( _GAITesterGetDNSMessageFromPacket( packet
, pktHdr
->caplen
, &msgPtr
, &msgLen
) != kNoErr
) continue;
9831 if( msgLen
< kDNSHeaderLength
) continue;
9833 hdr
= (const DNSHeader
*) msgPtr
;
9834 flags
= DNSHeaderGetFlags( hdr
);
9835 if( DNSFlagsGetOpCode( flags
) != kDNSOpCode_Query
) continue;
9836 if( DNSHeaderGetQuestionCount( hdr
) < 1 ) continue;
9838 ptr
= (const uint8_t *) &hdr
[ 1 ];
9839 if( DNSMessageExtractDomainName( msgPtr
, msgLen
, ptr
, qname
, &ptr
) != kNoErr
) continue;
9840 if( !DomainNameEqual( qname
, name
) ) continue;
9842 qfields
= (const DNSQuestionFixedFields
*) ptr
;
9843 if( DNSQuestionFixedFieldsGetClass( qfields
) != kDNSServiceClass_IN
) continue;
9845 qtype
= DNSQuestionFixedFieldsGetType( qfields
);
9846 if( item
->wantV4
&& ( qtype
== kDNSServiceType_A
) )
9848 if( flags
& kDNSHeaderFlag_Response
)
9862 else if( item
->wantV6
&& ( qtype
== kDNSServiceType_AAAA
) )
9864 if( flags
& kDNSHeaderFlag_Response
)
9866 if( tsQAAAA
&& !tsRAAAA
)
9869 *tsRAAAA
= pktHdr
->ts
;
9875 *tsQAAAA
= pktHdr
->ts
;
9880 if( tsQA
&& tsQAAAA
) tsQ
= TIMEVAL_GT( *tsQA
, *tsQAAAA
) ? tsQA
: tsQAAAA
;
9881 else tsQ
= tsQA
? tsQA
: tsQAAAA
;
9883 if( tsRA
&& tsRAAAA
) tsR
= TIMEVAL_LT( *tsRA
, *tsRAAAA
) ? tsRA
: tsRAAAA
;
9884 else tsR
= tsQA
? tsQA
: tsQAAAA
;
9888 idleTimeUs
= TIMEVAL_USEC64_DIFF( *tsR
, *tsQ
);
9889 if( idleTimeUs
< 0 ) idleTimeUs
= 0;
9896 item
->connectionTimeUs
= (int64_t) UpTicksToMicroseconds( me
->connTicks
- me
->startTicks
);
9897 item
->firstTimeUs
= (int64_t)( UpTicksToMicroseconds( me
->firstTicks
- me
->connTicks
) - (uint64_t) idleTimeUs
);
9898 item
->timeUs
= (int64_t)( UpTicksToMicroseconds( me
->endTicks
- me
->connTicks
) - (uint64_t) idleTimeUs
);
9900 _GAITesterAdvanceCurrentItem( me
);
9902 dispatch_async_f( me
->queue
, me
, _GAITesterRun
);
9905 if( err
) _GAITesterStop( me
);
9908 //===========================================================================================================================
9909 // _GAITesterGetDNSMessageFromPacket
9910 //===========================================================================================================================
9912 #define kHeaderSizeNullLink 4
9913 #define kHeaderSizeIPv4Min 20
9914 #define kHeaderSizeIPv6 40
9915 #define kHeaderSizeUDP 8
9917 #define kIPProtocolUDP 0x11
9920 _GAITesterGetDNSMessageFromPacket(
9921 const uint8_t * inPacketPtr
,
9923 const uint8_t ** outMsgPtr
,
9924 size_t * outMsgLen
)
9927 const uint8_t * nullLink
;
9928 uint32_t addressFamily
;
9932 const uint8_t * msg
;
9933 const uint8_t * const end
= &inPacketPtr
[ inPacketLen
];
9935 nullLink
= &inPacketPtr
[ 0 ];
9936 require_action_quiet( ( end
- nullLink
) >= kHeaderSizeNullLink
, exit
, err
= kUnderrunErr
);
9937 addressFamily
= ReadHost32( &nullLink
[ 0 ] );
9939 ip
= &nullLink
[ kHeaderSizeNullLink
];
9940 if( addressFamily
== AF_INET
)
9942 require_action_quiet( ( end
- ip
) >= kHeaderSizeIPv4Min
, exit
, err
= kUnderrunErr
);
9943 ipHeaderLen
= ( ip
[ 0 ] & 0x0F ) * 4;
9946 else if( addressFamily
== AF_INET6
)
9948 require_action_quiet( ( end
- ip
) >= kHeaderSizeIPv6
, exit
, err
= kUnderrunErr
);
9949 ipHeaderLen
= kHeaderSizeIPv6
;
9957 require_action_quiet( protocol
== kIPProtocolUDP
, exit
, err
= kTypeErr
);
9958 require_action_quiet( ( end
- ip
) >= ( ipHeaderLen
+ kHeaderSizeUDP
), exit
, err
= kUnderrunErr
);
9960 msg
= &ip
[ ipHeaderLen
+ kHeaderSizeUDP
];
9963 *outMsgLen
= (size_t)( end
- msg
);
9970 //===========================================================================================================================
9971 // GAITestCaseCreate
9972 //===========================================================================================================================
9974 static OSStatus
GAITestCaseCreate( const char *inTitle
, unsigned int inTimeLimitMs
, GAITestCase
**outSet
)
9979 obj
= (GAITestCase
*) calloc( 1, sizeof( *obj
) );
9980 require_action( obj
, exit
, err
= kNoMemoryErr
);
9982 obj
->title
= strdup( inTitle
);
9983 require_action( obj
->title
, exit
, err
= kNoMemoryErr
);
9985 obj
->timeLimitMs
= inTimeLimitMs
;
9992 if( obj
) GAITestCaseFree( obj
);
9996 //===========================================================================================================================
9998 //===========================================================================================================================
10000 static void GAITestCaseFree( GAITestCase
*inCase
)
10002 GAITestItem
* item
;
10004 while( ( item
= inCase
->itemList
) != NULL
)
10006 inCase
->itemList
= item
->next
;
10007 GAITestItemFree( item
);
10009 ForgetMem( &inCase
->title
);
10013 //===========================================================================================================================
10014 // GAITestCaseAddItem
10015 //===========================================================================================================================
10017 // A character set of lower-case alphabet characters and digits and a string length of six allows for 36^6 = 2,176,782,336
10018 // possible strings to use in the Tag label.
10020 #define kUniqueStringCharSet "abcdefghijklmnopqrstuvwxyz0123456789"
10021 #define kUniqueStringCharSetLen sizeof_string( kUniqueStringCharSet )
10022 #define kUniqueStringLen 6
10025 GAITestCaseAddItem(
10026 GAITestCase
* inCase
,
10027 unsigned int inAliasCount
,
10028 unsigned int inAddressCount
,
10030 GAITestAddrType inHasAddrs
,
10031 GAITestAddrType inWantAddrs
,
10032 unsigned int inItemCount
)
10035 GAITestItem
* item
;
10036 GAITestItem
* item2
;
10037 GAITestItem
* newItemList
= NULL
;
10038 GAITestItem
** itemPtr
;
10043 char uniqueStr
[ kUniqueStringLen
+ 1 ];
10045 require_action_quiet( inItemCount
> 0, exit
, err
= kNoErr
);
10047 // Limit address count to 64 because we use 64-bit bitmaps for keeping track of addresses.
10049 require_action_quiet( ( inAddressCount
>= 1 ) && ( inAddressCount
<= 64 ), exit
, err
= kCountErr
);
10050 require_action_quiet( ( inAliasCount
>= 0 ) && ( inAliasCount
<= INT32_MAX
), exit
, err
= kCountErr
);
10051 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs
), exit
, err
= kValueErr
);
10054 end
= &name
[ countof( name
) ];
10056 // Add Alias label.
10058 if( inAliasCount
== 1 ) SNPrintF_Add( &ptr
, end
, "alias." );
10059 else if( inAliasCount
>= 2 ) SNPrintF_Add( &ptr
, end
, "alias-%u.", inAliasCount
);
10061 // Add Count label.
10063 SNPrintF_Add( &ptr
, end
, "count-%u.", inAddressCount
);
10067 if( inTTL
>= 0 ) SNPrintF_Add( &ptr
, end
, "ttl-%d.", inTTL
);
10071 RandomString( kUniqueStringCharSet
, kUniqueStringCharSetLen
, kUniqueStringLen
, kUniqueStringLen
, uniqueStr
);
10072 SNPrintF_Add( &ptr
, end
, "tag-%s.", uniqueStr
);
10074 // Add IPv4 or IPv6 label if necessary.
10076 switch( inHasAddrs
)
10078 case kGAITestAddrType_IPv4
:
10079 SNPrintF_Add( &ptr
, end
, "ipv4." );
10082 case kGAITestAddrType_IPv6
:
10083 SNPrintF_Add( &ptr
, end
, "ipv6." );
10087 // Add d.test. labels.
10089 SNPrintF_Add( &ptr
, end
, "d.test." );
10093 err
= GAITestItemCreate( name
, inAddressCount
, inHasAddrs
, inWantAddrs
, &item
);
10094 require_noerr( err
, exit
);
10096 newItemList
= item
;
10097 itemPtr
= &item
->next
;
10099 // Create repeat items.
10101 for( i
= 1; i
< inItemCount
; ++i
)
10103 err
= GAITestItemDuplicate( item
, &item2
);
10104 require_noerr( err
, exit
);
10107 itemPtr
= &item2
->next
;
10110 // Append to test case's item list.
10112 for( itemPtr
= &inCase
->itemList
; *itemPtr
; itemPtr
= &( *itemPtr
)->next
) {}
10113 *itemPtr
= newItemList
;
10114 newItemList
= NULL
;
10117 while( ( item
= newItemList
) != NULL
)
10119 newItemList
= item
->next
;
10120 GAITestItemFree( item
);
10125 //===========================================================================================================================
10126 // GAITestCaseAddLocalHostItem
10127 //===========================================================================================================================
10129 static OSStatus
GAITestCaseAddLocalHostItem( GAITestCase
*inCase
, GAITestAddrType inWantAddrs
, unsigned int inItemCount
)
10132 GAITestItem
* item
;
10133 GAITestItem
* item2
;
10134 GAITestItem
* newItemList
= NULL
;
10135 GAITestItem
** itemPtr
;
10138 require_action_quiet( inItemCount
> 1, exit
, err
= kNoErr
);
10140 err
= GAITestItemCreate( "localhost.", 1, kGAITestAddrType_Both
, inWantAddrs
, &item
);
10141 require_noerr( err
, exit
);
10143 newItemList
= item
;
10144 itemPtr
= &item
->next
;
10146 // Create repeat items.
10148 for( i
= 1; i
< inItemCount
; ++i
)
10150 err
= GAITestItemDuplicate( item
, &item2
);
10151 require_noerr( err
, exit
);
10154 itemPtr
= &item2
->next
;
10157 for( itemPtr
= &inCase
->itemList
; *itemPtr
; itemPtr
= &( *itemPtr
)->next
) {}
10158 *itemPtr
= newItemList
;
10159 newItemList
= NULL
;
10162 while( ( item
= newItemList
) != NULL
)
10164 newItemList
= item
->next
;
10165 GAITestItemFree( item
);
10170 //===========================================================================================================================
10171 // GAITestItemCreate
10172 //===========================================================================================================================
10176 const char * inName
,
10177 unsigned int inAddressCount
,
10178 GAITestAddrType inHasAddrs
,
10179 GAITestAddrType inWantAddrs
,
10180 GAITestItem
** outItem
)
10183 GAITestItem
* obj
= NULL
;
10185 require_action_quiet( inAddressCount
>= 1, exit
, err
= kCountErr
);
10186 require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs
), exit
, err
= kValueErr
);
10187 require_action_quiet( GAITestAddrTypeIsValid( inWantAddrs
), exit
, err
= kValueErr
);
10189 obj
= (GAITestItem
*) calloc( 1, sizeof( *obj
) );
10190 require_action( obj
, exit
, err
= kNoMemoryErr
);
10192 obj
->name
= strdup( inName
);
10193 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
10195 obj
->addressCount
= inAddressCount
;
10196 obj
->hasV4
= ( inHasAddrs
& kGAITestAddrType_IPv4
) ? true : false;
10197 obj
->hasV6
= ( inHasAddrs
& kGAITestAddrType_IPv6
) ? true : false;
10198 obj
->wantV4
= ( inWantAddrs
& kGAITestAddrType_IPv4
) ? true : false;
10199 obj
->wantV6
= ( inWantAddrs
& kGAITestAddrType_IPv6
) ? true : false;
10206 if( obj
) GAITestItemFree( obj
);
10210 //===========================================================================================================================
10211 // GAITestItemDuplicate
10212 //===========================================================================================================================
10214 static OSStatus
GAITestItemDuplicate( const GAITestItem
*inItem
, GAITestItem
**outItem
)
10219 obj
= (GAITestItem
*) calloc( 1, sizeof( *obj
) );
10220 require_action( obj
, exit
, err
= kNoMemoryErr
);
10226 obj
->name
= strdup( inItem
->name
);
10227 require_action( obj
->name
, exit
, err
= kNoMemoryErr
);
10235 if( obj
) GAITestItemFree( obj
);
10239 //===========================================================================================================================
10241 //===========================================================================================================================
10243 static void GAITestItemFree( GAITestItem
*inItem
)
10245 ForgetMem( &inItem
->name
);
10249 //===========================================================================================================================
10251 //===========================================================================================================================
10253 #define kSSDPPort 1900
10257 HTTPHeader header
; // HTTP header object for sending and receiving.
10258 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
10259 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
10260 int receiveSecs
; // After send, the amount of time to spend receiving.
10261 uint32_t ifindex
; // Index of the interface over which to send the query.
10262 Boolean useIPv4
; // True if the query should be sent via IPv4 multicast.
10263 Boolean useIPv6
; // True if the query should be sent via IPv6 multicast.
10265 } SSDPDiscoverContext
;
10267 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext
*inContext
);
10268 static void SSDPDiscoverReadHandler( void *inContext
);
10269 static int SocketToPortNumber( SocketRef inSock
);
10270 static OSStatus
WriteSSDPSearchRequest( HTTPHeader
*inHeader
, const void *inHostSA
, int inMX
, const char *inST
);
10272 static void SSDPDiscoverCmd( void )
10275 struct timeval now
;
10276 SSDPDiscoverContext
* context
;
10277 dispatch_source_t signalSource
= NULL
;
10278 SocketRef sockV4
= kInvalidSocketRef
;
10279 SocketRef sockV6
= kInvalidSocketRef
;
10283 // Set up SIGINT handler.
10285 signal( SIGINT
, SIG_IGN
);
10286 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
10287 require_noerr( err
, exit
);
10288 dispatch_resume( signalSource
);
10290 // Check command parameters.
10292 if( gSSDPDiscover_ReceiveSecs
< -1 )
10294 FPrintF( stdout
, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs
);
10301 context
= (SSDPDiscoverContext
*) calloc( 1, sizeof( *context
) );
10302 require_action( context
, exit
, err
= kNoMemoryErr
);
10304 context
->receiveSecs
= gSSDPDiscover_ReceiveSecs
;
10305 context
->useIPv4
= ( gSSDPDiscover_UseIPv4
|| !gSSDPDiscover_UseIPv6
) ? true : false;
10306 context
->useIPv6
= ( gSSDPDiscover_UseIPv6
|| !gSSDPDiscover_UseIPv4
) ? true : false;
10308 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifindex
);
10309 require_noerr_quiet( err
, exit
);
10311 // Set up IPv4 socket.
10313 if( context
->useIPv4
)
10316 err
= UDPClientSocketOpen( AF_INET
, NULL
, 0, -1, &port
, &sockV4
);
10317 require_noerr( err
, exit
);
10319 err
= SocketSetMulticastInterface( sockV4
, NULL
, context
->ifindex
);
10320 require_noerr( err
, exit
);
10322 err
= setsockopt( sockV4
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *) &(uint8_t){ 1 }, (socklen_t
) sizeof( uint8_t ) );
10323 err
= map_socket_noerr_errno( sockV4
, err
);
10324 require_noerr( err
, exit
);
10327 // Set up IPv6 socket.
10329 if( context
->useIPv6
)
10331 err
= UDPClientSocketOpen( AF_INET6
, NULL
, 0, -1, NULL
, &sockV6
);
10332 require_noerr( err
, exit
);
10334 err
= SocketSetMulticastInterface( sockV6
, NULL
, context
->ifindex
);
10335 require_noerr( err
, exit
);
10337 err
= setsockopt( sockV6
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *) &(int){ 1 }, (socklen_t
) sizeof( int ) );
10338 err
= map_socket_noerr_errno( sockV6
, err
);
10339 require_noerr( err
, exit
);
10344 SSDPDiscoverPrintPrologue( context
);
10346 // Send mDNS query message.
10349 if( IsValidSocket( sockV4
) )
10351 struct sockaddr_in mcastAddr4
;
10353 memset( &mcastAddr4
, 0, sizeof( mcastAddr4
) );
10354 SIN_LEN_SET( &mcastAddr4
);
10355 mcastAddr4
.sin_family
= AF_INET
;
10356 mcastAddr4
.sin_port
= htons( kSSDPPort
);
10357 mcastAddr4
.sin_addr
.s_addr
= htonl( 0xEFFFFFFA ); // 239.255.255.250
10359 err
= WriteSSDPSearchRequest( &context
->header
, &mcastAddr4
, gSSDPDiscover_MX
, gSSDPDiscover_ST
);
10360 require_noerr( err
, exit
);
10362 n
= sendto( sockV4
, context
->header
.buf
, context
->header
.len
, 0, (const struct sockaddr
*) &mcastAddr4
,
10363 (socklen_t
) sizeof( mcastAddr4
) );
10364 err
= map_socket_value_errno( sockV4
, n
== (ssize_t
) context
->header
.len
, n
);
10367 FPrintF( stderr
, "*** Failed to send query on IPv4 socket with error %#m\n", err
);
10368 ForgetSocket( &sockV4
);
10372 if( gSSDPDiscover_Verbose
)
10374 gettimeofday( &now
, NULL
);
10375 FPrintF( stdout
, "---\n" );
10376 FPrintF( stdout
, "Send time: %{du:time}\n", &now
);
10377 FPrintF( stdout
, "Source Port: %d\n", SocketToPortNumber( sockV4
) );
10378 FPrintF( stdout
, "Destination: %##a\n", &mcastAddr4
);
10379 FPrintF( stdout
, "Message size: %zu\n", context
->header
.len
);
10380 FPrintF( stdout
, "HTTP header:\n%1{text}", context
->header
.buf
, context
->header
.len
);
10386 if( IsValidSocket( sockV6
) )
10388 struct sockaddr_in6 mcastAddr6
;
10390 memset( &mcastAddr6
, 0, sizeof( mcastAddr6
) );
10391 SIN6_LEN_SET( &mcastAddr6
);
10392 mcastAddr6
.sin6_family
= AF_INET6
;
10393 mcastAddr6
.sin6_port
= htons( kSSDPPort
);
10394 mcastAddr6
.sin6_addr
.s6_addr
[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C
10395 mcastAddr6
.sin6_addr
.s6_addr
[ 1 ] = 0x02;
10396 mcastAddr6
.sin6_addr
.s6_addr
[ 15 ] = 0x0C;
10398 err
= WriteSSDPSearchRequest( &context
->header
, &mcastAddr6
, gSSDPDiscover_MX
, gSSDPDiscover_ST
);
10399 require_noerr( err
, exit
);
10401 n
= sendto( sockV6
, context
->header
.buf
, context
->header
.len
, 0, (const struct sockaddr
*) &mcastAddr6
,
10402 (socklen_t
) sizeof( mcastAddr6
) );
10403 err
= map_socket_value_errno( sockV6
, n
== (ssize_t
) context
->header
.len
, n
);
10406 FPrintF( stderr
, "*** Failed to send query on IPv6 socket with error %#m\n", err
);
10407 ForgetSocket( &sockV6
);
10411 if( gSSDPDiscover_Verbose
)
10413 gettimeofday( &now
, NULL
);
10414 FPrintF( stdout
, "---\n" );
10415 FPrintF( stdout
, "Send time: %{du:time}\n", &now
);
10416 FPrintF( stdout
, "Source Port: %d\n", SocketToPortNumber( sockV6
) );
10417 FPrintF( stdout
, "Destination: %##a\n", &mcastAddr6
);
10418 FPrintF( stdout
, "Message size: %zu\n", context
->header
.len
);
10419 FPrintF( stdout
, "HTTP header:\n%1{text}", context
->header
.buf
, context
->header
.len
);
10424 require_action_quiet( sendCount
> 0, exit
, err
= kUnexpectedErr
);
10426 // If there's no wait period after the send, then exit.
10428 if( context
->receiveSecs
== 0 ) goto exit
;
10430 // Create dispatch read sources for socket(s).
10432 if( IsValidSocket( sockV4
) )
10434 SocketContext
* sockCtx
;
10436 err
= SocketContextCreate( sockV4
, context
, &sockCtx
);
10437 require_noerr( err
, exit
);
10438 sockV4
= kInvalidSocketRef
;
10440 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, SSDPDiscoverReadHandler
, SocketContextCancelHandler
, sockCtx
,
10441 &context
->readSourceV4
);
10442 if( err
) ForgetSocketContext( &sockCtx
);
10443 require_noerr( err
, exit
);
10445 dispatch_resume( context
->readSourceV4
);
10448 if( IsValidSocket( sockV6
) )
10450 SocketContext
* sockCtx
;
10452 err
= SocketContextCreate( sockV6
, context
, &sockCtx
);
10453 require_noerr( err
, exit
);
10454 sockV6
= kInvalidSocketRef
;
10456 err
= DispatchReadSourceCreate( sockCtx
->sock
, NULL
, SSDPDiscoverReadHandler
, SocketContextCancelHandler
, sockCtx
,
10457 &context
->readSourceV6
);
10458 if( err
) ForgetSocketContext( &sockCtx
);
10459 require_noerr( err
, exit
);
10461 dispatch_resume( context
->readSourceV6
);
10464 if( context
->receiveSecs
> 0 )
10466 dispatch_after_f( dispatch_time_seconds( context
->receiveSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
10472 ForgetSocket( &sockV4
);
10473 ForgetSocket( &sockV6
);
10474 dispatch_source_forget( &signalSource
);
10475 if( err
) exit( 1 );
10478 static int SocketToPortNumber( SocketRef inSock
)
10484 len
= (socklen_t
) sizeof( sip
);
10485 err
= getsockname( inSock
, &sip
.sa
, &len
);
10486 err
= map_socket_noerr_errno( inSock
, err
);
10487 check_noerr( err
);
10488 return( err
? -1 : SockAddrGetPort( &sip
) );
10491 static OSStatus
WriteSSDPSearchRequest( HTTPHeader
*inHeader
, const void *inHostSA
, int inMX
, const char *inST
)
10495 err
= HTTPHeader_InitRequest( inHeader
, "M-SEARCH", "*", "HTTP/1.1" );
10496 require_noerr( err
, exit
);
10498 err
= HTTPHeader_SetField( inHeader
, "Host", "%##a", inHostSA
);
10499 require_noerr( err
, exit
);
10501 err
= HTTPHeader_SetField( inHeader
, "ST", "%s", inST
? inST
: "ssdp:all" );
10502 require_noerr( err
, exit
);
10504 err
= HTTPHeader_SetField( inHeader
, "Man", "\"ssdp:discover\"" );
10505 require_noerr( err
, exit
);
10507 err
= HTTPHeader_SetField( inHeader
, "MX", "%d", inMX
);
10508 require_noerr( err
, exit
);
10510 err
= HTTPHeader_Commit( inHeader
);
10511 require_noerr( err
, exit
);
10517 //===========================================================================================================================
10518 // SSDPDiscoverPrintPrologue
10519 //===========================================================================================================================
10521 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext
*inContext
)
10523 const int receiveSecs
= inContext
->receiveSecs
;
10524 const char * ifName
;
10525 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
10526 NetTransportType ifType
;
10528 ifName
= if_indextoname( inContext
->ifindex
, ifNameBuf
);
10530 ifType
= kNetTransportType_Undefined
;
10531 if( ifName
) SocketGetInterfaceInfo( kInvalidSocketRef
, ifName
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &ifType
);
10533 FPrintF( stdout
, "Interface: %s/%d/%s\n",
10534 ifName
? ifName
: "?", inContext
->ifindex
, NetTransportTypeToString( ifType
) );
10535 FPrintF( stdout
, "IP protocols: %?s%?s%?s\n",
10536 inContext
->useIPv4
, "IPv4", ( inContext
->useIPv4
&& inContext
->useIPv6
), ", ", inContext
->useIPv6
, "IPv6" );
10537 FPrintF( stdout
, "Receive duration: " );
10538 if( receiveSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", receiveSecs
, receiveSecs
!= 1, 's' );
10539 else FPrintF( stdout
, "∞\n" );
10540 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
10543 //===========================================================================================================================
10544 // SSDPDiscoverReadHandler
10545 //===========================================================================================================================
10547 static void SSDPDiscoverReadHandler( void *inContext
)
10550 struct timeval now
;
10551 SocketContext
* const sockCtx
= (SocketContext
*) inContext
;
10552 SSDPDiscoverContext
* const context
= (SSDPDiscoverContext
*) sockCtx
->userContext
;
10553 HTTPHeader
* const header
= &context
->header
;
10554 sockaddr_ip fromAddr
;
10557 gettimeofday( &now
, NULL
);
10559 err
= SocketRecvFrom( sockCtx
->sock
, header
->buf
, sizeof( header
->buf
), &msgLen
, &fromAddr
, sizeof( fromAddr
),
10560 NULL
, NULL
, NULL
, NULL
);
10561 require_noerr( err
, exit
);
10563 FPrintF( stdout
, "---\n" );
10564 FPrintF( stdout
, "Receive time: %{du:time}\n", &now
);
10565 FPrintF( stdout
, "Source: %##a\n", &fromAddr
);
10566 FPrintF( stdout
, "Message size: %zu\n", msgLen
);
10567 header
->len
= msgLen
;
10568 if( HTTPHeader_Validate( header
) )
10570 FPrintF( stdout
, "HTTP header:\n%1{text}", header
->buf
, header
->len
);
10571 if( header
->extraDataLen
> 0 )
10573 FPrintF( stdout
, "HTTP body: %1.1H", header
->extraDataPtr
, (int) header
->extraDataLen
, INT_MAX
);
10578 FPrintF( stdout
, "Invalid HTTP message:\n%1.1H", header
->buf
, (int) msgLen
, INT_MAX
);
10583 if( err
) exit( 1 );
10586 //===========================================================================================================================
10587 // HTTPHeader_Validate
10589 // Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid.
10590 // This assumes the "buf" and "len" fields are set. The other fields are set by this function.
10592 // Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework.
10593 //===========================================================================================================================
10595 Boolean
HTTPHeader_Validate( HTTPHeader
*inHeader
)
10600 // Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
10602 require( inHeader
->len
< sizeof( inHeader
->buf
), exit
);
10603 src
= inHeader
->buf
;
10604 end
= src
+ inHeader
->len
;
10605 if( ( ( end
- src
) >= 4 ) && ( src
[ 0 ] == '$' ) )
10611 // Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted.
10612 // $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over.
10616 while( ( src
< end
) && ( src
[ 0 ] != '\n' ) ) ++src
;
10617 if( src
>= end
) goto exit
;
10619 if( ( ( end
- src
) >= 2 ) && ( src
[ 0 ] == '\r' ) && ( src
[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF
10624 else if( ( ( end
- src
) >= 1 ) && ( src
[ 0 ] == '\n' ) ) // LFLF
10631 inHeader
->extraDataPtr
= src
;
10632 inHeader
->extraDataLen
= (size_t)( end
- src
);
10633 inHeader
->len
= (size_t)( src
- inHeader
->buf
);
10640 #if( TARGET_OS_DARWIN )
10641 //===========================================================================================================================
10643 //===========================================================================================================================
10645 // res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h).
10647 SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv
);
10648 SOFT_LINK_FUNCTION_EX( resolv
, res_9_query
,
10650 ( const char *dname
, int class, int type
, u_char
*answer
, int anslen
),
10651 ( dname
, class, type
, answer
, anslen
) );
10653 // res_query() from libinfo
10655 SOFT_LINK_LIBRARY_EX( "/usr/lib", info
);
10656 SOFT_LINK_FUNCTION_EX( info
, res_query
,
10658 ( const char *dname
, int class, int type
, u_char
*answer
, int anslen
),
10659 ( dname
, class, type
, answer
, anslen
) );
10661 typedef int ( *res_query_f
)( const char *dname
, int class, int type
, u_char
*answer
, int anslen
);
10663 static void ResQueryCmd( void )
10666 res_query_f res_query_ptr
;
10668 uint16_t type
, class;
10669 uint8_t answer
[ 1024 ];
10671 // Get pointer to one of the res_query() functions.
10673 if( gResQuery_UseLibInfo
)
10675 if( !SOFT_LINK_HAS_FUNCTION( info
, res_query
) )
10677 FPrintF( stderr
, "Failed to soft link res_query from libinfo.\n" );
10678 err
= kNotFoundErr
;
10681 res_query_ptr
= soft_res_query
;
10685 if( !SOFT_LINK_HAS_FUNCTION( resolv
, res_9_query
) )
10687 FPrintF( stderr
, "Failed to soft link res_query from libresolv.\n" );
10688 err
= kNotFoundErr
;
10691 res_query_ptr
= soft_res_9_query
;
10694 // Get record type.
10696 err
= RecordTypeFromArgString( gResQuery_Type
, &type
);
10697 require_noerr( err
, exit
);
10699 // Get record class.
10701 if( gResQuery_Class
)
10703 err
= RecordClassFromArgString( gResQuery_Class
, &class );
10704 require_noerr( err
, exit
);
10708 class = kDNSServiceClass_IN
;
10713 FPrintF( stdout
, "Name: %s\n", gResQuery_Name
);
10714 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
10715 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
10716 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
10717 FPrintF( stdout
, "---\n" );
10719 // Call res_query().
10721 n
= res_query_ptr( gResQuery_Name
, class, type
, (u_char
*) answer
, (int) sizeof( answer
) );
10724 FPrintF( stderr
, "res_query() failed with error: %d (%s).\n", h_errno
, hstrerror( h_errno
) );
10731 FPrintF( stdout
, "Message size: %d\n\n%{du:dnsmsg}", n
, answer
, (size_t) n
);
10734 if( err
) exit( 1 );
10737 //===========================================================================================================================
10738 // ResolvDNSQueryCmd
10739 //===========================================================================================================================
10741 // 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
10742 // avoid including the header file.
10744 typedef void * dns_handle_t
;
10746 SOFT_LINK_FUNCTION_EX( resolv
, dns_open
, dns_handle_t
, ( const char *path
), ( path
) );
10747 SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv
, dns_free
, ( dns_handle_t
*dns
), ( dns
) );
10748 SOFT_LINK_FUNCTION_EX( resolv
, dns_query
,
10756 struct sockaddr
* from
,
10757 uint32_t * fromlen
),
10758 ( dns
, name
, dnsclass
, dnstype
, buf
, len
, from
, fromlen
) );
10760 static void ResolvDNSQueryCmd( void )
10764 dns_handle_t dns
= NULL
;
10765 uint16_t type
, class;
10768 uint8_t answer
[ 1024 ];
10770 // Make sure that the required symbols are available.
10772 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_open
) )
10774 FPrintF( stderr
, "Failed to soft link dns_open from libresolv.\n" );
10775 err
= kNotFoundErr
;
10779 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_free
) )
10781 FPrintF( stderr
, "Failed to soft link dns_free from libresolv.\n" );
10782 err
= kNotFoundErr
;
10786 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_query
) )
10788 FPrintF( stderr
, "Failed to soft link dns_query from libresolv.\n" );
10789 err
= kNotFoundErr
;
10793 // Get record type.
10795 err
= RecordTypeFromArgString( gResolvDNSQuery_Type
, &type
);
10796 require_noerr( err
, exit
);
10798 // Get record class.
10800 if( gResolvDNSQuery_Class
)
10802 err
= RecordClassFromArgString( gResolvDNSQuery_Class
, &class );
10803 require_noerr( err
, exit
);
10807 class = kDNSServiceClass_IN
;
10812 dns
= soft_dns_open( gResolvDNSQuery_Path
);
10815 FPrintF( stderr
, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path
);
10822 FPrintF( stdout
, "Name: %s\n", gResolvDNSQuery_Name
);
10823 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
10824 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
10825 FPrintF( stdout
, "Path: %s\n", gResolvDNSQuery_Path
? gResolvDNSQuery_Name
: "<NULL>" );
10826 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
10827 FPrintF( stdout
, "---\n" );
10829 // Call dns_query().
10831 memset( &from
, 0, sizeof( from
) );
10832 fromLen
= (uint32_t) sizeof( from
);
10833 n
= soft_dns_query( dns
, gResolvDNSQuery_Name
, class, type
, (char *) answer
, (uint32_t) sizeof( answer
), &from
.sa
,
10837 FPrintF( stderr
, "dns_query() failed with error: %d (%s).\n", h_errno
, hstrerror( h_errno
) );
10844 FPrintF( stdout
, "From: %##a\n", &from
);
10845 FPrintF( stdout
, "Message size: %d\n\n%{du:dnsmsg}", n
, answer
, (size_t) n
);
10848 if( dns
) soft_dns_free( dns
);
10849 if( err
) exit( 1 );
10852 //===========================================================================================================================
10854 //===========================================================================================================================
10857 _CFHostResolveCallback(
10859 CFHostInfoType inInfoType
,
10860 const CFStreamError
* inError
,
10863 static void CFHostCmd( void )
10868 CFHostRef host
= NULL
;
10869 CFHostClientContext context
;
10870 CFStreamError streamErr
;
10872 name
= CFStringCreateWithCString( kCFAllocatorDefault
, gCFHost_Name
, kCFStringEncodingUTF8
);
10873 require_action( name
, exit
, err
= kUnknownErr
);
10875 host
= CFHostCreateWithName( kCFAllocatorDefault
, name
);
10877 require_action( host
, exit
, err
= kUnknownErr
);
10879 memset( &context
, 0, sizeof( context
) );
10880 success
= CFHostSetClient( host
, _CFHostResolveCallback
, &context
);
10881 require_action( success
, exit
, err
= kUnknownErr
);
10883 CFHostScheduleWithRunLoop( host
, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode
);
10887 FPrintF( stdout
, "Hostname: %s\n", gCFHost_Name
);
10888 FPrintF( stdout
, "Start time: %{du:time}\n", NULL
);
10889 FPrintF( stdout
, "---\n" );
10891 success
= CFHostStartInfoResolution( host
, kCFHostAddresses
, &streamErr
);
10892 require_action( success
, exit
, err
= kUnknownErr
);
10898 CFReleaseNullSafe( host
);
10899 if( err
) exit( 1 );
10902 static void _CFHostResolveCallback( CFHostRef inHost
, CFHostInfoType inInfoType
, const CFStreamError
*inError
, void *inInfo
)
10905 struct timeval now
;
10907 gettimeofday( &now
, NULL
);
10909 Unused( inInfoType
);
10912 if( inError
&& ( inError
->domain
!= 0 ) && ( inError
->error
) )
10914 err
= inError
->error
;
10915 if( inError
->domain
== kCFStreamErrorDomainNetDB
)
10917 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
10921 FPrintF( stderr
, "Error %#m\n", err
);
10926 CFArrayRef addresses
;
10928 CFDataRef addrData
;
10929 const struct sockaddr
* sockAddr
;
10930 Boolean wasResolved
= false;
10932 addresses
= CFHostGetAddressing( inHost
, &wasResolved
);
10933 check( wasResolved
);
10937 count
= CFArrayGetCount( addresses
);
10938 for( i
= 0; i
< count
; ++i
)
10940 addrData
= CFArrayGetCFDataAtIndex( addresses
, i
, &err
);
10941 require_noerr( err
, exit
);
10943 sockAddr
= (const struct sockaddr
*) CFDataGetBytePtr( addrData
);
10944 FPrintF( stdout
, "%##a\n", sockAddr
);
10950 FPrintF( stdout
, "---\n" );
10951 FPrintF( stdout
, "End time: %{du:time}\n", &now
);
10953 if( gCFHost_WaitSecs
> 0 ) sleep( (unsigned int) gCFHost_WaitSecs
);
10956 exit( err
? 1 : 0 );
10959 //===========================================================================================================================
10962 // Note: Based on ajn's supplemental test tool.
10963 //===========================================================================================================================
10965 static void DNSConfigAddCmd( void )
10968 CFMutableDictionaryRef dict
= NULL
;
10969 CFMutableArrayRef array
= NULL
;
10971 SCDynamicStoreRef store
= NULL
;
10972 CFStringRef key
= NULL
;
10975 if( geteuid() != 0 )
10977 FPrintF( stderr
, "error: This command must to be run as root.\n" );
10982 // Create dictionary.
10984 dict
= CFDictionaryCreateMutable( NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
10985 require_action( dict
, exit
, err
= kNoMemoryErr
);
10987 // Add DNS server IP addresses.
10989 array
= CFArrayCreateMutable( NULL
, (CFIndex
) gDNSConfigAdd_IPAddrCount
, &kCFTypeArrayCallBacks
);
10990 require_action( array
, exit
, err
= kNoMemoryErr
);
10992 for( i
= 0; i
< gDNSConfigAdd_IPAddrCount
; ++i
)
10994 CFStringRef addrStr
;
10996 addrStr
= CFStringCreateWithCString( NULL
, gDNSConfigAdd_IPAddrArray
[ i
], kCFStringEncodingUTF8
);
10997 require_action( addrStr
, exit
, err
= kUnknownErr
);
10999 CFArrayAppendValue( array
, addrStr
);
11000 CFRelease( addrStr
);
11003 CFDictionarySetValue( dict
, kSCPropNetDNSServerAddresses
, array
);
11004 ForgetCF( &array
);
11006 // Add domains, if any.
11008 array
= CFArrayCreateMutable( NULL
, (CFIndex
) Min( gDNSConfigAdd_DomainCount
, 1 ), &kCFTypeArrayCallBacks
);
11009 require_action( array
, exit
, err
= kNoMemoryErr
);
11011 if( gDNSConfigAdd_DomainCount
> 0 )
11013 for( i
= 0; i
< gDNSConfigAdd_DomainCount
; ++i
)
11015 CFStringRef domainStr
;
11017 domainStr
= CFStringCreateWithCString( NULL
, gDNSConfigAdd_DomainArray
[ i
], kCFStringEncodingUTF8
);
11018 require_action( domainStr
, exit
, err
= kUnknownErr
);
11020 CFArrayAppendValue( array
, domainStr
);
11021 CFRelease( domainStr
);
11026 // There are no domains, but the domain array needs to be non-empty, so add a zero-length string to the array.
11028 CFArrayAppendValue( array
, CFSTR( "" ) );
11031 CFDictionarySetValue( dict
, kSCPropNetDNSSupplementalMatchDomains
, array
);
11032 ForgetCF( &array
);
11034 // Add interface, if any.
11036 if( gDNSConfigAdd_Interface
)
11038 err
= CFDictionarySetCString( dict
, kSCPropInterfaceName
, gDNSConfigAdd_Interface
, kSizeCString
);
11039 require_noerr( err
, exit
);
11041 CFDictionarySetValue( dict
, kSCPropNetDNSConfirmedServiceID
, gDNSConfigAdd_ID
);
11044 // Set dictionary in dynamic store.
11046 store
= SCDynamicStoreCreate( NULL
, CFSTR( "com.apple.dnssdutil" ), NULL
, NULL
);
11047 err
= map_scerror( store
);
11048 require_noerr( err
, exit
);
11050 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
, gDNSConfigAdd_ID
, kSCEntNetDNS
);
11051 require_action( key
, exit
, err
= kUnknownErr
);
11053 success
= SCDynamicStoreSetValue( store
, key
, dict
);
11054 require_action( success
, exit
, err
= kUnknownErr
);
11057 CFReleaseNullSafe( dict
);
11058 CFReleaseNullSafe( array
);
11059 CFReleaseNullSafe( store
);
11060 CFReleaseNullSafe( key
);
11061 gExitCode
= err
? 1 : 0;
11064 //===========================================================================================================================
11065 // DNSConfigRemoveCmd
11066 //===========================================================================================================================
11068 static void DNSConfigRemoveCmd( void )
11071 SCDynamicStoreRef store
= NULL
;
11072 CFStringRef key
= NULL
;
11075 if( geteuid() != 0 )
11077 FPrintF( stderr
, "error: This command must to be run as root.\n" );
11082 store
= SCDynamicStoreCreate( NULL
, CFSTR( "com.apple.dnssdutil" ), NULL
, NULL
);
11083 err
= map_scerror( store
);
11084 require_noerr( err
, exit
);
11086 key
= SCDynamicStoreKeyCreateNetworkServiceEntity( NULL
, kSCDynamicStoreDomainState
, gDNSConfigRemove_ID
, kSCEntNetDNS
);
11087 require_action( key
, exit
, err
= kUnknownErr
);
11089 success
= SCDynamicStoreRemoveValue( store
, key
);
11090 require_action( success
, exit
, err
= kUnknownErr
);
11093 CFReleaseNullSafe( store
);
11094 CFReleaseNullSafe( key
);
11095 gExitCode
= err
? 1 : 0;
11097 #endif // TARGET_OS_DARWIN
11099 //===========================================================================================================================
11100 // DaemonVersionCmd
11101 //===========================================================================================================================
11103 static void DaemonVersionCmd( void )
11106 uint32_t size
, version
;
11109 size
= (uint32_t) sizeof( version
);
11110 err
= DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion
, &version
, &size
);
11111 require_noerr( err
, exit
);
11113 FPrintF( stdout
, "Daemon version: %s\n", SourceVersionToCString( version
, strBuf
) );
11116 if( err
) exit( 1 );
11119 //===========================================================================================================================
11121 //===========================================================================================================================
11123 static void Exit( void *inContext
)
11125 const char * const reason
= (const char *) inContext
;
11127 FPrintF( stdout
, "---\n" );
11128 FPrintF( stdout
, "End time: %{du:time}\n", NULL
);
11129 if( reason
) FPrintF( stdout
, "End reason: %s\n", reason
);
11133 //===========================================================================================================================
11134 // PrintFTimestampHandler
11135 //===========================================================================================================================
11138 PrintFTimestampHandler(
11139 PrintFContext
* inContext
,
11140 PrintFFormat
* inFormat
,
11141 PrintFVAList
* inArgs
,
11142 void * inUserContext
)
11144 struct timeval now
;
11145 const struct timeval
* tv
;
11146 struct tm
* localTime
;
11149 char dateTimeStr
[ 32 ];
11151 Unused( inUserContext
);
11153 tv
= va_arg( inArgs
->args
, const struct timeval
* );
11154 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
11158 gettimeofday( &now
, NULL
);
11161 localTime
= localtime( &tv
->tv_sec
);
11162 len
= strftime( dateTimeStr
, sizeof( dateTimeStr
), "%Y-%m-%d %H:%M:%S", localTime
);
11163 if( len
== 0 ) dateTimeStr
[ 0 ] = '\0';
11165 n
= PrintFCore( inContext
, "%s.%06u", dateTimeStr
, (unsigned int) tv
->tv_usec
);
11171 //===========================================================================================================================
11172 // PrintFDNSMessageHandler
11173 //===========================================================================================================================
11176 PrintFDNSMessageHandler(
11177 PrintFContext
* inContext
,
11178 PrintFFormat
* inFormat
,
11179 PrintFVAList
* inArgs
,
11180 void * inUserContext
)
11183 const void * msgPtr
;
11188 Boolean printRawRData
;
11190 Unused( inUserContext
);
11192 msgPtr
= va_arg( inArgs
->args
, const void * );
11193 msgLen
= va_arg( inArgs
->args
, size_t );
11194 require_action_quiet( !inFormat
->suppress
, exit
, n
= 0 );
11196 isMDNS
= ( inFormat
->altForm
> 0 ) ? true : false;
11197 if( inFormat
->precision
== 0 ) printRawRData
= false;
11198 else if( inFormat
->precision
== 1 ) printRawRData
= true;
11201 n
= PrintFCore( inContext
, "<< BAD %%{du:dnsmsg} PRECISION >>" );
11205 err
= DNSMessageToText( msgPtr
, msgLen
, isMDNS
, printRawRData
, &text
);
11208 n
= PrintFCore( inContext
, "%*{text}", inFormat
->fieldWidth
, text
, kSizeCString
);
11213 n
= PrintFCore( inContext
, "%*.1H", inFormat
->fieldWidth
, msgPtr
, (int) msgLen
, (int) msgLen
);
11220 //===========================================================================================================================
11221 // GetDNSSDFlagsFromOpts
11222 //===========================================================================================================================
11224 static DNSServiceFlags
GetDNSSDFlagsFromOpts( void )
11226 DNSServiceFlags flags
;
11228 flags
= (DNSServiceFlags
) gDNSSDFlags
;
11229 if( flags
& kDNSServiceFlagsShareConnection
)
11231 FPrintF( stderr
, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
11232 kDNSServiceFlagsShareConnection
);
11235 if( gDNSSDFlag_BrowseDomains
) flags
|= kDNSServiceFlagsBrowseDomains
;
11236 if( gDNSSDFlag_DenyCellular
) flags
|= kDNSServiceFlagsDenyCellular
;
11237 if( gDNSSDFlag_DenyExpensive
) flags
|= kDNSServiceFlagsDenyExpensive
;
11238 if( gDNSSDFlag_ForceMulticast
) flags
|= kDNSServiceFlagsForceMulticast
;
11239 if( gDNSSDFlag_IncludeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
11240 if( gDNSSDFlag_NoAutoRename
) flags
|= kDNSServiceFlagsNoAutoRename
;
11241 if( gDNSSDFlag_PathEvaluationDone
) flags
|= kDNSServiceFlagsPathEvaluationDone
;
11242 if( gDNSSDFlag_RegistrationDomains
) flags
|= kDNSServiceFlagsRegistrationDomains
;
11243 if( gDNSSDFlag_ReturnIntermediates
) flags
|= kDNSServiceFlagsReturnIntermediates
;
11244 if( gDNSSDFlag_Shared
) flags
|= kDNSServiceFlagsShared
;
11245 if( gDNSSDFlag_SuppressUnusable
) flags
|= kDNSServiceFlagsSuppressUnusable
;
11246 if( gDNSSDFlag_Timeout
) flags
|= kDNSServiceFlagsTimeout
;
11247 if( gDNSSDFlag_UnicastResponse
) flags
|= kDNSServiceFlagsUnicastResponse
;
11248 if( gDNSSDFlag_Unique
) flags
|= kDNSServiceFlagsUnique
;
11249 if( gDNSSDFlag_WakeOnResolve
) flags
|= kDNSServiceFlagsWakeOnResolve
;
11254 //===========================================================================================================================
11255 // CreateConnectionFromArgString
11256 //===========================================================================================================================
11259 CreateConnectionFromArgString(
11260 const char * inString
,
11261 dispatch_queue_t inQueue
,
11262 DNSServiceRef
* outSDRef
,
11263 ConnectionDesc
* outDesc
)
11266 DNSServiceRef sdRef
= NULL
;
11267 ConnectionType type
;
11268 int32_t pid
= -1; // Initializing because the analyzer claims pid may be used uninitialized.
11269 uint8_t uuid
[ 16 ];
11271 if( strcasecmp( inString
, kConnectionArg_Normal
) == 0 )
11273 err
= DNSServiceCreateConnection( &sdRef
);
11274 require_noerr( err
, exit
);
11275 type
= kConnectionType_Normal
;
11277 else if( stricmp_prefix( inString
, kConnectionArgPrefix_PID
) == 0 )
11279 const char * const pidStr
= inString
+ sizeof_string( kConnectionArgPrefix_PID
);
11281 err
= StringToInt32( pidStr
, &pid
);
11284 FPrintF( stderr
, "Invalid delegate connection PID value: %s\n", pidStr
);
11289 memset( uuid
, 0, sizeof( uuid
) );
11290 err
= DNSServiceCreateDelegateConnection( &sdRef
, pid
, uuid
);
11293 FPrintF( stderr
, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err
, pid
);
11296 type
= kConnectionType_DelegatePID
;
11298 else if( stricmp_prefix( inString
, kConnectionArgPrefix_UUID
) == 0 )
11300 const char * const uuidStr
= inString
+ sizeof_string( kConnectionArgPrefix_UUID
);
11302 check_compile_time_code( sizeof( uuid
) == sizeof( uuid_t
) );
11304 err
= StringToUUID( uuidStr
, kSizeCString
, false, uuid
);
11307 FPrintF( stderr
, "Invalid delegate connection UUID value: %s\n", uuidStr
);
11312 err
= DNSServiceCreateDelegateConnection( &sdRef
, 0, uuid
);
11315 FPrintF( stderr
, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err
, uuid
);
11318 type
= kConnectionType_DelegateUUID
;
11322 FPrintF( stderr
, "Unrecognized connection string \"%s\".\n", inString
);
11327 err
= DNSServiceSetDispatchQueue( sdRef
, inQueue
);
11328 require_noerr( err
, exit
);
11333 outDesc
->type
= type
;
11334 if( type
== kConnectionType_DelegatePID
) outDesc
->delegate
.pid
= pid
;
11335 else if( type
== kConnectionType_DelegateUUID
) memcpy( outDesc
->delegate
.uuid
, uuid
, 16 );
11340 if( sdRef
) DNSServiceRefDeallocate( sdRef
);
11344 //===========================================================================================================================
11345 // InterfaceIndexFromArgString
11346 //===========================================================================================================================
11348 static OSStatus
InterfaceIndexFromArgString( const char *inString
, uint32_t *outIndex
)
11355 ifIndex
= if_nametoindex( inString
);
11358 err
= StringToUInt32( inString
, &ifIndex
);
11361 FPrintF( stderr
, "Invalid interface value: %s\n", inString
);
11372 *outIndex
= ifIndex
;
11379 //===========================================================================================================================
11380 // RecordDataFromArgString
11381 //===========================================================================================================================
11383 #define kRDataMaxLen UINT16_C( 0xFFFF )
11385 static OSStatus
StringToSRVRData( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
11386 static OSStatus
StringToTXTRData( const char *inString
, char inDelimiter
, uint8_t **outPtr
, size_t *outLen
);
11388 static OSStatus
RecordDataFromArgString( const char *inString
, uint8_t **outDataPtr
, size_t *outDataLen
)
11391 uint8_t * dataPtr
= NULL
;
11398 else if( stricmp_prefix( inString
, kRDataArgPrefix_Domain
) == 0 )
11400 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_Domain
);
11402 err
= StringToDomainName( str
, &dataPtr
, &dataLen
);
11403 require_noerr_quiet( err
, exit
);
11408 else if( stricmp_prefix( inString
, kRDataArgPrefix_File
) == 0 )
11410 const char * const path
= inString
+ sizeof_string( kRDataArgPrefix_File
);
11412 err
= CopyFileDataByPath( path
, (char **) &dataPtr
, &dataLen
);
11413 require_noerr( err
, exit
);
11414 require_action( dataLen
<= kRDataMaxLen
, exit
, err
= kSizeErr
);
11417 // Hexadecimal string
11419 else if( stricmp_prefix( inString
, kRDataArgPrefix_HexString
) == 0 )
11421 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_HexString
);
11423 err
= HexToDataCopy( str
, kSizeCString
, kHexToData_DefaultFlags
, &dataPtr
, &dataLen
, NULL
);
11424 require_noerr( err
, exit
);
11425 require_action( dataLen
<= kRDataMaxLen
, exit
, err
= kSizeErr
);
11428 // IPv4 address string
11430 else if( stricmp_prefix( inString
, kRDataArgPrefix_IPv4
) == 0 )
11432 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_IPv4
);
11434 err
= StringToARecordData( str
, &dataPtr
, &dataLen
);
11435 require_noerr_quiet( err
, exit
);
11438 // IPv6 address string
11440 else if( stricmp_prefix( inString
, kRDataArgPrefix_IPv6
) == 0 )
11442 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_IPv6
);
11444 err
= StringToAAAARecordData( str
, &dataPtr
, &dataLen
);
11445 require_noerr_quiet( err
, exit
);
11450 else if( stricmp_prefix( inString
, kRDataArgPrefix_SRV
) == 0 )
11452 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_SRV
);
11454 err
= StringToSRVRData( str
, &dataPtr
, &dataLen
);
11455 require_noerr( err
, exit
);
11458 // String with escaped hex and octal bytes
11460 else if( stricmp_prefix( inString
, kRDataArgPrefix_String
) == 0 )
11462 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_String
);
11463 const char * const end
= str
+ strlen( str
);
11470 success
= ParseQuotedEscapedString( str
, end
, "", NULL
, 0, NULL
, &totalLen
, NULL
);
11471 require_action( success
, exit
, err
= kParamErr
);
11472 require_action( totalLen
<= kRDataMaxLen
, exit
, err
= kSizeErr
);
11474 dataLen
= totalLen
;
11475 dataPtr
= (uint8_t *) malloc( dataLen
);
11476 require_action( dataPtr
, exit
, err
= kNoMemoryErr
);
11478 success
= ParseQuotedEscapedString( str
, end
, "", (char *) dataPtr
, dataLen
, &copiedLen
, NULL
, NULL
);
11479 require_action( success
, exit
, err
= kParamErr
);
11480 check( copiedLen
== dataLen
);
11491 else if( stricmp_prefix( inString
, kRDataArgPrefix_TXT
) == 0 )
11493 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_TXT
);
11495 err
= StringToTXTRData( str
, ',', &dataPtr
, &dataLen
);
11496 require_noerr( err
, exit
);
11499 // Unrecognized format
11503 FPrintF( stderr
, "Unrecognized record data string \"%s\".\n", inString
);
11509 *outDataLen
= dataLen
;
11510 *outDataPtr
= dataPtr
;
11514 FreeNullSafe( dataPtr
);
11518 static OSStatus
StringToSRVRData( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
11521 DataBuffer dataBuf
;
11525 uint8_t target
[ kDomainNameLengthMax
];
11527 DataBuffer_Init( &dataBuf
, NULL
, 0, ( 3 * 2 ) + kDomainNameLengthMax
);
11529 // Parse and set the priority, weight, and port values (all three are unsigned 16-bit values).
11532 for( i
= 0; i
< 3; ++i
)
11538 value
= strtol( ptr
, &next
, 0 );
11539 require_action_quiet( ( next
!= ptr
) && ( *next
== ',' ), exit
, err
= kMalformedErr
);
11540 require_action_quiet( ( value
>= 0 ) && ( value
<= UINT16_MAX
), exit
, err
= kRangeErr
);
11543 WriteBig16( buf
, value
);
11545 err
= DataBuffer_Append( &dataBuf
, buf
, sizeof( buf
) );
11546 require_noerr( err
, exit
);
11549 // Set the target domain name.
11551 err
= DomainNameFromString( target
, ptr
, &end
);
11552 require_noerr_quiet( err
, exit
);
11554 err
= DataBuffer_Append( &dataBuf
, target
, (size_t)( end
- target
) );
11555 require_noerr( err
, exit
);
11557 err
= DataBuffer_Detach( &dataBuf
, outPtr
, outLen
);
11558 require_noerr( err
, exit
);
11561 DataBuffer_Free( &dataBuf
);
11565 static OSStatus
StringToTXTRData( const char *inString
, char inDelimiter
, uint8_t **outPtr
, size_t *outLen
)
11568 DataBuffer dataBuf
;
11570 uint8_t txtStr
[ 256 ]; // Buffer for single TXT string: 1 length byte + up to 255 bytes of data.
11572 DataBuffer_Init( &dataBuf
, NULL
, 0, kRDataMaxLen
);
11577 uint8_t * dst
= &txtStr
[ 1 ];
11578 const uint8_t * const lim
= &txtStr
[ 256 ];
11581 while( *src
&& ( *src
!= inDelimiter
) )
11583 if( ( c
= *src
++ ) == '\\' )
11585 require_action_quiet( *src
!= '\0', exit
, err
= kUnderrunErr
);
11588 require_action_quiet( dst
< lim
, exit
, err
= kOverrunErr
);
11589 *dst
++ = (uint8_t) c
;
11591 txtStr
[ 0 ] = (uint8_t)( dst
- &txtStr
[ 1 ] );
11592 err
= DataBuffer_Append( &dataBuf
, txtStr
, 1 + txtStr
[ 0 ] );
11593 require_noerr( err
, exit
);
11595 if( *src
== '\0' ) break;
11599 err
= DataBuffer_Detach( &dataBuf
, outPtr
, outLen
);
11600 require_noerr( err
, exit
);
11603 DataBuffer_Free( &dataBuf
);
11607 //===========================================================================================================================
11608 // RecordTypeFromArgString
11609 //===========================================================================================================================
11613 uint16_t value
; // Record type's numeric value.
11614 const char * name
; // Record type's name as a string (e.g., "A", "PTR", "SRV").
11618 static const RecordType kRecordTypes
[] =
11622 { kDNSServiceType_A
, "A" },
11623 { kDNSServiceType_AAAA
, "AAAA" },
11624 { kDNSServiceType_PTR
, "PTR" },
11625 { kDNSServiceType_SRV
, "SRV" },
11626 { kDNSServiceType_TXT
, "TXT" },
11627 { kDNSServiceType_CNAME
, "CNAME" },
11628 { kDNSServiceType_SOA
, "SOA" },
11629 { kDNSServiceType_NSEC
, "NSEC" },
11630 { kDNSServiceType_NS
, "NS" },
11631 { kDNSServiceType_MX
, "MX" },
11632 { kDNSServiceType_ANY
, "ANY" },
11633 { kDNSServiceType_OPT
, "OPT" },
11635 // Less common types.
11637 { kDNSServiceType_MD
, "MD" },
11638 { kDNSServiceType_NS
, "NS" },
11639 { kDNSServiceType_MD
, "MD" },
11640 { kDNSServiceType_MF
, "MF" },
11641 { kDNSServiceType_MB
, "MB" },
11642 { kDNSServiceType_MG
, "MG" },
11643 { kDNSServiceType_MR
, "MR" },
11644 { kDNSServiceType_NULL
, "NULL" },
11645 { kDNSServiceType_WKS
, "WKS" },
11646 { kDNSServiceType_HINFO
, "HINFO" },
11647 { kDNSServiceType_MINFO
, "MINFO" },
11648 { kDNSServiceType_RP
, "RP" },
11649 { kDNSServiceType_AFSDB
, "AFSDB" },
11650 { kDNSServiceType_X25
, "X25" },
11651 { kDNSServiceType_ISDN
, "ISDN" },
11652 { kDNSServiceType_RT
, "RT" },
11653 { kDNSServiceType_NSAP
, "NSAP" },
11654 { kDNSServiceType_NSAP_PTR
, "NSAP_PTR" },
11655 { kDNSServiceType_SIG
, "SIG" },
11656 { kDNSServiceType_KEY
, "KEY" },
11657 { kDNSServiceType_PX
, "PX" },
11658 { kDNSServiceType_GPOS
, "GPOS" },
11659 { kDNSServiceType_LOC
, "LOC" },
11660 { kDNSServiceType_NXT
, "NXT" },
11661 { kDNSServiceType_EID
, "EID" },
11662 { kDNSServiceType_NIMLOC
, "NIMLOC" },
11663 { kDNSServiceType_ATMA
, "ATMA" },
11664 { kDNSServiceType_NAPTR
, "NAPTR" },
11665 { kDNSServiceType_KX
, "KX" },
11666 { kDNSServiceType_CERT
, "CERT" },
11667 { kDNSServiceType_A6
, "A6" },
11668 { kDNSServiceType_DNAME
, "DNAME" },
11669 { kDNSServiceType_SINK
, "SINK" },
11670 { kDNSServiceType_APL
, "APL" },
11671 { kDNSServiceType_DS
, "DS" },
11672 { kDNSServiceType_SSHFP
, "SSHFP" },
11673 { kDNSServiceType_IPSECKEY
, "IPSECKEY" },
11674 { kDNSServiceType_RRSIG
, "RRSIG" },
11675 { kDNSServiceType_DNSKEY
, "DNSKEY" },
11676 { kDNSServiceType_DHCID
, "DHCID" },
11677 { kDNSServiceType_NSEC3
, "NSEC3" },
11678 { kDNSServiceType_NSEC3PARAM
, "NSEC3PARAM" },
11679 { kDNSServiceType_HIP
, "HIP" },
11680 { kDNSServiceType_SPF
, "SPF" },
11681 { kDNSServiceType_UINFO
, "UINFO" },
11682 { kDNSServiceType_UID
, "UID" },
11683 { kDNSServiceType_GID
, "GID" },
11684 { kDNSServiceType_UNSPEC
, "UNSPEC" },
11685 { kDNSServiceType_TKEY
, "TKEY" },
11686 { kDNSServiceType_TSIG
, "TSIG" },
11687 { kDNSServiceType_IXFR
, "IXFR" },
11688 { kDNSServiceType_AXFR
, "AXFR" },
11689 { kDNSServiceType_MAILB
, "MAILB" },
11690 { kDNSServiceType_MAILA
, "MAILA" }
11693 static OSStatus
RecordTypeFromArgString( const char *inString
, uint16_t *outValue
)
11697 const RecordType
* type
;
11698 const RecordType
* const end
= kRecordTypes
+ countof( kRecordTypes
);
11700 for( type
= kRecordTypes
; type
< end
; ++type
)
11702 if( strcasecmp( type
->name
, inString
) == 0 )
11704 *outValue
= type
->value
;
11709 err
= StringToInt32( inString
, &i32
);
11710 require_noerr_quiet( err
, exit
);
11711 require_action_quiet( ( i32
>= 0 ) && ( i32
<= UINT16_MAX
), exit
, err
= kParamErr
);
11713 *outValue
= (uint16_t) i32
;
11719 //===========================================================================================================================
11720 // RecordClassFromArgString
11721 //===========================================================================================================================
11723 static OSStatus
RecordClassFromArgString( const char *inString
, uint16_t *outValue
)
11728 if( strcasecmp( inString
, "IN" ) == 0 )
11730 *outValue
= kDNSServiceClass_IN
;
11735 err
= StringToInt32( inString
, &i32
);
11736 require_noerr_quiet( err
, exit
);
11737 require_action_quiet( ( i32
>= 0 ) && ( i32
<= UINT16_MAX
), exit
, err
= kParamErr
);
11739 *outValue
= (uint16_t) i32
;
11745 //===========================================================================================================================
11746 // InterfaceIndexToName
11747 //===========================================================================================================================
11749 static char * InterfaceIndexToName( uint32_t inIfIndex
, char inNameBuf
[ kInterfaceNameBufLen
] )
11751 switch( inIfIndex
)
11753 case kDNSServiceInterfaceIndexAny
:
11754 strlcpy( inNameBuf
, "Any", kInterfaceNameBufLen
);
11757 case kDNSServiceInterfaceIndexLocalOnly
:
11758 strlcpy( inNameBuf
, "LocalOnly", kInterfaceNameBufLen
);
11761 case kDNSServiceInterfaceIndexUnicast
:
11762 strlcpy( inNameBuf
, "Unicast", kInterfaceNameBufLen
);
11765 case kDNSServiceInterfaceIndexP2P
:
11766 strlcpy( inNameBuf
, "P2P", kInterfaceNameBufLen
);
11769 #if( defined( kDNSServiceInterfaceIndexBLE ) )
11770 case kDNSServiceInterfaceIndexBLE
:
11771 strlcpy( inNameBuf
, "BLE", kInterfaceNameBufLen
);
11779 name
= if_indextoname( inIfIndex
, inNameBuf
);
11780 if( !name
) strlcpy( inNameBuf
, "NO NAME", kInterfaceNameBufLen
);
11785 return( inNameBuf
);
11788 //===========================================================================================================================
11789 // RecordTypeToString
11790 //===========================================================================================================================
11792 static const char * RecordTypeToString( unsigned int inValue
)
11794 const RecordType
* type
;
11795 const RecordType
* const end
= kRecordTypes
+ countof( kRecordTypes
);
11797 for( type
= kRecordTypes
; type
< end
; ++type
)
11799 if( type
->value
== inValue
) return( type
->name
);
11804 //===========================================================================================================================
11805 // DNSMessageExtractDomainName
11806 //===========================================================================================================================
11808 #define IsCompressionByte( X ) ( ( ( X ) & 0xC0 ) == 0xC0 )
11811 DNSMessageExtractDomainName(
11812 const uint8_t * inMsgPtr
,
11814 const uint8_t * inNamePtr
,
11815 uint8_t inBuf
[ kDomainNameLengthMax
],
11816 const uint8_t ** outNextPtr
)
11819 const uint8_t * label
;
11821 const uint8_t * nextLabel
;
11822 const uint8_t * const msgEnd
= inMsgPtr
+ inMsgLen
;
11823 uint8_t * dst
= inBuf
;
11824 const uint8_t * const dstLim
= inBuf
? ( inBuf
+ kDomainNameLengthMax
) : NULL
;
11825 const uint8_t * nameEnd
= NULL
;
11827 require_action( ( inNamePtr
>= inMsgPtr
) && ( inNamePtr
< msgEnd
), exit
, err
= kRangeErr
);
11829 for( label
= inNamePtr
; ( labelLen
= label
[ 0 ] ) != 0; label
= nextLabel
)
11831 if( labelLen
<= kDomainLabelLengthMax
)
11833 nextLabel
= label
+ 1 + labelLen
;
11834 require_action( nextLabel
< msgEnd
, exit
, err
= kUnderrunErr
);
11837 require_action( ( dstLim
- dst
) > ( 1 + labelLen
), exit
, err
= kOverrunErr
);
11838 memcpy( dst
, label
, 1 + labelLen
);
11839 dst
+= ( 1 + labelLen
);
11842 else if( IsCompressionByte( labelLen
) )
11846 require_action( ( msgEnd
- label
) >= 2, exit
, err
= kUnderrunErr
);
11849 nameEnd
= label
+ 2;
11852 offset
= (uint16_t)( ( ( label
[ 0 ] & 0x3F ) << 8 ) | label
[ 1 ] );
11853 nextLabel
= inMsgPtr
+ offset
;
11854 require_action( nextLabel
< msgEnd
, exit
, err
= kUnderrunErr
);
11855 require_action( !IsCompressionByte( nextLabel
[ 0 ] ), exit
, err
= kMalformedErr
);
11859 dlogassert( "Unhandled label length 0x%02X\n", labelLen
);
11860 err
= kMalformedErr
;
11865 if( dst
) *dst
= 0;
11866 if( !nameEnd
) nameEnd
= label
+ 1;
11868 if( outNextPtr
) *outNextPtr
= nameEnd
;
11875 //===========================================================================================================================
11876 // DNSMessageExtractDomainNameString
11877 //===========================================================================================================================
11880 DNSMessageExtractDomainNameString(
11881 const void * inMsgPtr
,
11883 const void * inNamePtr
,
11884 char inBuf
[ kDNSServiceMaxDomainName
],
11885 const uint8_t ** outNextPtr
)
11888 const uint8_t * nextPtr
;
11889 uint8_t domainName
[ kDomainNameLengthMax
];
11891 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, inNamePtr
, domainName
, &nextPtr
);
11892 require_noerr( err
, exit
);
11894 err
= DomainNameToString( domainName
, NULL
, inBuf
, NULL
);
11895 require_noerr( err
, exit
);
11897 if( outNextPtr
) *outNextPtr
= nextPtr
;
11903 //===========================================================================================================================
11904 // DNSMessageExtractRecord
11905 //===========================================================================================================================
11910 uint8_t class[ 2 ];
11912 uint8_t rdLength
[ 2 ];
11913 uint8_t rdata
[ 1 ];
11917 check_compile_time( offsetof( DNSRecordFields
, rdata
) == 10 );
11920 DNSMessageExtractRecord(
11921 const uint8_t * inMsgPtr
,
11923 const uint8_t * inPtr
,
11924 uint8_t inNameBuf
[ kDomainNameLengthMax
],
11925 uint16_t * outType
,
11926 uint16_t * outClass
,
11928 const uint8_t ** outRDataPtr
,
11929 size_t * outRDataLen
,
11930 const uint8_t ** outPtr
)
11933 const uint8_t * const msgEnd
= inMsgPtr
+ inMsgLen
;
11934 const uint8_t * ptr
;
11935 const DNSRecordFields
* record
;
11938 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, inPtr
, inNameBuf
, &ptr
);
11939 require_noerr_quiet( err
, exit
);
11940 require_action_quiet( (size_t)( msgEnd
- ptr
) >= offsetof( DNSRecordFields
, rdata
), exit
, err
= kUnderrunErr
);
11942 record
= (DNSRecordFields
*) ptr
;
11943 rdLength
= ReadBig16( record
->rdLength
);
11944 require_action_quiet( (size_t)( msgEnd
- record
->rdata
) >= rdLength
, exit
, err
= kUnderrunErr
);
11946 if( outType
) *outType
= ReadBig16( record
->type
);
11947 if( outClass
) *outClass
= ReadBig16( record
->class );
11948 if( outTTL
) *outTTL
= ReadBig32( record
->ttl
);
11949 if( outRDataPtr
) *outRDataPtr
= record
->rdata
;
11950 if( outRDataLen
) *outRDataLen
= rdLength
;
11951 if( outPtr
) *outPtr
= record
->rdata
+ rdLength
;
11957 //===========================================================================================================================
11958 // DNSMessageGetAnswerSection
11959 //===========================================================================================================================
11961 static OSStatus
DNSMessageGetAnswerSection( const uint8_t *inMsgPtr
, size_t inMsgLen
, const uint8_t **outPtr
)
11964 const uint8_t * const msgEnd
= inMsgPtr
+ inMsgLen
;
11965 unsigned int questionCount
, i
;
11966 const DNSHeader
* hdr
;
11967 const uint8_t * ptr
;
11969 require_action_quiet( inMsgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
11971 hdr
= (DNSHeader
*) inMsgPtr
;
11972 questionCount
= DNSHeaderGetQuestionCount( hdr
);
11974 ptr
= (uint8_t *)( hdr
+ 1 );
11975 for( i
= 0; i
< questionCount
; ++i
)
11977 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, ptr
, NULL
, &ptr
);
11978 require_noerr( err
, exit
);
11979 require_action_quiet( ( msgEnd
- ptr
) >= 4, exit
, err
= kUnderrunErr
);
11983 if( outPtr
) *outPtr
= ptr
;
11990 //===========================================================================================================================
11991 // DNSRecordDataToString
11992 //===========================================================================================================================
11995 DNSRecordDataToString(
11996 const void * inRDataPtr
,
11998 unsigned int inRDataType
,
11999 const void * inMsgPtr
,
12001 char ** outString
)
12004 const uint8_t * const rdataPtr
= (uint8_t *) inRDataPtr
;
12005 const uint8_t * const rdataEnd
= rdataPtr
+ inRDataLen
;
12007 const uint8_t * ptr
;
12009 char domainNameStr
[ kDNSServiceMaxDomainName
];
12012 if( inRDataType
== kDNSServiceType_A
)
12014 require_action_quiet( inRDataLen
== 4, exit
, err
= kMalformedErr
);
12016 ASPrintF( &rdataStr
, "%.4a", rdataPtr
);
12017 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
12019 else if( inRDataType
== kDNSServiceType_AAAA
)
12021 require_action_quiet( inRDataLen
== 16, exit
, err
= kMalformedErr
);
12023 ASPrintF( &rdataStr
, "%.16a", rdataPtr
);
12024 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
12026 else if( ( inRDataType
== kDNSServiceType_PTR
) || ( inRDataType
== kDNSServiceType_CNAME
) ||
12027 ( inRDataType
== kDNSServiceType_NS
) )
12031 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, NULL
);
12032 require_noerr( err
, exit
);
12036 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, NULL
);
12037 require_noerr( err
, exit
);
12040 rdataStr
= strdup( domainNameStr
);
12041 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
12043 else if( inRDataType
== kDNSServiceType_SRV
)
12045 uint16_t priority
, weight
, port
;
12046 const uint8_t * target
;
12048 require_action_quiet( ( rdataPtr
+ 6 ) < rdataEnd
, exit
, err
= kMalformedErr
);
12050 priority
= ReadBig16( rdataPtr
);
12051 weight
= ReadBig16( rdataPtr
+ 2 );
12052 port
= ReadBig16( rdataPtr
+ 4 );
12053 target
= rdataPtr
+ 6;
12057 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, target
, domainNameStr
, NULL
);
12058 require_noerr( err
, exit
);
12062 err
= DomainNameToString( target
, rdataEnd
, domainNameStr
, NULL
);
12063 require_noerr( err
, exit
);
12066 ASPrintF( &rdataStr
, "%u %u %u %s", priority
, weight
, port
, domainNameStr
);
12067 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
12069 else if( inRDataType
== kDNSServiceType_TXT
)
12071 require_action_quiet( inRDataLen
> 0, exit
, err
= kMalformedErr
);
12073 if( inRDataLen
== 1 )
12075 ASPrintF( &rdataStr
, "%#H", rdataPtr
, (int) inRDataLen
, INT_MAX
);
12076 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
12080 ASPrintF( &rdataStr
, "%#{txt}", rdataPtr
, inRDataLen
);
12081 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
12084 else if( inRDataType
== kDNSServiceType_SOA
)
12086 uint32_t serial
, refresh
, retry
, expire
, minimum
;
12090 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
12091 require_noerr( err
, exit
);
12093 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
12095 rdataStr
= strdup( domainNameStr
);
12096 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
12098 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, ptr
, domainNameStr
, &ptr
);
12099 require_noerr( err
, exit
);
12103 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
12104 require_noerr( err
, exit
);
12106 rdataStr
= strdup( domainNameStr
);
12107 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
12109 err
= DomainNameToString( ptr
, rdataEnd
, domainNameStr
, &ptr
);
12110 require_noerr( err
, exit
);
12113 require_action_quiet( ( ptr
+ 20 ) == rdataEnd
, exit
, err
= kMalformedErr
);
12115 serial
= ReadBig32( ptr
);
12116 refresh
= ReadBig32( ptr
+ 4 );
12117 retry
= ReadBig32( ptr
+ 8 );
12118 expire
= ReadBig32( ptr
+ 12 );
12119 minimum
= ReadBig32( ptr
+ 16 );
12121 n
= AppendPrintF( &rdataStr
, " %s %u %u %u %u %u\n", domainNameStr
, serial
, refresh
, retry
, expire
, minimum
);
12122 require_action( n
> 0, exit
, err
= kUnknownErr
);
12124 else if( inRDataType
== kDNSServiceType_NSEC
)
12126 unsigned int windowBlock
, bitmapLen
, i
, recordType
;
12127 const uint8_t * bitmapPtr
;
12131 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
12132 require_noerr( err
, exit
);
12136 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
12137 require_noerr( err
, exit
);
12140 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
12142 rdataStr
= strdup( domainNameStr
);
12143 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
12145 for( ; ptr
< rdataEnd
; ptr
+= ( 2 + bitmapLen
) )
12147 require_action_quiet( ( ptr
+ 2 ) < rdataEnd
, exit
, err
= kMalformedErr
);
12149 windowBlock
= ptr
[ 0 ];
12150 bitmapLen
= ptr
[ 1 ];
12151 bitmapPtr
= &ptr
[ 2 ];
12153 require_action_quiet( ( bitmapLen
>= 1 ) && ( bitmapLen
<= 32 ) , exit
, err
= kMalformedErr
);
12154 require_action_quiet( ( bitmapPtr
+ bitmapLen
) <= rdataEnd
, exit
, err
= kMalformedErr
);
12156 for( i
= 0; i
< BitArray_MaxBits( bitmapLen
); ++i
)
12158 if( BitArray_GetBit( bitmapPtr
, bitmapLen
, i
) )
12160 recordType
= ( windowBlock
* 256 ) + i
;
12161 n
= AppendPrintF( &rdataStr
, " %s", RecordTypeToString( recordType
) );
12162 require_action( n
> 0, exit
, err
= kUnknownErr
);
12167 else if( inRDataType
== kDNSServiceType_MX
)
12169 uint16_t preference
;
12170 const uint8_t * exchange
;
12172 require_action_quiet( ( rdataPtr
+ 2 ) < rdataEnd
, exit
, err
= kMalformedErr
);
12174 preference
= ReadBig16( rdataPtr
);
12175 exchange
= &rdataPtr
[ 2 ];
12179 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, exchange
, domainNameStr
, NULL
);
12180 require_noerr( err
, exit
);
12184 err
= DomainNameToString( exchange
, rdataEnd
, domainNameStr
, NULL
);
12185 require_noerr( err
, exit
);
12188 n
= ASPrintF( &rdataStr
, "%u %s", preference
, domainNameStr
);
12189 require_action( n
> 0, exit
, err
= kUnknownErr
);
12193 err
= kNotHandledErr
;
12198 *outString
= rdataStr
;
12203 FreeNullSafe( rdataStr
);
12207 //===========================================================================================================================
12208 // DomainNameAppendString
12209 //===========================================================================================================================
12212 DomainNameAppendString(
12213 uint8_t inDomainName
[ kDomainNameLengthMax
],
12214 const char * inString
,
12215 uint8_t ** outEndPtr
)
12220 const uint8_t * const nameLim
= inDomainName
+ kDomainNameLengthMax
;
12222 for( root
= inDomainName
; ( root
< nameLim
) && *root
; root
+= ( 1 + *root
) ) {}
12223 require_action_quiet( root
< nameLim
, exit
, err
= kMalformedErr
);
12225 // If the string is a single dot, denoting the root domain, then there are no non-empty labels.
12228 if( ( src
[ 0 ] == '.' ) && ( src
[ 1 ] == '\0' ) ) ++src
;
12231 uint8_t * const label
= root
;
12232 const uint8_t * const labelLim
= Min( &label
[ 1 + kDomainLabelLengthMax
], nameLim
- 1 );
12238 while( *src
&& ( ( c
= *src
++ ) != '.' ) )
12242 require_action_quiet( *src
!= '\0', exit
, err
= kUnderrunErr
);
12244 if( isdigit_safe( c
) && isdigit_safe( src
[ 0 ] ) && isdigit_safe( src
[ 1 ] ) )
12246 const int decimal
= ( ( c
- '0' ) * 100 ) + ( ( src
[ 0 ] - '0' ) * 10 ) + ( src
[ 1 ] - '0' );
12248 if( decimal
<= 255 )
12255 require_action_quiet( dst
< labelLim
, exit
, err
= kOverrunErr
);
12256 *dst
++ = (uint8_t) c
;
12259 labelLen
= (size_t)( dst
- &label
[ 1 ] );
12260 require_action_quiet( labelLen
> 0, exit
, err
= kMalformedErr
);
12262 label
[ 0 ] = (uint8_t) labelLen
;
12267 if( outEndPtr
) *outEndPtr
= root
+ 1;
12274 //===========================================================================================================================
12276 //===========================================================================================================================
12278 static Boolean
DomainNameEqual( const uint8_t *inName1
, const uint8_t *inName2
)
12280 const uint8_t * p1
= inName1
;
12281 const uint8_t * p2
= inName2
;
12286 if( ( len
= *p1
++ ) != *p2
++ ) return( false );
12287 if( len
== 0 ) break;
12288 for( ; len
> 0; ++p1
, ++p2
, --len
)
12290 if( tolower_safe( *p1
) != tolower_safe( *p2
) ) return( false );
12296 //===========================================================================================================================
12297 // DomainNameLength
12298 //===========================================================================================================================
12300 static size_t DomainNameLength( const uint8_t * const inName
)
12302 const uint8_t * ptr
;
12304 for( ptr
= inName
; *ptr
!= 0; ptr
+= ( 1 + *ptr
) ) {}
12305 return( (size_t)( ptr
- inName
) + 1 );
12308 //===========================================================================================================================
12309 // DomainNameFromString
12310 //===========================================================================================================================
12313 DomainNameFromString(
12314 uint8_t inDomainName
[ kDomainNameLengthMax
],
12315 const char * inString
,
12316 uint8_t ** outEndPtr
)
12318 inDomainName
[ 0 ] = 0;
12319 return( DomainNameAppendString( inDomainName
, inString
, outEndPtr
) );
12322 //===========================================================================================================================
12323 // DomainNameToString
12324 //===========================================================================================================================
12327 DomainNameToString(
12328 const uint8_t * inDomainName
,
12329 const uint8_t * inEnd
,
12330 char inBuf
[ kDNSServiceMaxDomainName
],
12331 const uint8_t ** outNextPtr
)
12334 const uint8_t * label
;
12336 const uint8_t * nextLabel
;
12338 const uint8_t * src
;
12340 require_action( !inEnd
|| ( inDomainName
< inEnd
), exit
, err
= kUnderrunErr
);
12342 // Convert each label up until the root label, i.e., the zero-length label.
12345 for( label
= inDomainName
; ( labelLen
= label
[ 0 ] ) != 0; label
= nextLabel
)
12347 require_action( labelLen
<= kDomainLabelLengthMax
, exit
, err
= kMalformedErr
);
12349 nextLabel
= &label
[ 1 ] + labelLen
;
12350 require_action( ( nextLabel
- inDomainName
) < kDomainNameLengthMax
, exit
, err
= kMalformedErr
);
12351 require_action( !inEnd
|| ( nextLabel
< inEnd
), exit
, err
= kUnderrunErr
);
12353 for( src
= &label
[ 1 ]; src
< nextLabel
; ++src
)
12355 if( isprint_safe( *src
) )
12357 if( ( *src
== '.' ) || ( *src
== '\\' ) || ( *src
== ' ' ) ) *dst
++ = '\\';
12358 *dst
++ = (char) *src
;
12363 *dst
++ = '0' + ( *src
/ 100 );
12364 *dst
++ = '0' + ( ( *src
/ 10 ) % 10 );
12365 *dst
++ = '0' + ( *src
% 10 );
12371 // At this point, label points to the root label.
12372 // If the root label was the only label, then write a dot for it.
12374 if( label
== inDomainName
) *dst
++ = '.';
12376 if( outNextPtr
) *outNextPtr
= label
+ 1;
12383 //===========================================================================================================================
12384 // DNSMessageToText
12385 //===========================================================================================================================
12387 #define DNSFlagsOpCodeToString( X ) ( \
12388 ( (X) == kDNSOpCode_Query ) ? "Query" : \
12389 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \
12390 ( (X) == kDNSOpCode_Status ) ? "Status" : \
12391 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \
12392 ( (X) == kDNSOpCode_Update ) ? "Update" : \
12395 #define DNSFlagsRCodeToString( X ) ( \
12396 ( (X) == kDNSRCode_NoError ) ? "NoError" : \
12397 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \
12398 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \
12399 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \
12400 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \
12401 ( (X) == kDNSRCode_Refused ) ? "Refused" : \
12406 const uint8_t * inMsgPtr
,
12408 const Boolean inMDNS
,
12409 const Boolean inPrintRaw
,
12413 DataBuffer dataBuf
;
12415 const DNSHeader
* hdr
;
12416 const uint8_t * const msgEnd
= inMsgPtr
+ inMsgLen
;
12417 const uint8_t * ptr
;
12418 unsigned int id
, flags
, opcode
, rcode
;
12419 unsigned int questionCount
, answerCount
, authorityCount
, additionalCount
, i
, totalRRCount
;
12420 char nameStr
[ kDNSServiceMaxDomainName
];
12422 DataBuffer_Init( &dataBuf
, NULL
, 0, SIZE_MAX
);
12423 #define _Append( ... ) do { err = DataBuffer_AppendF( &dataBuf, __VA_ARGS__ ); require_noerr( err, exit ); } while( 0 )
12425 require_action_quiet( inMsgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
12427 hdr
= (DNSHeader
*) inMsgPtr
;
12428 id
= DNSHeaderGetID( hdr
);
12429 flags
= DNSHeaderGetFlags( hdr
);
12430 questionCount
= DNSHeaderGetQuestionCount( hdr
);
12431 answerCount
= DNSHeaderGetAnswerCount( hdr
);
12432 authorityCount
= DNSHeaderGetAuthorityCount( hdr
);
12433 additionalCount
= DNSHeaderGetAdditionalCount( hdr
);
12434 opcode
= DNSFlagsGetOpCode( flags
);
12435 rcode
= DNSFlagsGetRCode( flags
);
12437 _Append( "ID: 0x%04X (%u)\n", id
, id
);
12438 _Append( "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA%?s%?s %s\n",
12440 ( flags
& kDNSHeaderFlag_Response
) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode
),
12441 ( flags
& kDNSHeaderFlag_AuthAnswer
) ? ' ' : '!',
12442 ( flags
& kDNSHeaderFlag_Truncation
) ? ' ' : '!',
12443 ( flags
& kDNSHeaderFlag_RecursionDesired
) ? ' ' : '!',
12444 ( flags
& kDNSHeaderFlag_RecursionAvailable
) ? ' ' : '!',
12445 !inMDNS
, ( flags
& kDNSHeaderFlag_AuthenticData
) ? " AD" : "!AD",
12446 !inMDNS
, ( flags
& kDNSHeaderFlag_CheckingDisabled
) ? " CD" : "!CD",
12447 DNSFlagsRCodeToString( rcode
) );
12448 _Append( "Question count: %u\n", questionCount
);
12449 _Append( "Answer count: %u\n", answerCount
);
12450 _Append( "Authority count: %u\n", authorityCount
);
12451 _Append( "Additional count: %u\n", additionalCount
);
12453 ptr
= (const uint8_t *) &hdr
[ 1 ];
12454 for( i
= 0; i
< questionCount
; ++i
)
12456 unsigned int qtype
, qclass
;
12459 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, ptr
, nameStr
, &ptr
);
12460 require_noerr( err
, exit
);
12462 if( ( msgEnd
- ptr
) < 4 )
12464 err
= kUnderrunErr
;
12468 qtype
= DNSQuestionFixedFieldsGetType( (const DNSQuestionFixedFields
*) ptr
);
12469 qclass
= DNSQuestionFixedFieldsGetClass( (const DNSQuestionFixedFields
*) ptr
);
12472 isQU
= ( inMDNS
&& ( qclass
& kQClassUnicastResponseBit
) ) ? true : false;
12473 if( inMDNS
) qclass
&= ~kQClassUnicastResponseBit
;
12475 if( i
== 0 ) _Append( "\nQUESTION SECTION\n" );
12477 _Append( "%s %2s %?2s%?2u %-5s\n",
12478 nameStr
, inMDNS
? ( isQU
? "QU" : "QM" ) : "",
12479 ( qclass
== kDNSServiceClass_IN
), "IN", ( qclass
!= kDNSServiceClass_IN
), qclass
, RecordTypeToString( qtype
) );
12482 totalRRCount
= answerCount
+ authorityCount
+ additionalCount
;
12483 for( i
= 0; i
< totalRRCount
; ++i
)
12488 const uint8_t * rdataPtr
;
12491 Boolean cacheFlush
;
12492 uint8_t name
[ kDomainNameLengthMax
];
12494 err
= DNSMessageExtractRecord( inMsgPtr
, inMsgLen
, ptr
, name
, &type
, &class, &ttl
, &rdataPtr
, &rdataLen
, &ptr
);
12495 require_noerr( err
, exit
);
12497 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
12498 require_noerr( err
, exit
);
12500 cacheFlush
= ( inMDNS
&& ( class & kRRClassCacheFlushBit
) ) ? true : false;
12501 if( inMDNS
) class &= ~kRRClassCacheFlushBit
;
12504 if( !inPrintRaw
) DNSRecordDataToString( rdataPtr
, rdataLen
, type
, inMsgPtr
, inMsgLen
, &rdataStr
);
12507 ASPrintF( &rdataStr
, "%#H", rdataPtr
, (int) rdataLen
, INT_MAX
);
12508 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
12511 if( answerCount
&& ( i
== 0 ) ) _Append( "\nANSWER SECTION\n" );
12512 else if( authorityCount
&& ( i
== answerCount
) ) _Append( "\nAUTHORITY SECTION\n" );
12513 else if( additionalCount
&& ( i
== ( answerCount
+ authorityCount
) ) ) _Append( "\nADDITIONAL SECTION\n" );
12515 _Append( "%-42s %6u %2s %?2s%?2u %-5s %s\n",
12516 nameStr
, ttl
, cacheFlush
? "CF" : "",
12517 ( class == kDNSServiceClass_IN
), "IN", ( class != kDNSServiceClass_IN
), class,
12518 RecordTypeToString( type
), rdataStr
);
12523 err
= DataBuffer_Append( &dataBuf
, "", 1 );
12524 require_noerr( err
, exit
);
12526 err
= DataBuffer_Detach( &dataBuf
, (uint8_t **) outText
, &len
);
12527 require_noerr( err
, exit
);
12530 DataBuffer_Free( &dataBuf
);
12534 //===========================================================================================================================
12535 // WriteDNSQueryMessage
12536 //===========================================================================================================================
12539 WriteDNSQueryMessage(
12540 uint8_t inMsg
[ kDNSQueryMessageMaxLen
],
12543 const char * inQName
,
12546 size_t * outMsgLen
)
12549 DNSHeader
* const hdr
= (DNSHeader
*) inMsg
;
12553 memset( hdr
, 0, sizeof( *hdr
) );
12554 DNSHeaderSetID( hdr
, inMsgID
);
12555 DNSHeaderSetFlags( hdr
, inFlags
);
12556 DNSHeaderSetQuestionCount( hdr
, 1 );
12558 ptr
= (uint8_t *)( hdr
+ 1 );
12559 err
= DomainNameFromString( ptr
, inQName
, &ptr
);
12560 require_noerr_quiet( err
, exit
);
12562 DNSQuestionFixedFieldsInit( (DNSQuestionFixedFields
*) ptr
, inQType
, inQClass
);
12565 msgLen
= (size_t)( ptr
- inMsg
);
12566 check( msgLen
<= kDNSQueryMessageMaxLen
);
12568 if( outMsgLen
) *outMsgLen
= msgLen
;
12574 //===========================================================================================================================
12575 // DispatchSignalSourceCreate
12576 //===========================================================================================================================
12579 DispatchSignalSourceCreate(
12581 DispatchHandler inEventHandler
,
12583 dispatch_source_t
* outSource
)
12586 dispatch_source_t source
;
12588 source
= dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL
, (uintptr_t) inSignal
, 0, dispatch_get_main_queue() );
12589 require_action( source
, exit
, err
= kUnknownErr
);
12591 dispatch_set_context( source
, inContext
);
12592 dispatch_source_set_event_handler_f( source
, inEventHandler
);
12594 *outSource
= source
;
12601 //===========================================================================================================================
12602 // DispatchSocketSourceCreate
12603 //===========================================================================================================================
12606 DispatchSocketSourceCreate(
12608 dispatch_source_type_t inType
,
12609 dispatch_queue_t inQueue
,
12610 DispatchHandler inEventHandler
,
12611 DispatchHandler inCancelHandler
,
12613 dispatch_source_t
* outSource
)
12616 dispatch_source_t source
;
12618 source
= dispatch_source_create( inType
, (uintptr_t) inSock
, 0, inQueue
? inQueue
: dispatch_get_main_queue() );
12619 require_action( source
, exit
, err
= kUnknownErr
);
12621 dispatch_set_context( source
, inContext
);
12622 dispatch_source_set_event_handler_f( source
, inEventHandler
);
12623 dispatch_source_set_cancel_handler_f( source
, inCancelHandler
);
12625 *outSource
= source
;
12632 //===========================================================================================================================
12633 // DispatchTimerCreate
12634 //===========================================================================================================================
12637 DispatchTimerCreate(
12638 dispatch_time_t inStart
,
12639 uint64_t inIntervalNs
,
12640 uint64_t inLeewayNs
,
12641 dispatch_queue_t inQueue
,
12642 DispatchHandler inEventHandler
,
12643 DispatchHandler inCancelHandler
,
12645 dispatch_source_t
* outTimer
)
12648 dispatch_source_t timer
;
12650 timer
= dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, inQueue
? inQueue
: dispatch_get_main_queue() );
12651 require_action( timer
, exit
, err
= kUnknownErr
);
12653 dispatch_source_set_timer( timer
, inStart
, inIntervalNs
, inLeewayNs
);
12654 dispatch_set_context( timer
, inContext
);
12655 dispatch_source_set_event_handler_f( timer
, inEventHandler
);
12656 dispatch_source_set_cancel_handler_f( timer
, inCancelHandler
);
12665 //===========================================================================================================================
12666 // DispatchProcessMonitorCreate
12667 //===========================================================================================================================
12670 DispatchProcessMonitorCreate(
12672 unsigned long inFlags
,
12673 dispatch_queue_t inQueue
,
12674 DispatchHandler inEventHandler
,
12675 DispatchHandler inCancelHandler
,
12677 dispatch_source_t
* outMonitor
)
12680 dispatch_source_t monitor
;
12682 monitor
= dispatch_source_create( DISPATCH_SOURCE_TYPE_PROC
, (uintptr_t) inPID
, inFlags
,
12683 inQueue
? inQueue
: dispatch_get_main_queue() );
12684 require_action( monitor
, exit
, err
= kUnknownErr
);
12686 dispatch_set_context( monitor
, inContext
);
12687 dispatch_source_set_event_handler_f( monitor
, inEventHandler
);
12688 dispatch_source_set_cancel_handler_f( monitor
, inCancelHandler
);
12690 *outMonitor
= monitor
;
12697 //===========================================================================================================================
12698 // ServiceTypeDescription
12699 //===========================================================================================================================
12703 const char * name
; // Name of the service type in two-label "_service._proto" format.
12704 const char * description
; // Description of the service type.
12708 // A Non-comprehensive table of DNS-SD service types
12710 static const ServiceType kServiceTypes
[] =
12712 { "_acp-sync._tcp", "AirPort Base Station Sync" },
12713 { "_adisk._tcp", "Automatic Disk Discovery" },
12714 { "_afpovertcp._tcp", "Apple File Sharing" },
12715 { "_airdrop._tcp", "AirDrop" },
12716 { "_airplay._tcp", "AirPlay" },
12717 { "_airport._tcp", "AirPort Base Station" },
12718 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
12719 { "_eppc._tcp", "Remote AppleEvents" },
12720 { "_ftp._tcp", "File Transfer Protocol" },
12721 { "_home-sharing._tcp", "Home Sharing" },
12722 { "_homekit._tcp", "HomeKit" },
12723 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
12724 { "_https._tcp", "HTTP over SSL/TLS" },
12725 { "_ipp._tcp", "Internet Printing Protocol" },
12726 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
12727 { "_mediaremotetv._tcp", "Media Remote" },
12728 { "_net-assistant._tcp", "Apple Remote Desktop" },
12729 { "_od-master._tcp", "OpenDirectory Master" },
12730 { "_nfs._tcp", "Network File System" },
12731 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
12732 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
12733 { "_raop._tcp", "Remote Audio Output Protocol" },
12734 { "_rfb._tcp", "Remote Frame Buffer" },
12735 { "_scanner._tcp", "Bonjour Scanning" },
12736 { "_smb._tcp", "Server Message Block over TCP/IP" },
12737 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
12738 { "_sleep-proxy._udp", "Sleep Proxy Server" },
12739 { "_ssh._tcp", "SSH Remote Login Protocol" },
12740 { "_teleport._tcp", "teleport" },
12741 { "_tftp._tcp", "Trivial File Transfer Protocol" },
12742 { "_workstation._tcp", "Workgroup Manager" },
12743 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
12744 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
12747 static const char * ServiceTypeDescription( const char *inName
)
12749 const ServiceType
* serviceType
;
12750 const ServiceType
* const end
= kServiceTypes
+ countof( kServiceTypes
);
12752 for( serviceType
= kServiceTypes
; serviceType
< end
; ++serviceType
)
12754 if( strcasecmp( inName
, serviceType
->name
) == 0 ) return( serviceType
->description
);
12759 //===========================================================================================================================
12760 // SocketContextCreate
12761 //===========================================================================================================================
12763 static OSStatus
SocketContextCreate( SocketRef inSock
, void * inUserContext
, SocketContext
**outContext
)
12766 SocketContext
* context
;
12768 context
= (SocketContext
*) calloc( 1, sizeof( *context
) );
12769 require_action( context
, exit
, err
= kNoMemoryErr
);
12771 context
->refCount
= 1;
12772 context
->sock
= inSock
;
12773 context
->userContext
= inUserContext
;
12775 *outContext
= context
;
12782 //===========================================================================================================================
12783 // SocketContextRetain
12784 //===========================================================================================================================
12786 static SocketContext
* SocketContextRetain( SocketContext
*inContext
)
12788 ++inContext
->refCount
;
12789 return( inContext
);
12792 //===========================================================================================================================
12793 // SocketContextRelease
12794 //===========================================================================================================================
12796 static void SocketContextRelease( SocketContext
*inContext
)
12798 if( --inContext
->refCount
== 0 )
12800 ForgetSocket( &inContext
->sock
);
12805 //===========================================================================================================================
12806 // SocketContextCancelHandler
12807 //===========================================================================================================================
12809 static void SocketContextCancelHandler( void *inContext
)
12811 SocketContextRelease( (SocketContext
*) inContext
);
12814 //===========================================================================================================================
12816 //===========================================================================================================================
12818 static OSStatus
StringToInt32( const char *inString
, int32_t *outValue
)
12824 value
= strtol( inString
, &endPtr
, 0 );
12825 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
12826 require_action_quiet( ( value
>= INT32_MIN
) && ( value
<= INT32_MAX
), exit
, err
= kRangeErr
);
12828 *outValue
= (int32_t) value
;
12835 //===========================================================================================================================
12837 //===========================================================================================================================
12839 static OSStatus
StringToUInt32( const char *inString
, uint32_t *outValue
)
12845 value
= (uint32_t) strtol( inString
, &endPtr
, 0 );
12846 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
12855 //===========================================================================================================================
12856 // StringToLongLong
12857 //===========================================================================================================================
12859 static OSStatus
StringToLongLong( const char *inString
, long long *outValue
)
12865 set_errno_compat( 0 );
12866 value
= strtol( inString
, &endPtr
, 0 );
12867 err
= errno_compat();
12868 if( ( ( value
== LLONG_MIN
) || ( value
== LLONG_MAX
) ) && ( err
== ERANGE
) ) goto exit
;
12869 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
12878 //===========================================================================================================================
12879 // StringToARecordData
12880 //===========================================================================================================================
12882 static OSStatus
StringToARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
12885 uint32_t * addrPtr
;
12886 const size_t addrLen
= sizeof( *addrPtr
);
12889 addrPtr
= (uint32_t *) malloc( addrLen
);
12890 require_action( addrPtr
, exit
, err
= kNoMemoryErr
);
12892 err
= StringToIPv4Address( inString
, kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
, addrPtr
,
12893 NULL
, NULL
, NULL
, &end
);
12894 if( !err
&& ( *end
!= '\0' ) ) err
= kMalformedErr
;
12895 require_noerr_quiet( err
, exit
);
12897 *addrPtr
= HostToBig32( *addrPtr
);
12899 *outPtr
= (uint8_t *) addrPtr
;
12904 FreeNullSafe( addrPtr
);
12908 //===========================================================================================================================
12909 // StringToAAAARecordData
12910 //===========================================================================================================================
12912 static OSStatus
StringToAAAARecordData( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
12916 const size_t addrLen
= 16;
12919 addrPtr
= (uint8_t *) malloc( addrLen
);
12920 require_action( addrPtr
, exit
, err
= kNoMemoryErr
);
12922 err
= StringToIPv6Address( inString
,
12923 kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
| kStringToIPAddressFlagsNoScope
,
12924 addrPtr
, NULL
, NULL
, NULL
, &end
);
12925 if( !err
&& ( *end
!= '\0' ) ) err
= kMalformedErr
;
12926 require_noerr_quiet( err
, exit
);
12933 FreeNullSafe( addrPtr
);
12937 //===========================================================================================================================
12938 // StringToDomainName
12939 //===========================================================================================================================
12941 static OSStatus
StringToDomainName( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
12947 uint8_t nameBuf
[ kDomainNameLengthMax
];
12949 err
= DomainNameFromString( nameBuf
, inString
, &end
);
12950 require_noerr_quiet( err
, exit
);
12952 nameLen
= (size_t)( end
- nameBuf
);
12953 namePtr
= memdup( nameBuf
, nameLen
);
12954 require_action( namePtr
, exit
, err
= kNoMemoryErr
);
12958 if( outLen
) *outLen
= nameLen
;
12964 #if( TARGET_OS_DARWIN )
12965 //===========================================================================================================================
12966 // GetDefaultDNSServer
12967 //===========================================================================================================================
12969 static OSStatus
GetDefaultDNSServer( sockaddr_ip
*outAddr
)
12972 dns_config_t
* config
;
12973 struct sockaddr
* addr
;
12976 config
= dns_configuration_copy();
12977 require_action( config
, exit
, err
= kUnknownErr
);
12980 for( i
= 0; i
< config
->n_resolver
; ++i
)
12982 const dns_resolver_t
* const resolver
= config
->resolver
[ i
];
12984 if( !resolver
->domain
&& ( resolver
->n_nameserver
> 0 ) )
12986 addr
= resolver
->nameserver
[ 0 ];
12990 require_action_quiet( addr
, exit
, err
= kNotFoundErr
);
12992 SockAddrCopy( addr
, outAddr
);
12996 if( config
) dns_configuration_free( config
);
13001 //===========================================================================================================================
13002 // GetCurrentMicroTime
13003 //===========================================================================================================================
13005 static MicroTime64
GetCurrentMicroTime( void )
13007 struct timeval now
;
13009 TIMEVAL_ZERO( now
);
13010 gettimeofday( &now
, NULL
);
13012 return( (MicroTime64
) TIMEVAL_USEC64( now
) );
13015 //===========================================================================================================================
13018 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
13019 //===========================================================================================================================
13021 OSStatus
SocketWriteAll( SocketRef inSock
, const void *inData
, size_t inSize
, int32_t inTimeoutSecs
)
13024 const uint8_t * src
;
13025 const uint8_t * end
;
13027 struct timeval timeout
;
13030 FD_ZERO( &writeSet
);
13031 src
= (const uint8_t *) inData
;
13032 end
= src
+ inSize
;
13035 FD_SET( inSock
, &writeSet
);
13036 timeout
.tv_sec
= inTimeoutSecs
;
13037 timeout
.tv_usec
= 0;
13038 n
= select( (int)( inSock
+ 1 ), NULL
, &writeSet
, NULL
, &timeout
);
13039 if( n
== 0 ) { err
= kTimeoutErr
; goto exit
; }
13040 err
= map_socket_value_errno( inSock
, n
> 0, n
);
13041 require_noerr( err
, exit
);
13043 n
= send( inSock
, (char *) src
, (size_t)( end
- src
), 0 );
13044 err
= map_socket_value_errno( inSock
, n
>= 0, n
);
13045 if( err
== EINTR
) continue;
13046 require_noerr( err
, exit
);
13056 //===========================================================================================================================
13057 // ParseIPv4Address
13059 // Warning: "inBuffer" may be modified even in error cases.
13061 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
13062 //===========================================================================================================================
13064 static OSStatus
ParseIPv4Address( const char *inStr
, uint8_t inBuffer
[ 4 ], const char **outStr
)
13080 for( ; ( c
= *inStr
) != '\0'; ++inStr
)
13082 if( isdigit_safe( c
) )
13084 v
= ( *dst
* 10 ) + ( c
- '0' );
13085 require_action_quiet( v
<= 255, exit
, err
= kRangeErr
);
13086 *dst
= (uint8_t) v
;
13090 require_action_quiet( segments
<= 4, exit
, err
= kOverrunErr
);
13094 else if( ( c
== '.' ) && sawDigit
)
13096 require_action_quiet( segments
< 4, exit
, err
= kMalformedErr
);
13105 require_action_quiet( segments
== 4, exit
, err
= kUnderrunErr
);
13114 //===========================================================================================================================
13115 // StringToIPv4Address
13117 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
13118 //===========================================================================================================================
13121 StringToIPv4Address(
13122 const char * inStr
,
13123 StringToIPAddressFlags inFlags
,
13126 uint32_t * outSubnet
,
13127 uint32_t * outRouter
,
13128 const char ** outStr
)
13138 uint32_t subnetMask
;
13141 require_action( inStr
, exit
, err
= kParamErr
);
13143 // Parse the address-only part of the address (e.g. "1.2.3.4").
13145 err
= ParseIPv4Address( inStr
, buf
, &inStr
);
13146 require_noerr_quiet( err
, exit
);
13147 ip
= (uint32_t)( ( buf
[ 0 ] << 24 ) | ( buf
[ 1 ] << 16 ) | ( buf
[ 2 ] << 8 ) | buf
[ 3 ] );
13150 // Parse the port (if any).
13156 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPort
), exit
, err
= kUnexpectedErr
);
13157 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) port
= ( port
* 10 ) + ( c
- '0' );
13158 require_action_quiet( port
<= 65535, exit
, err
= kRangeErr
);
13162 // Parse the prefix length (if any).
13170 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPrefix
), exit
, err
= kUnexpectedErr
);
13171 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) prefix
= ( prefix
* 10 ) + ( c
- '0' );
13172 require_action_quiet( ( prefix
>= 0 ) && ( prefix
<= 32 ), exit
, err
= kRangeErr
);
13175 subnetMask
= ( prefix
> 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix
) ) : 0;
13176 router
= ( ip
& subnetMask
) | 1;
13179 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
13181 if( outIP
) *outIP
= ip
;
13182 if( outPort
&& hasPort
) *outPort
= port
;
13183 if( outSubnet
&& hasPrefix
) *outSubnet
= subnetMask
;
13184 if( outRouter
&& hasPrefix
) *outRouter
= router
;
13185 if( outStr
) *outStr
= inStr
;
13192 //===========================================================================================================================
13193 // ParseIPv6Address
13195 // Note: Parsed according to the rules specified in RFC 3513.
13196 // Warning: "inBuffer" may be modified even in error cases.
13198 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
13199 //===========================================================================================================================
13201 static OSStatus
ParseIPv6Address( const char *inStr
, int inAllowV4Mapped
, uint8_t inBuffer
[ 16 ], const char **outStr
)
13203 // Table to map uppercase hex characters - '0' to their numeric values.
13204 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
13205 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 };
13210 uint8_t * colonPtr
;
13217 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
13219 for( i
= 0; i
< 16; ++i
) inBuffer
[ i
] = 0;
13221 // Special case leading :: (e.g. "::1") to simplify processing later.
13223 if( *inStr
== ':' )
13226 require_action_quiet( *inStr
== ':', exit
, err
= kMalformedErr
);
13229 // Parse the address.
13237 while( ( ( c
= *inStr
++ ) != '\0' ) && ( c
!= '%' ) && ( c
!= '/' ) && ( c
!= ']' ) )
13239 if( ( c
>= 'a' ) && ( c
<= 'f' ) ) c
-= ( 'a' - 'A' );
13240 if( ( ( c
>= '0' ) && ( c
<= '9' ) ) || ( ( c
>= 'A' ) && ( c
<= 'F' ) ) )
13243 check( c
< (int) countof( kASCIItoHexTable
) );
13244 v
= ( v
<< 4 ) | kASCIItoHexTable
[ c
];
13245 require_action_quiet( v
<= 0xFFFF, exit
, err
= kRangeErr
);
13254 require_action_quiet( !colonPtr
, exit
, err
= kMalformedErr
);
13258 require_action_quiet( *inStr
!= '\0', exit
, err
= kUnderrunErr
);
13259 require_action_quiet( ( dst
+ 2 ) <= lim
, exit
, err
= kOverrunErr
);
13260 *dst
++ = (uint8_t)( ( v
>> 8 ) & 0xFF );
13261 *dst
++ = (uint8_t)( v
& 0xFF );
13267 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
13269 if( inAllowV4Mapped
&& ( c
== '.' ) && ( ( dst
+ 4 ) <= lim
) )
13271 err
= ParseIPv4Address( ptr
, dst
, &inStr
);
13272 require_noerr_quiet( err
, exit
);
13275 ++inStr
; // Increment because the code below expects the end to be at "inStr - 1".
13281 require_action_quiet( ( dst
+ 2 ) <= lim
, exit
, err
= kOverrunErr
);
13282 *dst
++ = (uint8_t)( ( v
>> 8 ) & 0xFF );
13283 *dst
++ = (uint8_t)( v
& 0xFF );
13285 check( dst
<= lim
);
13288 require_action_quiet( dst
< lim
, exit
, err
= kOverrunErr
);
13289 n
= (int)( dst
- colonPtr
);
13290 for( i
= 1; i
<= n
; ++i
)
13292 lim
[ -i
] = colonPtr
[ n
- i
];
13293 colonPtr
[ n
- i
] = 0;
13297 require_action_quiet( dst
== lim
, exit
, err
= kUnderrunErr
);
13299 *outStr
= inStr
- 1;
13306 //===========================================================================================================================
13309 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
13310 //===========================================================================================================================
13312 static OSStatus
ParseIPv6Scope( const char *inStr
, uint32_t *outScope
, const char **outStr
)
13314 #if( TARGET_OS_POSIX )
13316 char scopeStr
[ 64 ];
13323 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
13326 lim
= dst
+ ( countof( scopeStr
) - 1 );
13327 while( ( ( c
= *inStr
) != '\0' ) && ( c
!= ':' ) && ( c
!= '/' ) && ( c
!= ']' ) && ( dst
< lim
) )
13332 check( dst
<= lim
);
13334 // First try to map as a name and if that fails, treat it as a numeric scope.
13336 scope
= if_nametoindex( scopeStr
);
13339 for( ptr
= scopeStr
; ( ( c
= *ptr
) >= '0' ) && ( c
<= '9' ); ++ptr
)
13341 scope
= ( scope
* 10 ) + ( ( (uint8_t) c
) - '0' );
13343 require_action_quiet( c
== '\0', exit
, err
= kMalformedErr
);
13344 require_action_quiet( ( ptr
!= scopeStr
) && ( ( (int)( ptr
- scopeStr
) ) <= 10 ), exit
, err
= kMalformedErr
);
13356 const char * start
;
13360 for( start
= inStr
; ( ( c
= *inStr
) >= '0' ) && ( c
<= '9' ); ++inStr
)
13362 scope
= ( scope
* 10 ) + ( c
- '0' );
13364 require_action_quiet( ( inStr
!= start
) && ( ( (int)( inStr
- start
) ) <= 10 ), exit
, err
= kMalformedErr
);
13375 //===========================================================================================================================
13376 // StringToIPv6Address
13378 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
13379 //===========================================================================================================================
13382 StringToIPv6Address(
13383 const char * inStr
,
13384 StringToIPAddressFlags inFlags
,
13385 uint8_t outIPv6
[ 16 ],
13386 uint32_t * outScope
,
13389 const char ** outStr
)
13392 uint8_t ipv6
[ 16 ];
13403 require_action( inStr
, exit
, err
= kParamErr
);
13405 if( *inStr
== '[' ) ++inStr
; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
13407 // Parse the address-only part of the address (e.g. "1::1").
13409 err
= ParseIPv6Address( inStr
, !( inFlags
& kStringToIPAddressFlagsNoIPv4Mapped
), ipv6
, &inStr
);
13410 require_noerr_quiet( err
, exit
);
13413 // Parse the scope, port, or prefix length.
13424 if( c
== '%' ) // Scope (e.g. "%en0" or "%5")
13426 require_action_quiet( !hasScope
, exit
, err
= kMalformedErr
);
13427 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoScope
), exit
, err
= kUnexpectedErr
);
13429 err
= ParseIPv6Scope( inStr
, &scope
, &inStr
);
13430 require_noerr_quiet( err
, exit
);
13434 else if( c
== ':' ) // Port (e.g. ":80")
13436 require_action_quiet( !hasPort
, exit
, err
= kMalformedErr
);
13437 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPort
), exit
, err
= kUnexpectedErr
);
13438 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) port
= ( port
* 10 ) + ( c
- '0' );
13439 require_action_quiet( port
<= 65535, exit
, err
= kRangeErr
);
13442 else if( c
== '/' ) // Prefix Length (e.g. "/64")
13444 require_action_quiet( !hasPrefix
, exit
, err
= kMalformedErr
);
13445 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPrefix
), exit
, err
= kUnexpectedErr
);
13446 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) prefix
= ( prefix
* 10 ) + ( c
- '0' );
13447 require_action_quiet( ( prefix
>= 0 ) && ( prefix
<= 128 ), exit
, err
= kRangeErr
);
13450 else if( c
== ']' )
13452 require_action_quiet( !hasBracket
, exit
, err
= kMalformedErr
);
13462 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
13464 if( outIPv6
) for( i
= 0; i
< 16; ++i
) outIPv6
[ i
] = ipv6
[ i
];
13465 if( outScope
&& hasScope
) *outScope
= scope
;
13466 if( outPort
&& hasPort
) *outPort
= port
;
13467 if( outPrefix
&& hasPrefix
) *outPrefix
= prefix
;
13468 if( outStr
) *outStr
= inStr
;
13475 //===========================================================================================================================
13476 // StringArray_Free
13478 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
13479 //===========================================================================================================================
13481 void StringArray_Free( char **inArray
, size_t inCount
)
13485 for( i
= 0; i
< inCount
; ++i
)
13487 free( inArray
[ i
] );
13489 if( inCount
> 0 ) free( inArray
);
13492 //===========================================================================================================================
13493 // ParseQuotedEscapedString
13495 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
13496 //===========================================================================================================================
13499 ParseQuotedEscapedString(
13500 const char * inSrc
,
13501 const char * inEnd
,
13502 const char * inDelimiters
,
13505 size_t * outCopiedLen
,
13506 size_t * outTotalLen
,
13507 const char ** outSrc
)
13509 const unsigned char * src
;
13510 const unsigned char * end
;
13511 unsigned char * dst
;
13512 unsigned char * lim
;
13516 Boolean singleQuote
;
13517 Boolean doubleQuote
;
13519 if( inEnd
== NULL
) inEnd
= inSrc
+ strlen( inSrc
);
13520 src
= (const unsigned char *) inSrc
;
13521 end
= (const unsigned char *) inEnd
;
13522 dst
= (unsigned char *) inBuf
;
13523 lim
= dst
+ inMaxLen
;
13524 while( ( src
< end
) && isspace_safe( *src
) ) ++src
; // Skip leading spaces.
13525 if( src
>= end
) return( false );
13527 // Parse each argument from the string.
13529 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
13532 singleQuote
= false;
13533 doubleQuote
= false;
13539 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
13543 singleQuote
= false;
13547 else if( doubleQuote
)
13549 // Double quotes protect everything except double quotes and backslashes. A backslash can be
13550 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
13551 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
13552 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
13553 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
13557 doubleQuote
= false;
13560 else if( c
== '\\' )
13565 if( ( c2
== '"' ) || ( c2
== '\\' ) )
13570 else if( c2
== '\n' )
13575 else if( ( c2
== 'x' ) || ( c2
== 'X' ) )
13579 if( ( ( end
- src
) >= 2 ) && IsHexPair( src
) )
13581 c
= HexPairToByte( src
);
13585 else if( isoctal_safe( c2
) )
13587 if( ( ( end
- src
) >= 3 ) && IsOctalTriple( src
) )
13589 c
= OctalTripleToByte( src
);
13596 else if( strchr( inDelimiters
, c
) )
13600 else if( c
== '\\' )
13602 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
13603 // A backslash followed by a newline disappears completely.
13604 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
13605 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
13615 else if( ( c
== 'x' ) || ( c
== 'X' ) )
13618 if( ( ( end
- src
) >= 2 ) && IsHexPair( src
) )
13620 c
= HexPairToByte( src
);
13624 else if( isoctal_safe( c
) )
13626 if( ( ( end
- src
) >= 3 ) && IsOctalTriple( src
) )
13628 c
= OctalTripleToByte( src
);
13642 else if( c
== '\'' )
13644 singleQuote
= true;
13647 else if( c
== '"' )
13649 doubleQuote
= true;
13655 if( inBuf
) *dst
= c
;
13661 if( outCopiedLen
) *outCopiedLen
= (size_t)( dst
- ( (unsigned char *) inBuf
) );
13662 if( outTotalLen
) *outTotalLen
= totalLen
;
13663 if( outSrc
) *outSrc
= (const char *) src
;
13667 //===========================================================================================================================
13668 // _ServerSocketOpenEx2
13670 // Note: Based on ServerSocketOpenEx() from CoreUtils. Added parameter to not use SO_REUSEPORT.
13671 //===========================================================================================================================
13674 _ServerSocketOpenEx2(
13678 const void * inAddr
,
13682 Boolean inNoPortReuse
,
13683 SocketRef
* outSock
)
13693 port
= ( inPort
< 0 ) ? -inPort
: inPort
; // Negated port number means "try this port, but allow dynamic".
13695 sock
= socket( inFamily
, inType
, inProtocol
);
13696 err
= map_socket_creation_errno( sock
);
13697 require_noerr_quiet( err
, exit
);
13699 #if( defined( SO_NOSIGPIPE ) )
13700 setsockopt( sock
, SOL_SOCKET
, SO_NOSIGPIPE
, &(int){ 1 }, (socklen_t
) sizeof( int ) );
13703 err
= SocketMakeNonBlocking( sock
);
13704 require_noerr( err
, exit
);
13706 // Set receive buffer size. This has to be done on the listening socket *before* listen is called because
13707 // accept does not return until after the window scale option is exchanged during the 3-way handshake.
13708 // Since accept returns a new socket, the only way to use a larger window scale option is to set the buffer
13709 // size on the listening socket since SO_RCVBUF is inherited by the accepted socket. See UNPv1e3 Section 7.5.
13711 err
= SocketSetBufferSize( sock
, SO_RCVBUF
, inRcvBufSize
);
13712 check_noerr( err
);
13714 // Allow port or address reuse because we may bind separate IPv4 and IPv6 sockets to the same port.
13716 if( ( inType
!= SOCK_DGRAM
) || !inNoPortReuse
)
13719 name
= ( inType
== SOCK_DGRAM
) ? SO_REUSEPORT
: SO_REUSEADDR
;
13720 err
= setsockopt( sock
, SOL_SOCKET
, name
, (char *) &option
, (socklen_t
) sizeof( option
) );
13721 err
= map_socket_noerr_errno( sock
, err
);
13722 require_noerr( err
, exit
);
13725 if( inFamily
== AF_INET
)
13727 // Bind to the port. If it fails, retry with a dynamic port.
13729 memset( &sip
.v4
, 0, sizeof( sip
.v4
) );
13730 SIN_LEN_SET( &sip
.v4
);
13731 sip
.v4
.sin_family
= AF_INET
;
13732 sip
.v4
.sin_port
= htons( (uint16_t) port
);
13733 sip
.v4
.sin_addr
.s_addr
= inAddr
? *( (const uint32_t *) inAddr
) : htonl( INADDR_ANY
);
13734 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v4
) );
13735 err
= map_socket_noerr_errno( sock
, err
);
13736 if( err
&& ( inPort
< 0 ) )
13738 sip
.v4
.sin_port
= 0;
13739 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v4
) );
13740 err
= map_socket_noerr_errno( sock
, err
);
13742 require_noerr( err
, exit
);
13744 #if( defined( AF_INET6 ) )
13745 else if( inFamily
== AF_INET6
)
13747 // Restrict this socket to IPv6 only because we're going to use a separate socket for IPv4.
13750 err
= setsockopt( sock
, IPPROTO_IPV6
, IPV6_V6ONLY
, (char *) &option
, (socklen_t
) sizeof( option
) );
13751 err
= map_socket_noerr_errno( sock
, err
);
13752 require_noerr( err
, exit
);
13754 // Bind to the port. If it fails, retry with a dynamic port.
13756 memset( &sip
.v6
, 0, sizeof( sip
.v6
) );
13757 SIN6_LEN_SET( &sip
.v6
);
13758 sip
.v6
.sin6_family
= AF_INET6
;
13759 sip
.v6
.sin6_port
= htons( (uint16_t) port
);
13760 sip
.v6
.sin6_addr
= inAddr
? *( (const struct in6_addr
*) inAddr
) : in6addr_any
;
13761 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v6
) );
13762 err
= map_socket_noerr_errno( sock
, err
);
13763 if( err
&& ( inPort
< 0 ) )
13765 sip
.v6
.sin6_port
= 0;
13766 err
= bind( sock
, &sip
.sa
, (socklen_t
) sizeof( sip
.v6
) );
13767 err
= map_socket_noerr_errno( sock
, err
);
13769 require_noerr( err
, exit
);
13774 dlogassert( "Unsupported family: %d", inFamily
);
13775 err
= kUnsupportedErr
;
13779 if( inType
== SOCK_STREAM
)
13781 err
= listen( sock
, SOMAXCONN
);
13782 err
= map_socket_noerr_errno( sock
, err
);
13785 err
= listen( sock
, 5 );
13786 err
= map_socket_noerr_errno( sock
, err
);
13787 require_noerr( err
, exit
);
13793 len
= (socklen_t
) sizeof( sip
);
13794 err
= getsockname( sock
, &sip
.sa
, &len
);
13795 err
= map_socket_noerr_errno( sock
, err
);
13796 require_noerr( err
, exit
);
13798 *outPort
= SockAddrGetPort( &sip
);
13801 sock
= kInvalidSocketRef
;
13804 ForgetSocket( &sock
);
13808 //===========================================================================================================================
13811 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
13812 //===========================================================================================================================
13814 void * memdup( const void *inPtr
, size_t inLen
)
13818 mem
= malloc( ( inLen
> 0 ) ? inLen
: 1 ); // If inLen is 0, use 1 since malloc( 0 ) is not well defined.
13819 require( mem
, exit
);
13820 if( inLen
> 0 ) memcpy( mem
, inPtr
, inLen
);
13826 #if( !TARGET_OS_WINDOWS )
13827 //===========================================================================================================================
13830 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
13831 //===========================================================================================================================
13833 int memicmp( const void *inP1
, const void *inP2
, size_t inLen
)
13835 const unsigned char * p1
;
13836 const unsigned char * e1
;
13837 const unsigned char * p2
;
13841 p1
= (const unsigned char *) inP1
;
13843 p2
= (const unsigned char *) inP2
;
13848 c1
= tolower( c1
);
13849 c2
= tolower( c2
);
13850 if( c1
< c2
) return( -1 );
13851 if( c1
> c2
) return( 1 );