2 Copyright (c) 2016-2017 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/CommandLineUtils.h>
10 #include <CoreUtils/DataBufferUtils.h>
11 #include <CoreUtils/DebugServices.h>
12 #include <CoreUtils/MiscUtils.h>
13 #include <CoreUtils/NetUtils.h>
14 #include <CoreUtils/PrintFUtils.h>
15 #include <CoreUtils/RandomNumberUtils.h>
16 #include <CoreUtils/StringUtils.h>
17 #include <CoreUtils/TickUtils.h>
19 #include <dns_sd_private.h>
21 #if( TARGET_OS_DARWIN )
24 #include <sys/proc_info.h>
27 #if( TARGET_OS_POSIX )
28 #include <sys/resource.h>
31 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
32 #include "tweetnacl.h" // TweetNaCl from <https://tweetnacl.cr.yp.to/software.html>.
35 //===========================================================================================================================
37 //===========================================================================================================================
41 #define kDNSSDUtilNumVersion NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 )
43 #if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) )
44 #define DNSSDUTIL_SOURCE_VERSION "0.0.0"
47 // DNS-SD API flag descriptors
49 #define kDNSServiceFlagsDescriptors \
50 "\x00" "AutoTrigger\0" \
53 "\x03" "NoAutoRename\0" \
56 "\x06" "BrowseDomains\0" \
57 "\x07" "RegistrationDomains\0" \
58 "\x08" "LongLivedQuery\0" \
59 "\x09" "AllowRemoteQuery\0" \
60 "\x0A" "ForceMulticast\0" \
61 "\x0B" "KnownUnique\0" \
62 "\x0C" "ReturnIntermediates\0" \
63 "\x0D" "NonBrowsable\0" \
64 "\x0E" "ShareConnection\0" \
65 "\x0F" "SuppressUnusable\0" \
67 "\x11" "IncludeP2P\0" \
68 "\x12" "WakeOnResolve\0" \
69 "\x13" "BackgroundTrafficClass\0" \
70 "\x14" "IncludeAWDL\0" \
72 "\x16" "UnicastResponse\0" \
73 "\x17" "ValidateOptional\0" \
74 "\x18" "WakeOnlyService\0" \
75 "\x19" "ThresholdOne\0" \
76 "\x1A" "ThresholdFinder\0" \
77 "\x1B" "DenyCellular\0" \
78 "\x1C" "ServiceIndex\0" \
79 "\x1D" "DenyExpensive\0" \
80 "\x1E" "PathEvaluationDone\0" \
83 #define kDNSServiceProtocolDescriptors \
90 #define kDNSHeaderFlag_Response ( 1 << 15 )
91 #define kDNSHeaderFlag_AuthAnswer ( 1 << 10 )
92 #define kDNSHeaderFlag_Truncation ( 1 << 9 )
93 #define kDNSHeaderFlag_RecursionDesired ( 1 << 8 )
94 #define kDNSHeaderFlag_RecursionAvailable ( 1 << 7 )
96 #define kDNSOpCode_Query 0
97 #define kDNSOpCode_InverseQuery 1
98 #define kDNSOpCode_Status 2
99 #define kDNSOpCode_Notify 4
100 #define kDNSOpCode_Update 5
102 #define kDNSRCode_NoError 0
103 #define kDNSRCode_FormatError 1
104 #define kDNSRCode_ServerFailure 2
105 #define kDNSRCode_NXDomain 3
106 #define kDNSRCode_NotImplemented 4
107 #define kDNSRCode_Refused 5
109 #define kQClassUnicastResponseBit ( 1U << 15 )
110 #define kRRClassCacheFlushBit ( 1U << 15 )
112 #define kDomainLabelLengthMax 63
113 #define kDomainNameLengthMax 256
115 //===========================================================================================================================
116 // Gerneral Command Options
117 //===========================================================================================================================
119 // Command option macros
121 #define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON ) \
122 CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None, \
125 #define kRequiredOptionSuffix " [REQUIRED]"
127 #define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
128 CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, \
129 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
130 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
132 #define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
133 MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
135 #define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
136 CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
137 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
138 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
140 #define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \
141 CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL )
143 #define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
144 CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
145 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
146 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
148 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
149 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
151 // DNS-SD API flag options
153 static int gDNSSDFlags
= 0;
154 static int gDNSSDFlag_BrowseDomains
= false;
155 static int gDNSSDFlag_DenyCellular
= false;
156 static int gDNSSDFlag_DenyExpensive
= false;
157 static int gDNSSDFlag_ForceMulticast
= false;
158 static int gDNSSDFlag_IncludeAWDL
= false;
159 static int gDNSSDFlag_NoAutoRename
= false;
160 static int gDNSSDFlag_PathEvaluationDone
= false;
161 static int gDNSSDFlag_RegistrationDomains
= false;
162 static int gDNSSDFlag_ReturnIntermediates
= false;
163 static int gDNSSDFlag_Shared
= false;
164 static int gDNSSDFlag_SuppressUnusable
= false;
165 static int gDNSSDFlag_Timeout
= false;
166 static int gDNSSDFlag_UnicastResponse
= false;
167 static int gDNSSDFlag_Unique
= false;
169 #define DNSSDFlagsOption() \
170 IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \
171 "DNSServiceFlags to use. This value is bitwise ORed with other single flag options.", false )
173 #define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \
174 BooleanOption( SHORT_CHAR, Stringify( FLAG_NAME ), &gDNSSDFlag_ ## FLAG_NAME, \
175 "Use kDNSServiceFlags" Stringify( FLAG_NAME ) "." )
177 #define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular )
178 #define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive )
179 #define DNSSDFlagsOption_ForceMulticast() DNSSDFlagOption( 'M', ForceMulticast )
180 #define DNSSDFlagsOption_IncludeAWDL() DNSSDFlagOption( 'A', IncludeAWDL )
181 #define DNSSDFlagsOption_NoAutoRename() DNSSDFlagOption( 'N', NoAutoRename )
182 #define DNSSDFlagsOption_PathEvalDone() DNSSDFlagOption( 'P', PathEvaluationDone )
183 #define DNSSDFlagsOption_ReturnIntermediates() DNSSDFlagOption( 'I', ReturnIntermediates )
184 #define DNSSDFlagsOption_Shared() DNSSDFlagOption( 'S', Shared )
185 #define DNSSDFlagsOption_SuppressUnusable() DNSSDFlagOption( 'S', SuppressUnusable )
186 #define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout )
187 #define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse )
188 #define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique )
192 static const char * gInterface
= NULL
;
194 #define InterfaceOption() \
195 StringOption( 'i', "interface", &gInterface, "interface", \
196 "Network interface by name or index. Use index -1 for local-only.", false )
198 // Connection options
200 #define kConnectionArg_Normal ""
201 #define kConnectionArgPrefix_PID "pid:"
202 #define kConnectionArgPrefix_UUID "uuid:"
204 static const char * gConnectionOpt
= kConnectionArg_Normal
;
206 #define ConnectionOptions() \
207 { kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type", \
208 kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL, \
209 "Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL }
211 #define kConnectionSection_Name "Connection Option"
212 #define kConnectionSection_Text \
213 "The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n" \
214 "the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n" \
215 "specifying the connection option without an argument, i.e.,\n" \
219 "To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n" \
221 " --connection=pid:<PID>\n" \
223 "to specify the delegator by PID, or use\n" \
225 " --connection=uuid:<UUID>\n" \
227 "to specify the delegator by UUID.\n" \
229 "To not use a main connection at all, but instead perform operations on their own connections, use\n" \
233 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
235 // Help text for record data options
237 #define kRDataArgPrefix_File "file:"
238 #define kRDataArgPrefix_HexString "hex:"
239 #define kRDataArgPrefix_String "string:"
240 #define kRDataArgPrefix_TXT "txt:"
242 #define kRecordDataSection_Name "Record Data Arguments"
243 #define kRecordDataSection_Text \
244 "A record data argument is specified in one of the following formats:\n" \
246 "Format Syntax Example\n" \
247 "String string:<string> string:'\\x09color=red'\n" \
248 "Hexadecimal string hex:<hex string> hex:c0a80101 or hex:'C0 A8 01 01'\n" \
249 "TXT record keys and values txt:<comma-delimited keys and values> txt:'key1=x,key2=y\\,z,key3'\n" \
250 "File containing raw record data file:<file path> file:dir/record_data.bin\n"
252 #define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text )
254 //===========================================================================================================================
255 // Browse Command Options
256 //===========================================================================================================================
258 static char ** gBrowse_ServiceTypes
= NULL
;
259 static size_t gBrowse_ServiceTypesCount
= 0;
260 static const char * gBrowse_Domain
= NULL
;
261 static int gBrowse_DoResolve
= false;
262 static int gBrowse_QueryTXT
= false;
263 static int gBrowse_TimeLimitSecs
= 0;
265 static CLIOption kBrowseOpts
[] =
268 MultiStringOption( 't', "type", &gBrowse_ServiceTypes
, &gBrowse_ServiceTypesCount
, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ),
269 StringOption( 'd', "domain", &gBrowse_Domain
, "domain", "Domain in which to browse for the service type(s).", false ),
271 CLI_OPTION_GROUP( "Flags" ),
273 DNSSDFlagsOption_IncludeAWDL(),
275 CLI_OPTION_GROUP( "Operation" ),
277 BooleanOption( 0 , "resolve", &gBrowse_DoResolve
, "Resolve service instances." ),
278 BooleanOption( 0 , "queryTXT", &gBrowse_QueryTXT
, "Query TXT records of service instances." ),
279 IntegerOption( 'l', "timeLimit", &gBrowse_TimeLimitSecs
, "seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ),
285 //===========================================================================================================================
286 // GetAddrInfo Command Options
287 //===========================================================================================================================
289 static const char * gGetAddrInfo_Name
= NULL
;
290 static int gGetAddrInfo_ProtocolIPv4
= false;
291 static int gGetAddrInfo_ProtocolIPv6
= false;
292 static int gGetAddrInfo_OneShot
= false;
293 static int gGetAddrInfo_TimeLimitSecs
= 0;
295 static CLIOption kGetAddrInfoOpts
[] =
298 StringOption( 'n', "name", &gGetAddrInfo_Name
, "domain name", "Domain name to resolve.", true ),
299 BooleanOption( 0 , "ipv4", &gGetAddrInfo_ProtocolIPv4
, "Use kDNSServiceProtocol_IPv4." ),
300 BooleanOption( 0 , "ipv6", &gGetAddrInfo_ProtocolIPv6
, "Use kDNSServiceProtocol_IPv6." ),
302 CLI_OPTION_GROUP( "Flags" ),
304 DNSSDFlagsOption_DenyCellular(),
305 DNSSDFlagsOption_DenyExpensive(),
306 DNSSDFlagsOption_PathEvalDone(),
307 DNSSDFlagsOption_ReturnIntermediates(),
308 DNSSDFlagsOption_SuppressUnusable(),
309 DNSSDFlagsOption_Timeout(),
311 CLI_OPTION_GROUP( "Operation" ),
313 BooleanOption( 'o', "oneshot", &gGetAddrInfo_OneShot
, "Finish after first set of results." ),
314 IntegerOption( 'l', "timeLimit", &gGetAddrInfo_TimeLimitSecs
, "seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ),
320 //===========================================================================================================================
321 // QueryRecord Command Options
322 //===========================================================================================================================
324 static const char * gQueryRecord_Name
= NULL
;
325 static const char * gQueryRecord_Type
= NULL
;
326 static int gQueryRecord_OneShot
= false;
327 static int gQueryRecord_TimeLimitSecs
= 0;
328 static int gQueryRecord_RawRData
= false;
330 static CLIOption kQueryRecordOpts
[] =
333 StringOption( 'n', "name", &gQueryRecord_Name
, "domain name", "Full domain name of record to query.", true ),
334 StringOption( 't', "type", &gQueryRecord_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
336 CLI_OPTION_GROUP( "Flags" ),
338 DNSSDFlagsOption_IncludeAWDL(),
339 DNSSDFlagsOption_ForceMulticast(),
340 DNSSDFlagsOption_Timeout(),
341 DNSSDFlagsOption_ReturnIntermediates(),
342 DNSSDFlagsOption_SuppressUnusable(),
343 DNSSDFlagsOption_UnicastResponse(),
344 DNSSDFlagsOption_DenyCellular(),
345 DNSSDFlagsOption_DenyExpensive(),
346 DNSSDFlagsOption_PathEvalDone(),
348 CLI_OPTION_GROUP( "Operation" ),
350 BooleanOption( 'o', "oneshot", &gQueryRecord_OneShot
, "Finish after first set of results." ),
351 IntegerOption( 'l', "timeLimit", &gQueryRecord_TimeLimitSecs
, "seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ),
352 BooleanOption( 0 , "raw", &gQueryRecord_RawRData
, "Show record data as a hexdump." ),
358 //===========================================================================================================================
359 // Register Command Options
360 //===========================================================================================================================
362 static const char * gRegister_Name
= NULL
;
363 static const char * gRegister_Type
= NULL
;
364 static const char * gRegister_Domain
= NULL
;
365 static int gRegister_Port
= 0;
366 static const char * gRegister_TXT
= NULL
;
367 static int gRegister_LifetimeMs
= -1;
368 static const char ** gAddRecord_Types
= NULL
;
369 static size_t gAddRecord_TypesCount
= 0;
370 static const char ** gAddRecord_Data
= NULL
;
371 static size_t gAddRecord_DataCount
= 0;
372 static const char ** gAddRecord_TTLs
= NULL
;
373 static size_t gAddRecord_TTLsCount
= 0;
374 static const char * gUpdateRecord_Data
= NULL
;
375 static int gUpdateRecord_DelayMs
= 0;
376 static int gUpdateRecord_TTL
= 0;
378 static CLIOption kRegisterOpts
[] =
381 StringOption( 'n', "name", &gRegister_Name
, "service name", "Name of service.", false ),
382 StringOption( 't', "type", &gRegister_Type
, "service type", "Service type, e.g., \"_ssh._tcp\".", true ),
383 StringOption( 'd', "domain", &gRegister_Domain
, "domain", "Domain in which to advertise the service.", false ),
384 IntegerOption( 'p', "port", &gRegister_Port
, "port number", "Service's port number.", true ),
385 StringOption( 0 , "txt", &gRegister_TXT
, "record data", "The TXT record data. See " kRecordDataSection_Name
" below.", false ),
387 CLI_OPTION_GROUP( "Flags" ),
389 DNSSDFlagsOption_IncludeAWDL(),
390 DNSSDFlagsOption_NoAutoRename(),
392 CLI_OPTION_GROUP( "Operation" ),
393 IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs
, "ms", "Lifetime of the service registration in milliseconds.", false ),
395 CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ),
396 StringOption( 0 , "updateData", &gUpdateRecord_Data
, "record data", "Record data for the record update. See " kRecordDataSection_Name
" below.", false ),
397 IntegerOption( 0 , "updateDelay", &gUpdateRecord_DelayMs
, "ms", "Number of milliseconds after registration to wait before record update.", false ),
398 IntegerOption( 0 , "updateTTL", &gUpdateRecord_TTL
, "seconds", "Time-to-live of the updated record.", false ),
400 CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ),
401 MultiStringOption( 0 , "addType", &gAddRecord_Types
, &gAddRecord_TypesCount
, "record type", "Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ),
402 MultiStringOptionEx( 0 , "addData", &gAddRecord_Data
, &gAddRecord_DataCount
, "record data", "Additional record's data. See " kRecordDataSection_Name
" below.", false, NULL
),
403 MultiStringOption( 0 , "addTTL", &gAddRecord_TTLs
, &gAddRecord_TTLsCount
, "seconds", "Time-to-live of additional record in seconds. Use '0' for default.", false ),
409 //===========================================================================================================================
410 // RegisterRecord Command Options
411 //===========================================================================================================================
413 static const char * gRegisterRecord_Name
= NULL
;
414 static const char * gRegisterRecord_Type
= NULL
;
415 static const char * gRegisterRecord_Data
= NULL
;
416 static int gRegisterRecord_TTL
= 0;
417 static int gRegisterRecord_LifetimeMs
= -1;
418 static const char * gRegisterRecord_UpdateData
= NULL
;
419 static int gRegisterRecord_UpdateDelayMs
= 0;
420 static int gRegisterRecord_UpdateTTL
= 0;
422 static CLIOption kRegisterRecordOpts
[] =
425 StringOption( 'n', "name", &gRegisterRecord_Name
, "record name", "Fully qualified domain name of record.", true ),
426 StringOption( 't', "type", &gRegisterRecord_Type
, "record type", "Record type by name (e.g., TXT, PTR, A) or number.", true ),
427 StringOption( 'd', "data", &gRegisterRecord_Data
, "record data", "The record data. See " kRecordDataSection_Name
" below.", false ),
428 IntegerOption( 0 , "ttl", &gRegisterRecord_TTL
, "seconds", "Time-to-live in seconds. Use '0' for default.", false ),
430 CLI_OPTION_GROUP( "Flags" ),
432 DNSSDFlagsOption_IncludeAWDL(),
433 DNSSDFlagsOption_Shared(),
434 DNSSDFlagsOption_Unique(),
436 CLI_OPTION_GROUP( "Operation" ),
437 IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs
, "ms", "Lifetime of the service registration in milliseconds.", false ),
439 CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ),
440 StringOption( 0 , "updateData", &gRegisterRecord_UpdateData
, "record data", "Record data for the record update.", false ),
441 IntegerOption( 0 , "updateDelay", &gRegisterRecord_UpdateDelayMs
, "ms", "Number of milliseconds after registration to wait before record update.", false ),
442 IntegerOption( 0 , "updateTTL", &gRegisterRecord_UpdateTTL
, "seconds", "Time-to-live of the updated record.", false ),
448 //===========================================================================================================================
449 // Resolve Command Options
450 //===========================================================================================================================
452 static char * gResolve_Name
= NULL
;
453 static char * gResolve_Type
= NULL
;
454 static char * gResolve_Domain
= NULL
;
455 static int gResolve_TimeLimitSecs
= 0;
457 static CLIOption kResolveOpts
[] =
460 StringOption( 'n', "name", &gResolve_Name
, "service name", "Name of the service instance to resolve.", true ),
461 StringOption( 't', "type", &gResolve_Type
, "service type", "Type of the service instance to resolve.", true ),
462 StringOption( 'd', "domain", &gResolve_Domain
, "domain", "Domain of the service instance to resolve.", true ),
464 CLI_OPTION_GROUP( "Flags" ),
466 DNSSDFlagsOption_ForceMulticast(),
467 DNSSDFlagsOption_IncludeAWDL(),
468 DNSSDFlagsOption_ReturnIntermediates(),
470 CLI_OPTION_GROUP( "Operation" ),
472 IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs
, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ),
478 //===========================================================================================================================
479 // Reconfirm Command Options
480 //===========================================================================================================================
482 static const char * gReconfirmRecord_Name
= NULL
;
483 static const char * gReconfirmRecord_Type
= NULL
;
484 static const char * gReconfirmRecord_Class
= NULL
;
485 static const char * gReconfirmRecord_Data
= NULL
;
487 static CLIOption kReconfirmOpts
[] =
490 StringOption( 'n', "name", &gReconfirmRecord_Name
, "record name", "Full name of the record to reconfirm.", true ),
491 StringOption( 't', "type", &gReconfirmRecord_Type
, "record type", "Type of the record to reconfirm.", true ),
492 StringOption( 'c', "class", &gReconfirmRecord_Class
, "record class", "Class of the record to reconfirm. Default class is IN.", false ),
493 StringOption( 'd', "data", &gReconfirmRecord_Data
, "record data", "The record data. See " kRecordDataSection_Name
" below.", false ),
495 CLI_OPTION_GROUP( "Flags" ),
502 //===========================================================================================================================
503 // getaddrinfo-POSIX Command Options
504 //===========================================================================================================================
506 static const char * gGAIPOSIX_HostName
= NULL
;
507 static const char * gGAIPOSIX_ServName
= NULL
;
508 static const char * gGAIPOSIX_Family
= NULL
;
509 static int gGAIPOSIXFlag_AddrConfig
= false;
510 static int gGAIPOSIXFlag_All
= false;
511 static int gGAIPOSIXFlag_CanonName
= false;
512 static int gGAIPOSIXFlag_NumericHost
= false;
513 static int gGAIPOSIXFlag_NumericServ
= false;
514 static int gGAIPOSIXFlag_Passive
= false;
515 static int gGAIPOSIXFlag_V4Mapped
= false;
516 #if( defined( AI_V4MAPPED_CFG ) )
517 static int gGAIPOSIXFlag_V4MappedCFG
= false;
519 #if( defined( AI_DEFAULT ) )
520 static int gGAIPOSIXFlag_Default
= false;
523 static CLIOption kGetAddrInfoPOSIXOpts
[] =
525 StringOption( 'n', "hostname", &gGAIPOSIX_HostName
, "hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ),
526 StringOption( 's', "servname", &gGAIPOSIX_ServName
, "servname", "Port number in decimal or service name from services(5).", false ),
528 CLI_OPTION_GROUP( "Hints " ),
529 StringOptionEx( 'f', "family", &gGAIPOSIX_Family
, "address family", "Address family to use for hints ai_family field.", false,
531 "Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n"
532 "address family is specified, then AF_UNSPEC is used.\n"
534 BooleanOption( 0 , "flag-addrconfig", &gGAIPOSIXFlag_AddrConfig
, "In hints ai_flags field, set AI_ADDRCONFIG." ),
535 BooleanOption( 0 , "flag-all", &gGAIPOSIXFlag_All
, "In hints ai_flags field, set AI_ALL." ),
536 BooleanOption( 0 , "flag-canonname", &gGAIPOSIXFlag_CanonName
, "In hints ai_flags field, set AI_CANONNAME." ),
537 BooleanOption( 0 , "flag-numerichost", &gGAIPOSIXFlag_NumericHost
, "In hints ai_flags field, set AI_NUMERICHOST." ),
538 BooleanOption( 0 , "flag-numericserv", &gGAIPOSIXFlag_NumericServ
, "In hints ai_flags field, set AI_NUMERICSERV." ),
539 BooleanOption( 0 , "flag-passive", &gGAIPOSIXFlag_Passive
, "In hints ai_flags field, set AI_PASSIVE." ),
540 BooleanOption( 0 , "flag-v4mapped", &gGAIPOSIXFlag_V4Mapped
, "In hints ai_flags field, set AI_V4MAPPED." ),
541 #if( defined( AI_V4MAPPED_CFG ) )
542 BooleanOption( 0 , "flag-v4mappedcfg", &gGAIPOSIXFlag_V4MappedCFG
, "In hints ai_flags field, set AI_V4MAPPED_CFG." ),
544 #if( defined( AI_DEFAULT ) )
545 BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default
, "In hints ai_flags field, set AI_DEFAULT." ),
548 CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ),
552 //===========================================================================================================================
553 // ReverseLookup Command Options
554 //===========================================================================================================================
556 static const char * gReverseLookup_IPAddr
= NULL
;
557 static int gReverseLookup_OneShot
= false;
558 static int gReverseLookup_TimeLimitSecs
= 0;
560 static CLIOption kReverseLookupOpts
[] =
563 StringOption( 'a', "address", &gReverseLookup_IPAddr
, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ),
565 CLI_OPTION_GROUP( "Flags" ),
567 DNSSDFlagsOption_ForceMulticast(),
568 DNSSDFlagsOption_ReturnIntermediates(),
569 DNSSDFlagsOption_SuppressUnusable(),
571 CLI_OPTION_GROUP( "Operation" ),
573 BooleanOption( 'o', "oneshot", &gReverseLookup_OneShot
, "Finish after first set of results." ),
574 IntegerOption( 'l', "timeLimit", &gReverseLookup_TimeLimitSecs
, "seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ),
580 //===========================================================================================================================
581 // BrowseAll Command Options
582 //===========================================================================================================================
584 static const char * gBrowseAll_Domain
= NULL
;
585 static char ** gBrowseAll_ServiceTypes
= NULL
;
586 static size_t gBrowseAll_ServiceTypesCount
= 0;
587 static int gBrowseAll_IncludeAWDL
= false;
588 static int gBrowseAll_BrowseTimeSecs
= 5;
589 static int gBrowseAll_ConnectTimeLimitSecs
= 5;
591 static CLIOption kBrowseAllOpts
[] =
594 StringOption( 'd', "domain", &gBrowseAll_Domain
, "domain", "Domain in which to browse for the service.", false ),
595 MultiStringOption( 't', "type", &gBrowseAll_ServiceTypes
, &gBrowseAll_ServiceTypesCount
, "service type", "Service type(s), e.g., \"_ssh._tcp\".", false ),
597 CLI_OPTION_GROUP( "Flags" ),
598 DNSSDFlagsOption_IncludeAWDL(),
600 CLI_OPTION_GROUP( "Operation" ),
601 IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs
, "seconds", "Specifies the duration of the browse.", false ),
602 IntegerOption( 'c', "connectTimeLimit", &gBrowseAll_ConnectTimeLimitSecs
, "seconds", "Specifies the max duration of the connect operations.", false ),
606 //===========================================================================================================================
607 // GetAddrInfoStress Command Options
608 //===========================================================================================================================
610 static int gGAIStress_TestDurationSecs
= 0;
611 static int gGAIStress_ConnectionCount
= 0;
612 static int gGAIStress_DurationMinMs
= 0;
613 static int gGAIStress_DurationMaxMs
= 0;
614 static int gGAIStress_RequestCountMax
= 0;
616 static CLIOption kGetAddrInfoStressOpts
[] =
620 CLI_OPTION_GROUP( "Flags" ),
621 DNSSDFlagsOption_ReturnIntermediates(),
622 DNSSDFlagsOption_SuppressUnusable(),
624 CLI_OPTION_GROUP( "Operation" ),
625 IntegerOption( 0, "testDuration", &gGAIStress_TestDurationSecs
, "seconds", "Stress test duration in seconds. Use '0' for forever.", false ),
626 IntegerOption( 0, "connectionCount", &gGAIStress_ConnectionCount
, "integer", "Number of simultaneous DNS-SD connections.", true ),
627 IntegerOption( 0, "requestDurationMin", &gGAIStress_DurationMinMs
, "ms", "Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
628 IntegerOption( 0, "requestDurationMax", &gGAIStress_DurationMaxMs
, "ms", "Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
629 IntegerOption( 0, "consecutiveRequestMax", &gGAIStress_RequestCountMax
, "integer", "Maximum number of requests on a connection before restarting it.", true ),
633 //===========================================================================================================================
634 // DNSQuery Command Options
635 //===========================================================================================================================
637 static char * gDNSQuery_Name
= NULL
;
638 static char * gDNSQuery_Type
= "A";
639 static char * gDNSQuery_Server
= NULL
;
640 static int gDNSQuery_TimeLimitSecs
= 5;
641 static int gDNSQuery_UseTCP
= false;
642 static int gDNSQuery_Flags
= kDNSHeaderFlag_RecursionDesired
;
643 static int gDNSQuery_RawRData
= false;
644 static int gDNSQuery_Verbose
= false;
646 #if( TARGET_OS_DARWIN )
647 #define kDNSQueryServerOptionIsRequired false
649 #define kDNSQueryServerOptionIsRequired true
652 static CLIOption kDNSQueryOpts
[] =
654 StringOption( 'n', "name", &gDNSQuery_Name
, "name", "Question name (QNAME) to put in DNS query message.", true ),
655 StringOption( 't', "type", &gDNSQuery_Type
, "type", "Question type (QTYPE) to put in DNS query message. Default value is 'A'.", false ),
656 StringOption( 's', "server", &gDNSQuery_Server
, "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired
),
657 IntegerOption( 'l', "timeLimit", &gDNSQuery_TimeLimitSecs
, "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ),
658 BooleanOption( 0 , "tcp", &gDNSQuery_UseTCP
, "Send the DNS query via TCP instead of UDP." ),
659 IntegerOption( 'f', "flags", &gDNSQuery_Flags
, "flags", "16-bit value for DNS header flags/codes field. Default value is 0x0100 (Recursion Desired).", false ),
660 BooleanOption( 0 , "raw", &gDNSQuery_RawRData
, "Present record data as a hexdump." ),
661 BooleanOption( 'v', "verbose", &gDNSQuery_Verbose
, "Prints the DNS message to be sent to the server." ),
665 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
666 //===========================================================================================================================
667 // DNSCrypt Command Options
668 //===========================================================================================================================
670 static char * gDNSCrypt_ProviderName
= NULL
;
671 static char * gDNSCrypt_ProviderKey
= NULL
;
672 static char * gDNSCrypt_Name
= NULL
;
673 static char * gDNSCrypt_Type
= NULL
;
674 static char * gDNSCrypt_Server
= NULL
;
675 static int gDNSCrypt_TimeLimitSecs
= 5;
676 static int gDNSCrypt_RawRData
= false;
677 static int gDNSCrypt_Verbose
= false;
679 static CLIOption kDNSCryptOpts
[] =
681 StringOption( 'p', "providerName", &gDNSCrypt_ProviderName
, "name", "The DNSCrypt provider name.", true ),
682 StringOption( 'k', "providerKey", &gDNSCrypt_ProviderKey
, "hex string", "The DNSCrypt provider's public signing key.", true ),
683 StringOption( 'n', "name", &gDNSCrypt_Name
, "name", "Question name (QNAME) to put in DNS query message.", true ),
684 StringOption( 't', "type", &gDNSCrypt_Type
, "type", "Question type (QTYPE) to put in DNS query message.", true ),
685 StringOption( 's', "server", &gDNSCrypt_Server
, "IP address", "DNS server's IPv4 or IPv6 address.", true ),
686 IntegerOption( 'l', "timeLimit", &gDNSCrypt_TimeLimitSecs
, "seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ),
687 BooleanOption( 0 , "raw", &gDNSCrypt_RawRData
, "Present record data as a hexdump." ),
688 BooleanOption( 'v', "verbose", &gDNSCrypt_Verbose
, "Prints the DNS message to be sent to the server." ),
693 //===========================================================================================================================
694 // MDNSQuery Command Options
695 //===========================================================================================================================
697 static char * gMDNSQuery_Name
= NULL
;
698 static char * gMDNSQuery_Type
= NULL
;
699 static int gMDNSQuery_SourcePort
= 0;
700 static int gMDNSQuery_IsQU
= false;
701 static int gMDNSQuery_RawRData
= false;
702 static int gMDNSQuery_UseIPv4
= false;
703 static int gMDNSQuery_UseIPv6
= false;
704 static int gMDNSQuery_AllResponses
= false;
705 static int gMDNSQuery_ReceiveSecs
= 1;
707 static CLIOption kMDNSQueryOpts
[] =
709 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
710 StringOption( 'n', "name", &gMDNSQuery_Name
, "name", "Question name (QNAME) to put in mDNS message.", true ),
711 StringOption( 't', "type", &gMDNSQuery_Type
, "type", "Question type (QTYPE) to put in mDNS message.", true ),
712 IntegerOption( 'p', "sourcePort", &gMDNSQuery_SourcePort
, "port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ),
713 BooleanOption( 'u', "QU", &gMDNSQuery_IsQU
, "Set the unicast-response bit, i.e., send a QU question." ),
714 BooleanOption( 0 , "raw", &gMDNSQuery_RawRData
, "Present record data as a hexdump." ),
715 BooleanOption( 0 , "ipv4", &gMDNSQuery_UseIPv4
, "Use IPv4." ),
716 BooleanOption( 0 , "ipv6", &gMDNSQuery_UseIPv6
, "Use IPv6." ),
717 BooleanOption( 'a', "allResponses", &gMDNSQuery_AllResponses
, "Print all received mDNS messages, not just those containing answers." ),
718 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 ),
722 //===========================================================================================================================
723 // PIDToUUID Command Options
724 //===========================================================================================================================
726 static int gPIDToUUID_PID
= 0;
728 static CLIOption kPIDToUUIDOpts
[] =
730 IntegerOption( 'p', "pid", &gPIDToUUID_PID
, "PID", "Process ID.", true ),
734 //===========================================================================================================================
736 //===========================================================================================================================
738 static OSStatus
VersionOptionCallback( CLIOption
*inOption
, const char *inArg
, int inUnset
);
740 static void BrowseCmd( void );
741 static void GetAddrInfoCmd( void );
742 static void QueryRecordCmd( void );
743 static void RegisterCmd( void );
744 static void RegisterRecordCmd( void );
745 static void ResolveCmd( void );
746 static void ReconfirmCmd( void );
747 static void GetAddrInfoPOSIXCmd( void );
748 static void ReverseLookupCmd( void );
749 static void BrowseAllCmd( void );
750 static void GetAddrInfoStressCmd( void );
751 static void DNSQueryCmd( void );
752 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
753 static void DNSCryptCmd( void );
755 static void MDNSQueryCmd( void );
756 static void PIDToUUIDCmd( void );
757 static void DaemonVersionCmd( void );
759 static CLIOption kGlobalOpts
[] =
761 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback
, NULL
, NULL
,
762 kCLIOptionFlags_NoArgument
| kCLIOptionFlags_GlobalOnly
, "Displays the version of this tool.", NULL
),
767 Command( "browse", BrowseCmd
, kBrowseOpts
, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
768 Command( "getAddrInfo", GetAddrInfoCmd
, kGetAddrInfoOpts
, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
769 Command( "queryRecord", QueryRecordCmd
, kQueryRecordOpts
, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
770 Command( "register", RegisterCmd
, kRegisterOpts
, "Uses DNSServiceRegister() to register a service.", false ),
771 Command( "registerRecord", RegisterRecordCmd
, kRegisterRecordOpts
, "Uses DNSServiceRegisterRecord() to register a record.", false ),
772 Command( "resolve", ResolveCmd
, kResolveOpts
, "Uses DNSServiceResolve() to resolve a service.", false ),
773 Command( "reconfirm", ReconfirmCmd
, kReconfirmOpts
, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
774 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd
, kGetAddrInfoPOSIXOpts
, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
775 Command( "reverseLookup", ReverseLookupCmd
, kReverseLookupOpts
, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
776 Command( "browseAll", BrowseAllCmd
, kBrowseAllOpts
, "Browse and resolve all, or just some, services.", false ),
778 // Uncommon commands.
780 Command( "getAddrInfoStress", GetAddrInfoStressCmd
, kGetAddrInfoStressOpts
, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
781 Command( "DNSQuery", DNSQueryCmd
, kDNSQueryOpts
, "Crafts and sends a DNS query.", true ),
782 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
783 Command( "DNSCrypt", DNSCryptCmd
, kDNSCryptOpts
, "Crafts and sends a DNSCrypt query.", true ),
785 Command( "mDNSQuery", MDNSQueryCmd
, kMDNSQueryOpts
, "Crafts and sends an mDNS query over the specified interface.", true ),
786 Command( "pid2uuid", PIDToUUIDCmd
, kPIDToUUIDOpts
, "Prints the UUID of a process.", true ),
787 Command( "daemonVersion", DaemonVersionCmd
, NULL
, "Prints the version of the DNS-SD daemon.", true ),
793 //===========================================================================================================================
795 //===========================================================================================================================
797 #define kExitReason_OneShotDone "one-shot done"
798 #define kExitReason_ReceivedResponse "received response"
799 #define kExitReason_SIGINT "interrupt signal"
800 #define kExitReason_Timeout "timeout"
801 #define kExitReason_TimeLimit "time limit"
803 static void Exit( void *inContext
) ATTRIBUTE_NORETURN
;
805 #define kTimestampBufLen 27
807 static char * GetTimestampStr( char inBuffer
[ kTimestampBufLen
] );
808 static DNSServiceFlags
GetDNSSDFlagsFromOpts( void );
812 kConnectionType_None
= 0,
813 kConnectionType_Normal
= 1,
814 kConnectionType_DelegatePID
= 2,
815 kConnectionType_DelegateUUID
= 3
832 CreateConnectionFromArgString(
833 const char * inString
,
834 dispatch_queue_t inQueue
,
835 DNSServiceRef
* outSDRef
,
836 ConnectionDesc
* outDesc
);
837 static OSStatus
InterfaceIndexFromArgString( const char *inString
, uint32_t *outIndex
);
838 static OSStatus
RecordDataFromArgString( const char *inString
, uint8_t **outDataPtr
, size_t *outDataLen
);
839 static OSStatus
RecordTypeFromArgString( const char *inString
, uint16_t *outValue
);
840 static OSStatus
RecordClassFromArgString( const char *inString
, uint16_t *outValue
);
842 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
844 static char * InterfaceIndexToName( uint32_t inIfIndex
, char inNameBuf
[ kInterfaceNameBufLen
] );
845 static const char * RecordTypeToString( unsigned int inValue
);
847 // DNS message helpers
853 uint8_t questionCount
[ 2 ];
854 uint8_t answerCount
[ 2 ];
855 uint8_t authorityCount
[ 2 ];
856 uint8_t additionalCount
[ 2 ];
860 #define kDNSHeaderLength 12
861 check_compile_time( sizeof( DNSHeader
) == kDNSHeaderLength
);
863 #define DNSHeaderGetID( HDR ) ReadBig16( ( HDR )->id )
864 #define DNSHeaderGetFlags( HDR ) ReadBig16( ( HDR )->flags )
865 #define DNSHeaderGetQuestionCount( HDR ) ReadBig16( ( HDR )->questionCount )
866 #define DNSHeaderGetAnswerCount( HDR ) ReadBig16( ( HDR )->answerCount )
867 #define DNSHeaderGetAuthorityCount( HDR ) ReadBig16( ( HDR )->authorityCount )
868 #define DNSHeaderGetAdditionalCount( HDR ) ReadBig16( ( HDR )->additionalCount )
871 DNSMessageExtractDomainName(
872 const uint8_t * inMsgPtr
,
874 const uint8_t * inNamePtr
,
875 uint8_t inBuf
[ kDomainNameLengthMax
],
876 const uint8_t ** outNextPtr
);
878 DNSMessageExtractDomainNameString(
879 const void * inMsgPtr
,
881 const void * inNamePtr
,
882 char inBuf
[ kDNSServiceMaxDomainName
],
883 const uint8_t ** outNextPtr
);
885 DNSMessageExtractRecord(
886 const uint8_t * inMsgPtr
,
888 const uint8_t * inPtr
,
889 uint8_t inNameBuf
[ kDomainNameLengthMax
],
893 const uint8_t ** outRDataPtr
,
894 size_t * outRDataLen
,
895 const uint8_t ** outPtr
);
896 static OSStatus
DNSMessageGetAnswerSection( const uint8_t *inMsgPtr
, size_t inMsgLen
, const uint8_t **outPtr
);
898 DNSRecordDataToString(
899 const void * inRDataPtr
,
901 unsigned int inRDataType
,
902 const void * inMsgPtr
,
906 DomainNameAppendString(
907 uint8_t inDomainName
[ kDomainNameLengthMax
],
908 const char * inString
,
909 uint8_t ** outEndPtr
);
910 static Boolean
DomainNameEqual( const uint8_t *inName1
, const uint8_t *inName2
);
913 const uint8_t * inDomainName
,
914 const uint8_t * inEnd
,
915 char inBuf
[ kDNSServiceMaxDomainName
],
916 const uint8_t ** outNextPtr
);
918 static OSStatus
PrintDNSMessage( const uint8_t *inMsgPtr
, size_t inMsgLen
, Boolean inIsMDNS
, Boolean inPrintRaw
);
920 #define PrintMDNSMessage( MSGPTR, MSGLEN, RAW ) PrintDNSMessage( MSGPTR, MSGLEN, true, RAW )
921 #define PrintUDNSMessage( MSGPTR, MSGLEN, RAW ) PrintDNSMessage( MSGPTR, MSGLEN, false, RAW )
923 #define kDNSQueryMessageMaxLen ( kDNSHeaderLength + kDomainNameLengthMax + 4 )
926 WriteDNSQueryMessage(
927 uint8_t inMsg
[ kDNSQueryMessageMaxLen
],
930 const char * inQName
,
933 size_t * outMsgLen
);
937 typedef void ( *DispatchHandler
)( void *inContext
);
940 DispatchSignalSourceCreate(
942 DispatchHandler inEventHandler
,
944 dispatch_source_t
* outSource
);
946 DispatchReadSourceCreate(
948 DispatchHandler inEventHandler
,
949 DispatchHandler inCancelHandler
,
951 dispatch_source_t
* outSource
);
954 dispatch_time_t inStart
,
955 uint64_t inIntervalNs
,
957 DispatchHandler inEventHandler
,
958 DispatchHandler inCancelHandler
,
960 dispatch_source_t
* outTimer
);
962 static const char * ServiceTypeDescription( const char *inName
);
971 static void SocketContextCancelHandler( void *inContext
);
972 static OSStatus
StringToInt32( const char *inString
, int32_t *outValue
);
973 static OSStatus
StringToUInt32( const char *inString
, uint32_t *outValue
);
974 #if( TARGET_OS_DARWIN )
975 static OSStatus
GetDefaultDNSServer( sockaddr_ip
*outAddr
);
978 #define AddRmvString( X ) ( ( (X) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" )
979 #define Unused( X ) (void)(X)
981 //===========================================================================================================================
983 //===========================================================================================================================
985 int main( int argc
, const char **argv
)
987 // Route DebugServices logging output to stderr.
989 dlog_control( "DebugServices:output=file;stderr" );
991 CLIInit( argc
, argv
);
992 CLIParse( kGlobalOpts
, kCLIFlags_None
);
997 //===========================================================================================================================
998 // VersionOptionCallback
999 //===========================================================================================================================
1001 static OSStatus
VersionOptionCallback( CLIOption
*inOption
, const char *inArg
, int inUnset
)
1003 const char * srcVers
;
1004 #if( MDNSRESPONDER_PROJECT )
1012 #if( MDNSRESPONDER_PROJECT )
1013 srcVers
= SourceVersionToCString( _DNS_SD_H
, srcStr
);
1015 srcVers
= DNSSDUTIL_SOURCE_VERSION
;
1017 FPrintF( stdout
, "%s version %v (%s)\n", gProgramName
, kDNSSDUtilNumVersion
, srcVers
);
1019 return( kEndingErr
);
1022 //===========================================================================================================================
1024 //===========================================================================================================================
1026 typedef struct BrowseResolveOp BrowseResolveOp
;
1028 struct BrowseResolveOp
1030 BrowseResolveOp
* next
; // Next resolve operation in list.
1031 DNSServiceRef sdRef
; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
1032 char * fullName
; // Full name of the service to resolve.
1033 uint32_t interfaceIndex
; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
1038 DNSServiceRef mainRef
; // Main sdRef for shared connection.
1039 DNSServiceRef
* opRefs
; // Array of sdRefs for individual Browse operarions.
1040 size_t opRefsCount
; // Count of array of sdRefs for non-shared connections.
1041 const char * domain
; // Domain for DNSServiceBrowse operation(s).
1042 DNSServiceFlags flags
; // Flags for DNSServiceBrowse operation(s).
1043 char ** serviceTypes
; // Array of service types to browse for.
1044 size_t serviceTypesCount
; // Count of array of service types to browse for.
1045 int timeLimitSecs
; // Time limit of DNSServiceBrowse operation in seconds.
1046 BrowseResolveOp
* resolveList
; // List of resolve and/or TXT record query operations.
1047 uint32_t ifIndex
; // Interface index of DNSServiceBrowse operation(s).
1048 Boolean printedHeader
; // True if results header has been printed.
1049 Boolean doResolve
; // True if service instances are to be resolved.
1050 Boolean doResolveTXTOnly
; // True if TXT records of service instances are to be queried.
1054 static void BrowsePrintPrologue( const BrowseContext
*inContext
);
1055 static void BrowseContextFree( BrowseContext
*inContext
);
1056 static OSStatus
BrowseResolveOpCreate( const char *inFullName
, uint32_t inInterfaceIndex
, BrowseResolveOp
**outOp
);
1057 static void BrowseResolveOpFree( BrowseResolveOp
*inOp
);
1058 static void DNSSD_API
1060 DNSServiceRef inSDRef
,
1061 DNSServiceFlags inFlags
,
1062 uint32_t inInterfaceIndex
,
1063 DNSServiceErrorType inError
,
1064 const char * inName
,
1065 const char * inRegType
,
1066 const char * inDomain
,
1068 static void DNSSD_API
1069 BrowseResolveCallback(
1070 DNSServiceRef inSDRef
,
1071 DNSServiceFlags inFlags
,
1072 uint32_t inInterfaceIndex
,
1073 DNSServiceErrorType inError
,
1074 const char * inFullName
,
1075 const char * inHostname
,
1078 const unsigned char * inTXTPtr
,
1080 static void DNSSD_API
1081 BrowseQueryRecordCallback(
1082 DNSServiceRef inSDRef
,
1083 DNSServiceFlags inFlags
,
1084 uint32_t inInterfaceIndex
,
1085 DNSServiceErrorType inError
,
1086 const char * inFullName
,
1089 uint16_t inRDataLen
,
1090 const void * inRDataPtr
,
1094 static void BrowseCmd( void )
1098 BrowseContext
* context
= NULL
;
1099 dispatch_source_t signalSource
= NULL
;
1100 int useMainConnection
;
1102 // Set up SIGINT handler.
1104 signal( SIGINT
, SIG_IGN
);
1105 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
1106 require_noerr( err
, exit
);
1107 dispatch_resume( signalSource
);
1111 context
= (BrowseContext
*) calloc( 1, sizeof( *context
) );
1112 require_action( context
, exit
, err
= kNoMemoryErr
);
1114 context
->opRefs
= (DNSServiceRef
*) calloc( gBrowse_ServiceTypesCount
, sizeof( DNSServiceRef
) );
1115 require_action( context
->opRefs
, exit
, err
= kNoMemoryErr
);
1116 context
->opRefsCount
= gBrowse_ServiceTypesCount
;
1118 // Check command parameters.
1120 if( gBrowse_TimeLimitSecs
< 0 )
1122 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs
);
1127 // Create main connection.
1129 if( gConnectionOpt
)
1131 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
1132 require_noerr_quiet( err
, exit
);
1133 useMainConnection
= true;
1137 useMainConnection
= false;
1142 context
->flags
= GetDNSSDFlagsFromOpts();
1143 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
1147 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
1148 require_noerr_quiet( err
, exit
);
1150 // Set remaining parameters.
1152 context
->serviceTypes
= gBrowse_ServiceTypes
;
1153 context
->serviceTypesCount
= gBrowse_ServiceTypesCount
;
1154 context
->domain
= gBrowse_Domain
;
1155 context
->doResolve
= gBrowse_DoResolve
? true : false;
1156 context
->timeLimitSecs
= gBrowse_TimeLimitSecs
;
1157 context
->doResolveTXTOnly
= gBrowse_QueryTXT
? true : false;
1161 BrowsePrintPrologue( context
);
1163 // Start operation(s).
1165 for( i
= 0; i
< context
->serviceTypesCount
; ++i
)
1167 DNSServiceRef sdRef
;
1169 if( useMainConnection
) sdRef
= context
->mainRef
;
1170 err
= DNSServiceBrowse( &sdRef
, context
->flags
, context
->ifIndex
, context
->serviceTypes
[ i
], context
->domain
,
1171 BrowseCallback
, context
);
1172 require_noerr( err
, exit
);
1174 context
->opRefs
[ i
] = sdRef
;
1175 if( !useMainConnection
)
1177 err
= DNSServiceSetDispatchQueue( context
->opRefs
[ i
], dispatch_get_main_queue() );
1178 require_noerr( err
, exit
);
1184 if( context
->timeLimitSecs
> 0 )
1186 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
1187 kExitReason_TimeLimit
, Exit
);
1192 dispatch_source_forget( &signalSource
);
1193 if( context
) BrowseContextFree( context
);
1194 if( err
) exit( 1 );
1197 //===========================================================================================================================
1198 // BrowsePrintPrologue
1199 //===========================================================================================================================
1201 static void BrowsePrintPrologue( const BrowseContext
*inContext
)
1203 const int timeLimitSecs
= inContext
->timeLimitSecs
;
1204 const char * const * serviceType
= (const char **) inContext
->serviceTypes
;
1205 const char * const * const end
= (const char **) inContext
->serviceTypes
+ inContext
->serviceTypesCount
;
1206 char time
[ kTimestampBufLen
];
1207 char ifName
[ kInterfaceNameBufLen
];
1209 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
1211 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
1212 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
1213 FPrintF( stdout
, "Service types: %s", *serviceType
++ );
1214 while( serviceType
< end
) FPrintF( stdout
, ", %s", *serviceType
++ );
1215 FPrintF( stdout
, "\n" );
1216 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "<NULL> (default domains)" );
1217 FPrintF( stdout
, "Time limit: " );
1218 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
1219 else FPrintF( stdout
, "∞\n" );
1220 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
1221 FPrintF( stdout
, "---\n" );
1224 //===========================================================================================================================
1225 // BrowseContextFree
1226 //===========================================================================================================================
1228 static void BrowseContextFree( BrowseContext
*inContext
)
1232 for( i
= 0; i
< inContext
->opRefsCount
; ++i
)
1234 DNSServiceForget( &inContext
->opRefs
[ i
] );
1236 if( inContext
->serviceTypes
)
1238 StringArray_Free( inContext
->serviceTypes
, inContext
->serviceTypesCount
);
1239 inContext
->serviceTypes
= NULL
;
1240 inContext
->serviceTypesCount
= 0;
1242 DNSServiceForget( &inContext
->mainRef
);
1246 //===========================================================================================================================
1247 // BrowseResolveOpCreate
1248 //===========================================================================================================================
1250 static OSStatus
BrowseResolveOpCreate( const char *inFullName
, uint32_t inInterfaceIndex
, BrowseResolveOp
**outOp
)
1253 BrowseResolveOp
* resolveOp
;
1255 resolveOp
= (BrowseResolveOp
*) calloc( 1, sizeof( *resolveOp
) );
1256 require_action( resolveOp
, exit
, err
= kNoMemoryErr
);
1258 resolveOp
->fullName
= strdup( inFullName
);
1259 require_action( resolveOp
->fullName
, exit
, err
= kNoMemoryErr
);
1261 resolveOp
->interfaceIndex
= inInterfaceIndex
;
1268 if( resolveOp
) BrowseResolveOpFree( resolveOp
);
1272 //===========================================================================================================================
1273 // BrowseResolveOpFree
1274 //===========================================================================================================================
1276 static void BrowseResolveOpFree( BrowseResolveOp
*inOp
)
1278 DNSServiceForget( &inOp
->sdRef
);
1279 ForgetMem( &inOp
->fullName
);
1283 //===========================================================================================================================
1285 //===========================================================================================================================
1287 static void DNSSD_API
1289 DNSServiceRef inSDRef
,
1290 DNSServiceFlags inFlags
,
1291 uint32_t inInterfaceIndex
,
1292 DNSServiceErrorType inError
,
1293 const char * inName
,
1294 const char * inRegType
,
1295 const char * inDomain
,
1298 BrowseContext
* const context
= (BrowseContext
*) inContext
;
1300 BrowseResolveOp
* newOp
= NULL
;
1301 BrowseResolveOp
** p
;
1302 char fullName
[ kDNSServiceMaxDomainName
];
1303 char time
[ kTimestampBufLen
];
1307 GetTimestampStr( time
);
1310 require_noerr( err
, exit
);
1312 if( !context
->printedHeader
)
1314 FPrintF( stdout
, "%-26s A/R Flags IF %-20s %-20s Instance Name\n", "Timestamp", "Domain", "Service Type" );
1315 context
->printedHeader
= true;
1317 FPrintF( stdout
, "%-26s %-3s %5X %2d %-20s %-20s %s\n",
1318 time
, AddRmvString( inFlags
), inFlags
, (int32_t) inInterfaceIndex
, inDomain
, inRegType
, inName
);
1320 if( !context
->doResolve
&& !context
->doResolveTXTOnly
) goto exit
;
1322 err
= DNSServiceConstructFullName( fullName
, inName
, inRegType
, inDomain
);
1323 require_noerr( err
, exit
);
1325 if( inFlags
& kDNSServiceFlagsAdd
)
1327 DNSServiceRef sdRef
;
1328 DNSServiceFlags flags
;
1330 err
= BrowseResolveOpCreate( fullName
, inInterfaceIndex
, &newOp
);
1331 require_noerr( err
, exit
);
1333 if( context
->mainRef
)
1335 sdRef
= context
->mainRef
;
1336 flags
= kDNSServiceFlagsShareConnection
;
1342 if( context
->doResolve
)
1344 err
= DNSServiceResolve( &sdRef
, flags
, inInterfaceIndex
, inName
, inRegType
, inDomain
, BrowseResolveCallback
,
1346 require_noerr( err
, exit
);
1350 err
= DNSServiceQueryRecord( &sdRef
, flags
, inInterfaceIndex
, fullName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
,
1351 BrowseQueryRecordCallback
, NULL
);
1352 require_noerr( err
, exit
);
1355 newOp
->sdRef
= sdRef
;
1356 if( !context
->mainRef
)
1358 err
= DNSServiceSetDispatchQueue( newOp
->sdRef
, dispatch_get_main_queue() );
1359 require_noerr( err
, exit
);
1361 for( p
= &context
->resolveList
; *p
; p
= &( *p
)->next
) {}
1367 BrowseResolveOp
* resolveOp
;
1369 for( p
= &context
->resolveList
; ( resolveOp
= *p
) != NULL
; p
= &resolveOp
->next
)
1371 if( ( resolveOp
->interfaceIndex
== inInterfaceIndex
) && ( strcasecmp( resolveOp
->fullName
, fullName
) == 0 ) )
1378 *p
= resolveOp
->next
;
1379 BrowseResolveOpFree( resolveOp
);
1384 if( newOp
) BrowseResolveOpFree( newOp
);
1385 if( err
) exit( 1 );
1388 //===========================================================================================================================
1389 // BrowseQueryRecordCallback
1390 //===========================================================================================================================
1392 static void DNSSD_API
1393 BrowseQueryRecordCallback(
1394 DNSServiceRef inSDRef
,
1395 DNSServiceFlags inFlags
,
1396 uint32_t inInterfaceIndex
,
1397 DNSServiceErrorType inError
,
1398 const char * inFullName
,
1401 uint16_t inRDataLen
,
1402 const void * inRDataPtr
,
1407 char time
[ kTimestampBufLen
];
1412 Unused( inContext
);
1414 GetTimestampStr( time
);
1417 require_noerr( err
, exit
);
1418 require_action( inType
== kDNSServiceType_TXT
, exit
, err
= kTypeErr
);
1420 FPrintF( stdout
, "%s %s %s TXT on interface %d\n TXT: %#{txt}\n",
1421 time
, AddRmvString( inFlags
), inFullName
, (int32_t) inInterfaceIndex
, inRDataPtr
, (size_t) inRDataLen
);
1424 if( err
) exit( 1 );
1427 //===========================================================================================================================
1428 // BrowseResolveCallback
1429 //===========================================================================================================================
1431 static void DNSSD_API
1432 BrowseResolveCallback(
1433 DNSServiceRef inSDRef
,
1434 DNSServiceFlags inFlags
,
1435 uint32_t inInterfaceIndex
,
1436 DNSServiceErrorType inError
,
1437 const char * inFullName
,
1438 const char * inHostname
,
1441 const unsigned char * inTXTPtr
,
1444 char time
[ kTimestampBufLen
];
1445 char errorStr
[ 64 ];
1449 Unused( inContext
);
1451 GetTimestampStr( time
);
1453 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " error %#m", inError
);
1455 FPrintF( stdout
, "%s %s can be reached at %s:%u (interface %d)%?s\n",
1456 time
, inFullName
, inHostname
, ntohs( inPort
), (int32_t) inInterfaceIndex
, inError
, errorStr
);
1459 FPrintF( stdout
, " TXT record: %#H\n", inTXTPtr
, (int) inTXTLen
, INT_MAX
);
1463 FPrintF( stdout
, " TXT record: %#{txt}\n", inTXTPtr
, (size_t) inTXTLen
);
1467 //===========================================================================================================================
1469 //===========================================================================================================================
1473 DNSServiceRef mainRef
; // Main sdRef for shared connection.
1474 DNSServiceRef opRef
; // sdRef for the DNSServiceGetAddrInfo operation.
1475 const char * name
; // Hostname to resolve.
1476 DNSServiceFlags flags
; // Flags argument for DNSServiceGetAddrInfo().
1477 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceGetAddrInfo().
1478 uint32_t ifIndex
; // Interface index argument for DNSServiceGetAddrInfo().
1479 int timeLimitSecs
; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
1480 Boolean printedHeader
; // True if the results header has been printed.
1481 Boolean oneShotMode
; // True if command is done after the first set of results (one-shot mode).
1482 Boolean needIPv4
; // True if in one-shot mode and an IPv4 result is needed.
1483 Boolean needIPv6
; // True if in one-shot mode and an IPv6 result is needed.
1485 } GetAddrInfoContext
;
1487 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext
*inContext
);
1488 static void GetAddrInfoContextFree( GetAddrInfoContext
*inContext
);
1489 static void DNSSD_API
1490 GetAddrInfoCallback(
1491 DNSServiceRef inSDRef
,
1492 DNSServiceFlags inFlags
,
1493 uint32_t inInterfaceIndex
,
1494 DNSServiceErrorType inError
,
1495 const char * inHostname
,
1496 const struct sockaddr
* inSockAddr
,
1500 static void GetAddrInfoCmd( void )
1503 DNSServiceRef sdRef
;
1504 GetAddrInfoContext
* context
= NULL
;
1505 dispatch_source_t signalSource
= NULL
;
1506 int useMainConnection
;
1508 // Set up SIGINT handler.
1510 signal( SIGINT
, SIG_IGN
);
1511 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
1512 require_noerr( err
, exit
);
1513 dispatch_resume( signalSource
);
1515 // Check command parameters.
1517 if( gGetAddrInfo_TimeLimitSecs
< 0 )
1519 FPrintF( stderr
, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs
);
1526 context
= (GetAddrInfoContext
*) calloc( 1, sizeof( *context
) );
1527 require_action( context
, exit
, err
= kNoMemoryErr
);
1529 // Create main connection.
1531 if( gConnectionOpt
)
1533 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
1534 require_noerr_quiet( err
, exit
);
1535 useMainConnection
= true;
1539 useMainConnection
= false;
1544 context
->flags
= GetDNSSDFlagsFromOpts();
1545 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
1549 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
1550 require_noerr_quiet( err
, exit
);
1552 // Set remaining parameters.
1554 context
->name
= gGetAddrInfo_Name
;
1555 context
->timeLimitSecs
= gGetAddrInfo_TimeLimitSecs
;
1556 if( gGetAddrInfo_ProtocolIPv4
) context
->protocols
|= kDNSServiceProtocol_IPv4
;
1557 if( gGetAddrInfo_ProtocolIPv6
) context
->protocols
|= kDNSServiceProtocol_IPv6
;
1558 if( gGetAddrInfo_OneShot
)
1560 context
->oneShotMode
= true;
1561 context
->needIPv4
= ( gGetAddrInfo_ProtocolIPv4
|| !gGetAddrInfo_ProtocolIPv6
) ? true : false;
1562 context
->needIPv6
= ( gGetAddrInfo_ProtocolIPv6
|| !gGetAddrInfo_ProtocolIPv4
) ? true : false;
1567 GetAddrInfoPrintPrologue( context
);
1571 if( useMainConnection
) sdRef
= context
->mainRef
;
1572 err
= DNSServiceGetAddrInfo( &sdRef
, context
->flags
, context
->ifIndex
, context
->protocols
, context
->name
,
1573 GetAddrInfoCallback
, context
);
1574 require_noerr( err
, exit
);
1576 context
->opRef
= sdRef
;
1577 if( !useMainConnection
)
1579 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
1580 require_noerr( err
, exit
);
1585 if( context
->timeLimitSecs
> 0 )
1587 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
1588 kExitReason_TimeLimit
, Exit
);
1593 dispatch_source_forget( &signalSource
);
1594 if( context
) GetAddrInfoContextFree( context
);
1595 if( err
) exit( 1 );
1598 //===========================================================================================================================
1599 // GetAddrInfoPrintPrologue
1600 //===========================================================================================================================
1602 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext
*inContext
)
1604 const int timeLimitSecs
= inContext
->timeLimitSecs
;
1605 char ifName
[ kInterfaceNameBufLen
];
1606 char time
[ kTimestampBufLen
];
1608 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
1610 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
1611 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
1612 FPrintF( stdout
, "Protocols: %#{flags}\n", inContext
->protocols
, kDNSServiceProtocolDescriptors
);
1613 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
1614 FPrintF( stdout
, "Mode: %s\n", inContext
->oneShotMode
? "one-shot" : "continuous" );
1615 FPrintF( stdout
, "Time limit: " );
1616 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
1617 else FPrintF( stdout
, "∞\n" );
1618 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
1619 FPrintF( stdout
, "---\n" );
1622 //===========================================================================================================================
1623 // GetAddrInfoContextFree
1624 //===========================================================================================================================
1626 static void GetAddrInfoContextFree( GetAddrInfoContext
*inContext
)
1628 DNSServiceForget( &inContext
->opRef
);
1629 DNSServiceForget( &inContext
->mainRef
);
1633 //===========================================================================================================================
1634 // GetAddrInfoCallback
1635 //===========================================================================================================================
1637 static void DNSSD_API
1638 GetAddrInfoCallback(
1639 DNSServiceRef inSDRef
,
1640 DNSServiceFlags inFlags
,
1641 uint32_t inInterfaceIndex
,
1642 DNSServiceErrorType inError
,
1643 const char * inHostname
,
1644 const struct sockaddr
* inSockAddr
,
1648 GetAddrInfoContext
* const context
= (GetAddrInfoContext
*) inContext
;
1650 const char * addrStr
;
1651 char addrStrBuf
[ kSockAddrStringMaxSize
];
1652 char time
[ kTimestampBufLen
];
1656 GetTimestampStr( time
);
1660 case kDNSServiceErr_NoError
:
1661 case kDNSServiceErr_NoSuchRecord
:
1665 case kDNSServiceErr_Timeout
:
1666 Exit( kExitReason_Timeout
);
1673 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
1675 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
1682 err
= SockAddrToString( inSockAddr
, kSockAddrStringFlagsNone
, addrStrBuf
);
1683 require_noerr( err
, exit
);
1684 addrStr
= addrStrBuf
;
1688 addrStr
= ( inSockAddr
->sa_family
== AF_INET
) ? "No Such Record (A)" : "No Such Record (AAAA)";
1691 if( !context
->printedHeader
)
1693 FPrintF( stdout
, "%-26s A/R Flags IF %-32s %-38s %6s\n", "Timestamp", "Hostname", "Address", "TTL" );
1694 context
->printedHeader
= true;
1696 FPrintF( stdout
, "%-26s %s %5X %2d %-32s %-38s %6u\n",
1697 time
, AddRmvString( inFlags
), inFlags
, (int32_t) inInterfaceIndex
, inHostname
, addrStr
, inTTL
);
1699 if( context
->oneShotMode
)
1701 if( inFlags
& kDNSServiceFlagsAdd
)
1703 if( inSockAddr
->sa_family
== AF_INET
) context
->needIPv4
= false;
1704 else context
->needIPv6
= false;
1706 if( !( inFlags
& kDNSServiceFlagsMoreComing
) && !context
->needIPv4
&& !context
->needIPv6
)
1708 Exit( kExitReason_OneShotDone
);
1713 if( err
) exit( 1 );
1716 //===========================================================================================================================
1718 //===========================================================================================================================
1722 DNSServiceRef mainRef
; // Main sdRef for shared connection.
1723 DNSServiceRef opRef
; // sdRef for the DNSServiceQueryRecord operation.
1724 const char * recordName
; // Resource record name argument for DNSServiceQueryRecord().
1725 DNSServiceFlags flags
; // Flags argument for DNSServiceQueryRecord().
1726 uint32_t ifIndex
; // Interface index argument for DNSServiceQueryRecord().
1727 int timeLimitSecs
; // Time limit for the DNSServiceQueryRecord() operation in seconds.
1728 uint16_t recordType
; // Resource record type argument for DNSServiceQueryRecord().
1729 Boolean printedHeader
; // True if the results header was printed.
1730 Boolean oneShotMode
; // True if command is done after the first set of results (one-shot mode).
1731 Boolean gotRecord
; // True if in one-shot mode and received at least one record of the desired type.
1732 Boolean printRawRData
; // True if RDATA results are not to be formatted when printed.
1734 } QueryRecordContext
;
1736 static void QueryRecordPrintPrologue( const QueryRecordContext
*inContext
);
1737 static void QueryRecordContextFree( QueryRecordContext
*inContext
);
1738 static void DNSSD_API
1739 QueryRecordCallback(
1740 DNSServiceRef inSDRef
,
1741 DNSServiceFlags inFlags
,
1742 uint32_t inInterfaceIndex
,
1743 DNSServiceErrorType inError
,
1744 const char * inFullName
,
1747 uint16_t inRDataLen
,
1748 const void * inRDataPtr
,
1752 static void QueryRecordCmd( void )
1755 DNSServiceRef sdRef
;
1756 QueryRecordContext
* context
= NULL
;
1757 dispatch_source_t signalSource
= NULL
;
1758 int useMainConnection
;
1760 // Set up SIGINT handler.
1762 signal( SIGINT
, SIG_IGN
);
1763 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
1764 require_noerr( err
, exit
);
1765 dispatch_resume( signalSource
);
1769 context
= (QueryRecordContext
*) calloc( 1, sizeof( *context
) );
1770 require_action( context
, exit
, err
= kNoMemoryErr
);
1772 // Check command parameters.
1774 if( gQueryRecord_TimeLimitSecs
< 0 )
1776 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs
);
1781 // Create main connection.
1783 if( gConnectionOpt
)
1785 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
1786 require_noerr_quiet( err
, exit
);
1787 useMainConnection
= true;
1791 useMainConnection
= false;
1796 context
->flags
= GetDNSSDFlagsFromOpts();
1797 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
1801 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
1802 require_noerr_quiet( err
, exit
);
1806 err
= RecordTypeFromArgString( gQueryRecord_Type
, &context
->recordType
);
1807 require_noerr( err
, exit
);
1809 // Set remaining parameters.
1811 context
->recordName
= gQueryRecord_Name
;
1812 context
->timeLimitSecs
= gQueryRecord_TimeLimitSecs
;
1813 context
->oneShotMode
= gQueryRecord_OneShot
? true : false;
1814 context
->printRawRData
= gQueryRecord_RawRData
? true : false;
1818 QueryRecordPrintPrologue( context
);
1822 if( useMainConnection
) sdRef
= context
->mainRef
;
1823 err
= DNSServiceQueryRecord( &sdRef
, context
->flags
, context
->ifIndex
, context
->recordName
, context
->recordType
,
1824 kDNSServiceClass_IN
, QueryRecordCallback
, context
);
1825 require_noerr( err
, exit
);
1827 context
->opRef
= sdRef
;
1828 if( !useMainConnection
)
1830 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
1831 require_noerr( err
, exit
);
1836 if( context
->timeLimitSecs
> 0 )
1838 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_TimeLimit
,
1844 dispatch_source_forget( &signalSource
);
1845 if( context
) QueryRecordContextFree( context
);
1846 if( err
) exit( 1 );
1849 //===========================================================================================================================
1850 // QueryRecordContextFree
1851 //===========================================================================================================================
1853 static void QueryRecordContextFree( QueryRecordContext
*inContext
)
1855 DNSServiceForget( &inContext
->opRef
);
1856 DNSServiceForget( &inContext
->mainRef
);
1860 //===========================================================================================================================
1861 // QueryRecordPrintPrologue
1862 //===========================================================================================================================
1864 static void QueryRecordPrintPrologue( const QueryRecordContext
*inContext
)
1866 const int timeLimitSecs
= inContext
->timeLimitSecs
;
1867 char ifName
[ kInterfaceNameBufLen
];
1868 char time
[ kTimestampBufLen
];
1870 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
1872 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
1873 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
1874 FPrintF( stdout
, "Name: %s\n", inContext
->recordName
);
1875 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->recordType
), inContext
->recordType
);
1876 FPrintF( stdout
, "Mode: %s\n", inContext
->oneShotMode
? "one-shot" : "continuous" );
1877 FPrintF( stdout
, "Time limit: " );
1878 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
1879 else FPrintF( stdout
, "∞\n" );
1880 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
1881 FPrintF( stdout
, "---\n" );
1885 //===========================================================================================================================
1886 // QueryRecordCallback
1887 //===========================================================================================================================
1889 static void DNSSD_API
1890 QueryRecordCallback(
1891 DNSServiceRef inSDRef
,
1892 DNSServiceFlags inFlags
,
1893 uint32_t inInterfaceIndex
,
1894 DNSServiceErrorType inError
,
1895 const char * inFullName
,
1898 uint16_t inRDataLen
,
1899 const void * inRDataPtr
,
1903 QueryRecordContext
* const context
= (QueryRecordContext
*) inContext
;
1905 char * rdataStr
= NULL
;
1906 char time
[ kTimestampBufLen
];
1910 GetTimestampStr( time
);
1914 case kDNSServiceErr_NoError
:
1915 case kDNSServiceErr_NoSuchRecord
:
1919 case kDNSServiceErr_Timeout
:
1920 Exit( kExitReason_Timeout
);
1927 if( inError
== kDNSServiceErr_NoSuchRecord
)
1929 ASPrintF( &rdataStr
, "No Such Record" );
1933 if( !context
->printRawRData
) DNSRecordDataToString( inRDataPtr
, inRDataLen
, inType
, NULL
, 0, &rdataStr
);
1936 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, INT_MAX
);
1937 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
1941 if( !context
->printedHeader
)
1943 FPrintF( stdout
, "%-26s A/R Flags IF %-32s %-5s %-5s %6s RData\n", "Timestamp", "Name", "Type", "Class", "TTL" );
1944 context
->printedHeader
= true;
1946 FPrintF( stdout
, "%-26s %-3s %5X %2d %-32s %-5s %?-5s%?5u %6u %s\n",
1947 time
, AddRmvString( inFlags
), inFlags
, (int32_t) inInterfaceIndex
, inFullName
, RecordTypeToString( inType
),
1948 ( inClass
== kDNSServiceClass_IN
), "IN", ( inClass
!= kDNSServiceClass_IN
), inClass
, inTTL
, rdataStr
);
1950 if( context
->oneShotMode
)
1952 if( ( inFlags
& kDNSServiceFlagsAdd
) &&
1953 ( ( context
->recordType
== kDNSServiceType_ANY
) || ( context
->recordType
== inType
) ) )
1955 context
->gotRecord
= true;
1957 if( !( inFlags
& kDNSServiceFlagsMoreComing
) && context
->gotRecord
) Exit( kExitReason_OneShotDone
);
1961 FreeNullSafe( rdataStr
);
1962 if( err
) exit( 1 );
1965 //===========================================================================================================================
1967 //===========================================================================================================================
1971 DNSRecordRef recordRef
; // Reference returned by DNSServiceAddRecord().
1972 uint8_t * dataPtr
; // Record data.
1973 size_t dataLen
; // Record data length.
1974 uint32_t ttl
; // Record TTL value.
1975 uint16_t type
; // Record type.
1981 DNSServiceRef opRef
; // sdRef for DNSServiceRegister operation.
1982 const char * name
; // Service name argument for DNSServiceRegister().
1983 const char * type
; // Service type argument for DNSServiceRegister().
1984 const char * domain
; // Domain in which advertise the service.
1985 uint8_t * txtPtr
; // Service TXT record data. (malloc'd)
1986 size_t txtLen
; // Service TXT record data len.
1987 ExtraRecord
* extraRecords
; // Array of extra records to add to registered service.
1988 size_t extraRecordsCount
; // Number of extra records.
1989 uint8_t * updateTXTPtr
; // Pointer to record data for TXT record update. (malloc'd)
1990 size_t updateTXTLen
; // Length of record data for TXT record update.
1991 uint32_t updateTTL
; // TTL of updated TXT record.
1992 int updateDelayMs
; // Post-registration TXT record update delay in milliseconds.
1993 DNSServiceFlags flags
; // Flags argument for DNSServiceRegister().
1994 uint32_t ifIndex
; // Interface index argument for DNSServiceRegister().
1995 int lifetimeMs
; // Lifetime of the record registration in milliseconds.
1996 uint16_t port
; // Service instance's port number.
1997 Boolean printedHeader
; // True if results header was printed.
1998 Boolean didRegister
; // True if service was registered.
2002 static void RegisterPrintPrologue( const RegisterContext
*inContext
);
2003 static void RegisterContextFree( RegisterContext
*inContext
);
2004 static void DNSSD_API
2006 DNSServiceRef inSDRef
,
2007 DNSServiceFlags inFlags
,
2008 DNSServiceErrorType inError
,
2009 const char * inName
,
2010 const char * inType
,
2011 const char * inDomain
,
2013 static void RegisterUpdate( void *inContext
);
2015 static void RegisterCmd( void )
2018 RegisterContext
* context
= NULL
;
2019 dispatch_source_t signalSource
= NULL
;
2021 // Set up SIGINT handler.
2023 signal( SIGINT
, SIG_IGN
);
2024 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
2025 require_noerr( err
, exit
);
2026 dispatch_resume( signalSource
);
2030 context
= (RegisterContext
*) calloc( 1, sizeof( *context
) );
2031 require_action( context
, exit
, err
= kNoMemoryErr
);
2033 // Check command parameters.
2035 if( ( gRegister_Port
< 0 ) || ( gRegister_Port
> UINT16_MAX
) )
2037 FPrintF( stderr
, "Port number %d is out-of-range.\n", gRegister_Port
);
2042 if( ( gAddRecord_DataCount
!= gAddRecord_TypesCount
) || ( gAddRecord_TTLsCount
!= gAddRecord_TypesCount
) )
2044 FPrintF( stderr
, "There are missing additional record parameters.\n" );
2051 context
->flags
= GetDNSSDFlagsFromOpts();
2053 // Get interface index.
2055 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
2056 require_noerr_quiet( err
, exit
);
2058 // Get TXT record data.
2062 err
= RecordDataFromArgString( gRegister_TXT
, &context
->txtPtr
, &context
->txtLen
);
2063 require_noerr_quiet( err
, exit
);
2066 // Set remaining parameters.
2068 context
->name
= gRegister_Name
;
2069 context
->type
= gRegister_Type
;
2070 context
->domain
= gRegister_Domain
;
2071 context
->port
= (uint16_t) gRegister_Port
;
2072 context
->lifetimeMs
= gRegister_LifetimeMs
;
2074 if( gAddRecord_TypesCount
> 0 )
2078 context
->extraRecords
= (ExtraRecord
*) calloc( gAddRecord_TypesCount
, sizeof( ExtraRecord
) );
2079 require_action( context
, exit
, err
= kNoMemoryErr
);
2080 context
->extraRecordsCount
= gAddRecord_TypesCount
;
2082 for( i
= 0; i
< gAddRecord_TypesCount
; ++i
)
2084 ExtraRecord
* const extraRecord
= &context
->extraRecords
[ i
];
2086 err
= RecordTypeFromArgString( gAddRecord_Types
[ i
], &extraRecord
->type
);
2087 require_noerr( err
, exit
);
2089 err
= StringToUInt32( gAddRecord_TTLs
[ i
], &extraRecord
->ttl
);
2092 FPrintF( stderr
, "Invalid TTL value: %s\n", gAddRecord_TTLs
[ i
] );
2097 err
= RecordDataFromArgString( gAddRecord_Data
[ i
], &extraRecord
->dataPtr
, &extraRecord
->dataLen
);
2098 require_noerr_quiet( err
, exit
);
2102 if( gUpdateRecord_Data
)
2104 err
= RecordDataFromArgString( gUpdateRecord_Data
, &context
->updateTXTPtr
, &context
->updateTXTLen
);
2105 require_noerr_quiet( err
, exit
);
2107 context
->updateTTL
= (uint32_t) gUpdateRecord_TTL
;
2108 context
->updateDelayMs
= gUpdateRecord_DelayMs
;
2113 RegisterPrintPrologue( context
);
2117 err
= DNSServiceRegister( &context
->opRef
, context
->flags
, context
->ifIndex
, context
->name
, context
->type
,
2118 context
->domain
, NULL
, ntohs( context
->port
), (uint16_t) context
->txtLen
, context
->txtPtr
,
2119 RegisterCallback
, context
);
2120 ForgetMem( &context
->txtPtr
);
2121 require_noerr( err
, exit
);
2123 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
2124 require_noerr( err
, exit
);
2129 dispatch_source_forget( &signalSource
);
2130 if( context
) RegisterContextFree( context
);
2131 if( err
) exit( 1 );
2134 //===========================================================================================================================
2135 // RegisterPrintPrologue
2136 //===========================================================================================================================
2138 static void RegisterPrintPrologue( const RegisterContext
*inContext
)
2142 char ifName
[ kInterfaceNameBufLen
];
2143 char time
[ kTimestampBufLen
];
2145 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
2147 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
2148 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
2149 FPrintF( stdout
, "Name: %s\n", inContext
->name
? inContext
->name
: "<NULL>" );
2150 FPrintF( stdout
, "Type: %s\n", inContext
->type
);
2151 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "<NULL> (default domains)" );
2152 FPrintF( stdout
, "Port: %u\n", inContext
->port
);
2153 FPrintF( stdout
, "TXT data: %#{txt}\n", inContext
->txtPtr
, inContext
->txtLen
);
2154 infinite
= ( inContext
->lifetimeMs
< 0 ) ? true : false;
2155 FPrintF( stdout
, "Lifetime: %?s%?d ms\n", infinite
, "∞", !infinite
, inContext
->lifetimeMs
);
2156 if( inContext
->updateTXTPtr
)
2158 FPrintF( stdout
, "\nUpdate record:\n" );
2159 FPrintF( stdout
, " Delay: %d ms\n", ( inContext
->updateDelayMs
> 0 ) ? inContext
->updateDelayMs
: 0 );
2160 FPrintF( stdout
, " TTL: %u%?s\n",
2161 inContext
->updateTTL
, inContext
->updateTTL
== 0, " (system will use a default value.)" );
2162 FPrintF( stdout
, " TXT data: %#{txt}\n", inContext
->updateTXTPtr
, inContext
->updateTXTLen
);
2164 if( inContext
->extraRecordsCount
> 0 ) FPrintF( stdout
, "\n" );
2165 for( i
= 0; i
< inContext
->extraRecordsCount
; ++i
)
2167 const ExtraRecord
* record
= &inContext
->extraRecords
[ i
];
2169 FPrintF( stdout
, "Extra record %zu:\n", i
+ 1 );
2170 FPrintF( stdout
, " Type: %s (%u)\n", RecordTypeToString( record
->type
), record
->type
);
2171 FPrintF( stdout
, " TTL: %u%?s\n", record
->ttl
, record
->ttl
== 0, " (system will use a default value.)" );
2172 FPrintF( stdout
, " RData: %#H\n\n", record
->dataPtr
, (int) record
->dataLen
, INT_MAX
);
2174 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
2175 FPrintF( stdout
, "---\n" );
2178 //===========================================================================================================================
2179 // RegisterContextFree
2180 //===========================================================================================================================
2182 static void RegisterContextFree( RegisterContext
*inContext
)
2184 ExtraRecord
* record
;
2185 const ExtraRecord
* const end
= inContext
->extraRecords
+ inContext
->extraRecordsCount
;
2187 DNSServiceForget( &inContext
->opRef
);
2188 ForgetMem( &inContext
->txtPtr
);
2189 for( record
= inContext
->extraRecords
; record
< end
; ++record
)
2191 check( !record
->recordRef
);
2192 ForgetMem( &record
->dataPtr
);
2194 ForgetMem( &inContext
->extraRecords
);
2195 ForgetMem( &inContext
->updateTXTPtr
);
2199 //===========================================================================================================================
2201 //===========================================================================================================================
2203 static void DNSSD_API
2205 DNSServiceRef inSDRef
,
2206 DNSServiceFlags inFlags
,
2207 DNSServiceErrorType inError
,
2208 const char * inName
,
2209 const char * inType
,
2210 const char * inDomain
,
2213 RegisterContext
* const context
= (RegisterContext
*) inContext
;
2215 char time
[ kTimestampBufLen
];
2219 GetTimestampStr( time
);
2221 if( !context
->printedHeader
)
2223 FPrintF( stdout
, "%-26s A/R Flags Service\n", "Timestamp" );
2224 context
->printedHeader
= true;
2226 FPrintF( stdout
, "%-26s %-3s %5X %s.%s%s %?#m\n",
2227 time
, AddRmvString( inFlags
), inFlags
, inName
, inType
, inDomain
, inError
, inError
);
2229 require_noerr_action_quiet( inError
, exit
, err
= inError
);
2231 if( !context
->didRegister
&& ( inFlags
& kDNSServiceFlagsAdd
) )
2233 context
->didRegister
= true;
2234 if( context
->updateTXTPtr
)
2236 if( context
->updateDelayMs
> 0 )
2238 dispatch_after_f( dispatch_time_milliseconds( context
->updateDelayMs
), dispatch_get_main_queue(),
2239 context
, RegisterUpdate
);
2243 RegisterUpdate( context
);
2246 if( context
->extraRecordsCount
> 0 )
2248 ExtraRecord
* record
;
2249 const ExtraRecord
* const end
= context
->extraRecords
+ context
->extraRecordsCount
;
2251 for( record
= context
->extraRecords
; record
< end
; ++record
)
2253 err
= DNSServiceAddRecord( context
->opRef
, &record
->recordRef
, 0, record
->type
,
2254 (uint16_t) record
->dataLen
, record
->dataPtr
, record
->ttl
);
2255 require_noerr( err
, exit
);
2258 if( context
->lifetimeMs
== 0 )
2260 Exit( kExitReason_TimeLimit
);
2262 else if( context
->lifetimeMs
> 0 )
2264 dispatch_after_f( dispatch_time_milliseconds( context
->lifetimeMs
), dispatch_get_main_queue(),
2265 kExitReason_TimeLimit
, Exit
);
2271 if( err
) exit( 1 );
2274 //===========================================================================================================================
2276 //===========================================================================================================================
2278 static void RegisterUpdate( void *inContext
)
2281 RegisterContext
* const context
= (RegisterContext
*) inContext
;
2283 err
= DNSServiceUpdateRecord( context
->opRef
, NULL
, 0, (uint16_t) context
->updateTXTLen
, context
->updateTXTPtr
,
2284 context
->updateTTL
);
2285 require_noerr( err
, exit
);
2288 if( err
) exit( 1 );
2291 //===========================================================================================================================
2292 // RegisterRecordCmd
2293 //===========================================================================================================================
2297 DNSServiceRef conRef
; // sdRef to be initialized by DNSServiceCreateConnection().
2298 DNSRecordRef recordRef
; // Registered record reference.
2299 const char * recordName
; // Name of resource record.
2300 uint8_t * dataPtr
; // Pointer to resource record data.
2301 size_t dataLen
; // Length of resource record data.
2302 uint32_t ttl
; // TTL value of resource record in seconds.
2303 uint32_t ifIndex
; // Interface index argument for DNSServiceRegisterRecord().
2304 DNSServiceFlags flags
; // Flags argument for DNSServiceRegisterRecord().
2305 int lifetimeMs
; // Lifetime of the record registration in milliseconds.
2306 uint16_t recordType
; // Resource record type.
2307 uint8_t * updateDataPtr
; // Pointer to data for record update. (malloc'd)
2308 size_t updateDataLen
; // Length of data for record update.
2309 uint32_t updateTTL
; // TTL for updated record.
2310 int updateDelayMs
; // Post-registration record update delay in milliseconds.
2311 Boolean didRegister
; // True if the record was registered.
2313 } RegisterRecordContext
;
2315 static void RegisterRecordPrintPrologue( const RegisterRecordContext
*inContext
);
2316 static void RegisterRecordContextFree( RegisterRecordContext
*inContext
);
2317 static void DNSSD_API
2318 RegisterRecordCallback(
2319 DNSServiceRef inSDRef
,
2320 DNSRecordRef inRecordRef
,
2321 DNSServiceFlags inFlags
,
2322 DNSServiceErrorType inError
,
2324 static void RegisterRecordUpdate( void *inContext
);
2326 static void RegisterRecordCmd( void )
2329 RegisterRecordContext
* context
= NULL
;
2330 dispatch_source_t signalSource
= NULL
;
2332 // Set up SIGINT handler.
2334 signal( SIGINT
, SIG_IGN
);
2335 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
2336 require_noerr( err
, exit
);
2337 dispatch_resume( signalSource
);
2341 context
= (RegisterRecordContext
*) calloc( 1, sizeof( *context
) );
2342 require_action( context
, exit
, err
= kNoMemoryErr
);
2344 // Create connection.
2346 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->conRef
, NULL
);
2347 require_noerr_quiet( err
, exit
);
2351 context
->flags
= GetDNSSDFlagsFromOpts();
2355 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
2356 require_noerr_quiet( err
, exit
);
2360 err
= RecordTypeFromArgString( gRegisterRecord_Type
, &context
->recordType
);
2361 require_noerr( err
, exit
);
2365 if( gRegisterRecord_Data
)
2367 err
= RecordDataFromArgString( gRegisterRecord_Data
, &context
->dataPtr
, &context
->dataLen
);
2368 require_noerr_quiet( err
, exit
);
2371 // Set remaining parameters.
2373 context
->recordName
= gRegisterRecord_Name
;
2374 context
->ttl
= (uint32_t) gRegisterRecord_TTL
;
2375 context
->lifetimeMs
= gRegisterRecord_LifetimeMs
;
2379 if( gRegisterRecord_UpdateData
)
2381 err
= RecordDataFromArgString( gRegisterRecord_UpdateData
, &context
->updateDataPtr
, &context
->updateDataLen
);
2382 require_noerr_quiet( err
, exit
);
2384 context
->updateTTL
= (uint32_t) gRegisterRecord_UpdateTTL
;
2385 context
->updateDelayMs
= gRegisterRecord_UpdateDelayMs
;
2390 RegisterRecordPrintPrologue( context
);
2394 err
= DNSServiceRegisterRecord( context
->conRef
, &context
->recordRef
, context
->flags
, context
->ifIndex
,
2395 context
->recordName
, context
->recordType
, kDNSServiceClass_IN
, (uint16_t) context
->dataLen
, context
->dataPtr
,
2396 context
->ttl
, RegisterRecordCallback
, context
);
2399 FPrintF( stderr
, "DNSServiceRegisterRecord() returned %#m\n", err
);
2406 dispatch_source_forget( &signalSource
);
2407 if( context
) RegisterRecordContextFree( context
);
2408 if( err
) exit( 1 );
2411 //===========================================================================================================================
2412 // RegisterRecordPrintPrologue
2413 //===========================================================================================================================
2415 static void RegisterRecordPrintPrologue( const RegisterRecordContext
*inContext
)
2418 char time
[ kTimestampBufLen
];
2419 char ifName
[ kInterfaceNameBufLen
];
2421 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
2423 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
2424 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
2425 FPrintF( stdout
, "Name: %s\n", inContext
->recordName
);
2426 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->recordType
), inContext
->recordType
);
2427 FPrintF( stdout
, "TTL: %u\n", inContext
->ttl
);
2428 FPrintF( stdout
, "Data: %#H\n", inContext
->dataPtr
, (int) inContext
->dataLen
, INT_MAX
);
2429 infinite
= ( inContext
->lifetimeMs
< 0 ) ? true : false;
2430 FPrintF( stdout
, "Lifetime: %?s%?d ms\n", infinite
, "∞", !infinite
, inContext
->lifetimeMs
);
2431 if( inContext
->updateDataPtr
)
2433 FPrintF( stdout
, "\nUpdate record:\n" );
2434 FPrintF( stdout
, " Delay: %d ms\n", ( inContext
->updateDelayMs
>= 0 ) ? inContext
->updateDelayMs
: 0 );
2435 FPrintF( stdout
, " TTL: %u%?s\n",
2436 inContext
->updateTTL
, inContext
->updateTTL
== 0, " (system will use a default value.)" );
2437 FPrintF( stdout
, " RData: %#H\n", inContext
->updateDataPtr
, (int) inContext
->updateDataLen
, INT_MAX
);
2439 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
2440 FPrintF( stdout
, "---\n" );
2443 //===========================================================================================================================
2444 // RegisterRecordContextFree
2445 //===========================================================================================================================
2447 static void RegisterRecordContextFree( RegisterRecordContext
*inContext
)
2449 DNSServiceForget( &inContext
->conRef
);
2450 ForgetMem( &inContext
->dataPtr
);
2451 ForgetMem( &inContext
->updateDataPtr
);
2455 //===========================================================================================================================
2456 // RegisterRecordCallback
2457 //===========================================================================================================================
2460 RegisterRecordCallback(
2461 DNSServiceRef inSDRef
,
2462 DNSRecordRef inRecordRef
,
2463 DNSServiceFlags inFlags
,
2464 DNSServiceErrorType inError
,
2467 RegisterRecordContext
* context
= (RegisterRecordContext
*) inContext
;
2468 char time
[ kTimestampBufLen
];
2471 Unused( inRecordRef
);
2475 GetTimestampStr( time
);
2476 FPrintF( stdout
, "%s Record registration result (error %#m)\n", time
, inError
);
2478 if( !context
->didRegister
&& !inError
)
2480 context
->didRegister
= true;
2481 if( context
->updateDataPtr
)
2483 if( context
->updateDelayMs
> 0 )
2485 dispatch_after_f( dispatch_time_milliseconds( context
->updateDelayMs
), dispatch_get_main_queue(),
2486 context
, RegisterRecordUpdate
);
2490 RegisterRecordUpdate( context
);
2493 if( context
->lifetimeMs
== 0 )
2495 Exit( kExitReason_TimeLimit
);
2497 else if( context
->lifetimeMs
> 0 )
2499 dispatch_after_f( dispatch_time_milliseconds( context
->lifetimeMs
), dispatch_get_main_queue(),
2500 kExitReason_TimeLimit
, Exit
);
2505 //===========================================================================================================================
2506 // RegisterRecordUpdate
2507 //===========================================================================================================================
2509 static void RegisterRecordUpdate( void *inContext
)
2512 RegisterRecordContext
* const context
= (RegisterRecordContext
*) inContext
;
2514 err
= DNSServiceUpdateRecord( context
->conRef
, context
->recordRef
, 0, (uint16_t) context
->updateDataLen
,
2515 context
->updateDataPtr
, context
->updateTTL
);
2516 require_noerr( err
, exit
);
2519 if( err
) exit( 1 );
2522 //===========================================================================================================================
2524 //===========================================================================================================================
2528 DNSServiceRef mainRef
; // Main sdRef for shared connections.
2529 DNSServiceRef opRef
; // sdRef for the DNSServiceResolve operation.
2530 DNSServiceFlags flags
; // Flags argument for DNSServiceResolve().
2531 const char * name
; // Service name argument for DNSServiceResolve().
2532 const char * type
; // Service type argument for DNSServiceResolve().
2533 const char * domain
; // Domain argument for DNSServiceResolve().
2534 uint32_t ifIndex
; // Interface index argument for DNSServiceResolve().
2535 int timeLimitSecs
; // Time limit for the DNSServiceResolve operation in seconds.
2539 static void ResolvePrintPrologue( const ResolveContext
*inContext
);
2540 static void ResolveContextFree( ResolveContext
*inContext
);
2541 static void DNSSD_API
2543 DNSServiceRef inSDRef
,
2544 DNSServiceFlags inFlags
,
2545 uint32_t inInterfaceIndex
,
2546 DNSServiceErrorType inError
,
2547 const char * inFullName
,
2548 const char * inHostname
,
2551 const unsigned char * inTXTPtr
,
2554 static void ResolveCmd( void )
2557 DNSServiceRef sdRef
;
2558 ResolveContext
* context
= NULL
;
2559 dispatch_source_t signalSource
= NULL
;
2560 int useMainConnection
;
2562 // Set up SIGINT handler.
2564 signal( SIGINT
, SIG_IGN
);
2565 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
2566 require_noerr( err
, exit
);
2567 dispatch_resume( signalSource
);
2571 context
= (ResolveContext
*) calloc( 1, sizeof( *context
) );
2572 require_action( context
, exit
, err
= kNoMemoryErr
);
2574 // Check command parameters.
2576 if( gResolve_TimeLimitSecs
< 0 )
2578 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs
);
2583 // Create main connection.
2585 if( gConnectionOpt
)
2587 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
2588 require_noerr_quiet( err
, exit
);
2589 useMainConnection
= true;
2593 useMainConnection
= false;
2598 context
->flags
= GetDNSSDFlagsFromOpts();
2599 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
2601 // Get interface index.
2603 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
2604 require_noerr_quiet( err
, exit
);
2606 // Set remaining parameters.
2608 context
->name
= gResolve_Name
;
2609 context
->type
= gResolve_Type
;
2610 context
->domain
= gResolve_Domain
;
2611 context
->timeLimitSecs
= gResolve_TimeLimitSecs
;
2615 ResolvePrintPrologue( context
);
2619 if( useMainConnection
) sdRef
= context
->mainRef
;
2620 err
= DNSServiceResolve( &sdRef
, context
->flags
, context
->ifIndex
, context
->name
, context
->type
, context
->domain
,
2621 ResolveCallback
, NULL
);
2622 require_noerr( err
, exit
);
2624 context
->opRef
= sdRef
;
2625 if( !useMainConnection
)
2627 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
2628 require_noerr( err
, exit
);
2633 if( context
->timeLimitSecs
> 0 )
2635 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
2636 kExitReason_TimeLimit
, Exit
);
2641 dispatch_source_forget( &signalSource
);
2642 if( context
) ResolveContextFree( context
);
2643 if( err
) exit( 1 );
2646 //===========================================================================================================================
2648 //===========================================================================================================================
2650 static void ReconfirmCmd( void )
2653 uint8_t * rdataPtr
= NULL
;
2654 size_t rdataLen
= 0;
2655 DNSServiceFlags flags
;
2657 uint16_t type
, class;
2658 char ifName
[ kInterfaceNameBufLen
];
2662 flags
= GetDNSSDFlagsFromOpts();
2664 // Get interface index.
2666 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
2667 require_noerr_quiet( err
, exit
);
2671 err
= RecordTypeFromArgString( gReconfirmRecord_Type
, &type
);
2672 require_noerr( err
, exit
);
2676 if( gReconfirmRecord_Data
)
2678 err
= RecordDataFromArgString( gReconfirmRecord_Data
, &rdataPtr
, &rdataLen
);
2679 require_noerr_quiet( err
, exit
);
2684 if( gReconfirmRecord_Class
)
2686 err
= RecordClassFromArgString( gReconfirmRecord_Class
, &class );
2687 require_noerr( err
, exit
);
2691 class = kDNSServiceClass_IN
;
2696 FPrintF( stdout
, "Flags: %#{flags}\n", flags
, kDNSServiceFlagsDescriptors
);
2697 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
2698 FPrintF( stdout
, "Name: %s\n", gReconfirmRecord_Name
);
2699 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
2700 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
2701 FPrintF( stdout
, "Data: %#H\n", rdataPtr
, (int) rdataLen
, INT_MAX
);
2702 FPrintF( stdout
, "---\n" );
2704 err
= DNSServiceReconfirmRecord( flags
, ifIndex
, gReconfirmRecord_Name
, type
, class, (uint16_t) rdataLen
, rdataPtr
);
2705 FPrintF( stdout
, "Error: %#m\n", err
);
2708 FreeNullSafe( rdataPtr
);
2709 if( err
) exit( 1 );
2712 //===========================================================================================================================
2713 // ResolvePrintPrologue
2714 //===========================================================================================================================
2716 static void ResolvePrintPrologue( const ResolveContext
*inContext
)
2718 const int timeLimitSecs
= inContext
->timeLimitSecs
;
2719 char ifName
[ kInterfaceNameBufLen
];
2720 char time
[ kTimestampBufLen
];
2722 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
2724 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
2725 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
2726 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
2727 FPrintF( stdout
, "Type: %s\n", inContext
->type
);
2728 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
);
2729 FPrintF( stdout
, "Time limit: " );
2730 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
2731 else FPrintF( stdout
, "∞\n" );
2732 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
2733 FPrintF( stdout
, "---\n" );
2736 //===========================================================================================================================
2737 // ResolveContextFree
2738 //===========================================================================================================================
2740 static void ResolveContextFree( ResolveContext
*inContext
)
2742 DNSServiceForget( &inContext
->opRef
);
2743 DNSServiceForget( &inContext
->mainRef
);
2747 //===========================================================================================================================
2749 //===========================================================================================================================
2751 static void DNSSD_API
2753 DNSServiceRef inSDRef
,
2754 DNSServiceFlags inFlags
,
2755 uint32_t inInterfaceIndex
,
2756 DNSServiceErrorType inError
,
2757 const char * inFullName
,
2758 const char * inHostname
,
2761 const unsigned char * inTXTPtr
,
2764 char time
[ kTimestampBufLen
];
2765 char errorStr
[ 64 ];
2769 Unused( inContext
);
2771 GetTimestampStr( time
);
2773 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " error %#m", inError
);
2775 FPrintF( stdout
, "%s: %s can be reached at %s:%u (interface %d)%?s\n",
2776 time
, inFullName
, inHostname
, ntohs( inPort
), (int32_t) inInterfaceIndex
, inError
, errorStr
);
2779 FPrintF( stdout
, " TXT record: %#H\n", inTXTPtr
, (int) inTXTLen
, INT_MAX
);
2783 FPrintF( stdout
, " TXT record: %#{txt}\n", inTXTPtr
, (size_t) inTXTLen
);
2787 //===========================================================================================================================
2788 // GetAddrInfoPOSIXCmd
2789 //===========================================================================================================================
2791 #define AddressFamilyStr( X ) ( \
2792 ( (X) == AF_INET ) ? "inet" : \
2793 ( (X) == AF_INET6 ) ? "inet6" : \
2794 ( (X) == AF_UNSPEC ) ? "unspec" : \
2797 static void GetAddrInfoPOSIXCmd( void )
2800 struct addrinfo hints
;
2801 const struct addrinfo
* addrInfo
;
2802 struct addrinfo
* addrInfoList
= NULL
;
2803 char time
[ kTimestampBufLen
];
2805 memset( &hints
, 0, sizeof( hints
) );
2806 hints
.ai_socktype
= SOCK_STREAM
;
2808 // Set hints address family.
2810 if( !gGAIPOSIX_Family
) hints
.ai_family
= AF_UNSPEC
;
2811 else if( strcasecmp( gGAIPOSIX_Family
, "inet" ) == 0 ) hints
.ai_family
= AF_INET
;
2812 else if( strcasecmp( gGAIPOSIX_Family
, "inet6" ) == 0 ) hints
.ai_family
= AF_INET6
;
2813 else if( strcasecmp( gGAIPOSIX_Family
, "unspec" ) == 0 ) hints
.ai_family
= AF_UNSPEC
;
2816 FPrintF( stderr
, "Invalid address family: %s.\n", gGAIPOSIX_Family
);
2823 if( gGAIPOSIXFlag_AddrConfig
) hints
.ai_flags
|= AI_ADDRCONFIG
;
2824 if( gGAIPOSIXFlag_All
) hints
.ai_flags
|= AI_ALL
;
2825 if( gGAIPOSIXFlag_CanonName
) hints
.ai_flags
|= AI_CANONNAME
;
2826 if( gGAIPOSIXFlag_NumericHost
) hints
.ai_flags
|= AI_NUMERICHOST
;
2827 if( gGAIPOSIXFlag_NumericServ
) hints
.ai_flags
|= AI_NUMERICSERV
;
2828 if( gGAIPOSIXFlag_Passive
) hints
.ai_flags
|= AI_PASSIVE
;
2829 if( gGAIPOSIXFlag_V4Mapped
) hints
.ai_flags
|= AI_V4MAPPED
;
2830 #if( defined( AI_V4MAPPED_CFG ) )
2831 if( gGAIPOSIXFlag_V4MappedCFG
) hints
.ai_flags
|= AI_V4MAPPED_CFG
;
2833 #if( defined( AI_DEFAULT ) )
2834 if( gGAIPOSIXFlag_Default
) hints
.ai_flags
|= AI_DEFAULT
;
2839 FPrintF( stdout
, "Hostname: %s\n", gGAIPOSIX_HostName
);
2840 FPrintF( stdout
, "Servname: %s\n", gGAIPOSIX_ServName
);
2841 FPrintF( stdout
, "Address family: %s\n", AddressFamilyStr( hints
.ai_family
) );
2842 FPrintF( stdout
, "Flags: 0x%X < ", hints
.ai_flags
);
2843 if( hints
.ai_flags
& AI_NUMERICSERV
) FPrintF( stdout
, "AI_NUMERICSERV " );
2844 if( hints
.ai_flags
& AI_V4MAPPED
) FPrintF( stdout
, "AI_V4MAPPED " );
2845 if( hints
.ai_flags
& AI_ADDRCONFIG
) FPrintF( stdout
, "AI_ADDRCONFIG " );
2846 #if( defined( AI_V4MAPPED_CFG ) )
2847 if( hints
.ai_flags
& AI_V4MAPPED_CFG
) FPrintF( stdout
, "AI_V4MAPPED_CFG " );
2849 if( hints
.ai_flags
& AI_ALL
) FPrintF( stdout
, "AI_ALL " );
2850 if( hints
.ai_flags
& AI_NUMERICHOST
) FPrintF( stdout
, "AI_NUMERICHOST " );
2851 if( hints
.ai_flags
& AI_CANONNAME
) FPrintF( stdout
, "AI_CANONNAME " );
2852 if( hints
.ai_flags
& AI_PASSIVE
) FPrintF( stdout
, "AI_PASSIVE " );
2853 FPrintF( stdout
, ">\n" );
2854 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
2855 FPrintF( stdout
, "---\n" );
2857 // Call getaddrinfo().
2859 err
= getaddrinfo( gGAIPOSIX_HostName
, gGAIPOSIX_ServName
, &hints
, &addrInfoList
);
2860 GetTimestampStr( time
);
2863 FPrintF( stderr
, "Error %#m: %s.\n", err
, gai_strerror( err
) );
2869 for( addrInfo
= addrInfoList
; addrInfo
; addrInfo
= addrInfo
->ai_next
) { ++addrCount
; }
2871 FPrintF( stdout
, "Addresses (%d total):\n", addrCount
);
2872 for( addrInfo
= addrInfoList
; addrInfo
; addrInfo
= addrInfo
->ai_next
)
2874 FPrintF( stdout
, "%##a\n", addrInfo
->ai_addr
);
2877 FPrintF( stdout
, "---\n" );
2878 FPrintF( stdout
, "End time: %s\n", time
);
2881 if( addrInfoList
) freeaddrinfo( addrInfoList
);
2882 if( err
) exit( 1 );
2885 //===========================================================================================================================
2887 //===========================================================================================================================
2889 static void ReverseLookupCmd( void )
2892 QueryRecordContext
* context
= NULL
;
2893 DNSServiceRef sdRef
;
2894 dispatch_source_t signalSource
= NULL
;
2896 uint8_t ipv6Addr
[ 16 ];
2897 char recordName
[ ( 16 * 4 ) + 9 + 1 ];
2898 int useMainConnection
;
2900 // Set up SIGINT handler.
2902 signal( SIGINT
, SIG_IGN
);
2903 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
2904 require_noerr( err
, exit
);
2905 dispatch_resume( signalSource
);
2909 context
= (QueryRecordContext
*) calloc( 1, sizeof( *context
) );
2910 require_action( context
, exit
, err
= kNoMemoryErr
);
2912 // Check command parameters.
2914 if( gReverseLookup_TimeLimitSecs
< 0 )
2916 FPrintF( stderr
, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs
);
2921 // Create main connection.
2923 if( gConnectionOpt
)
2925 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
2926 require_noerr_quiet( err
, exit
);
2927 useMainConnection
= true;
2931 useMainConnection
= false;
2936 context
->flags
= GetDNSSDFlagsFromOpts();
2937 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
2939 // Get interface index.
2941 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
2942 require_noerr_quiet( err
, exit
);
2944 // Create reverse lookup record name.
2946 err
= StringToIPv4Address( gReverseLookup_IPAddr
, kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
,
2947 &ipv4Addr
, NULL
, NULL
, NULL
, NULL
);
2953 err
= StringToIPv6Address( gReverseLookup_IPAddr
,
2954 kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
| kStringToIPAddressFlagsNoScope
,
2955 ipv6Addr
, NULL
, NULL
, NULL
, NULL
);
2958 FPrintF( stderr
, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr
);
2963 for( i
= 15; i
>= 0; --i
)
2965 *dst
++ = kHexDigitsLowercase
[ ipv6Addr
[ i
] & 0x0F ];
2967 *dst
++ = kHexDigitsLowercase
[ ipv6Addr
[ i
] >> 4 ];
2970 strcpy( dst
, "ip6.arpa." );
2971 check( ( strlen( recordName
) + 1 ) <= sizeof( recordName
) );
2975 SNPrintF( recordName
, sizeof( recordName
), "%u.%u.%u.%u.in-addr.arpa.",
2977 ( ipv4Addr
>> 8 ) & 0xFF,
2978 ( ipv4Addr
>> 16 ) & 0xFF,
2979 ( ipv4Addr
>> 24 ) & 0xFF );
2982 // Set remaining parameters.
2984 context
->recordName
= recordName
;
2985 context
->recordType
= kDNSServiceType_PTR
;
2986 context
->timeLimitSecs
= gReverseLookup_TimeLimitSecs
;
2987 context
->oneShotMode
= gReverseLookup_OneShot
? true : false;
2991 QueryRecordPrintPrologue( context
);
2995 if( useMainConnection
) sdRef
= context
->mainRef
;
2996 err
= DNSServiceQueryRecord( &sdRef
, context
->flags
, context
->ifIndex
, context
->recordName
, context
->recordType
,
2997 kDNSServiceClass_IN
, QueryRecordCallback
, context
);
2998 require_noerr( err
, exit
);
3000 context
->opRef
= sdRef
;
3001 if( !useMainConnection
)
3003 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3004 require_noerr( err
, exit
);
3009 if( context
->timeLimitSecs
> 0 )
3011 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
3012 kExitReason_TimeLimit
, Exit
);
3017 dispatch_source_forget( &signalSource
);
3018 if( context
) QueryRecordContextFree( context
);
3019 if( err
) exit( 1 );
3022 //===========================================================================================================================
3024 //===========================================================================================================================
3026 typedef struct BrowseDomain BrowseDomain
;
3027 typedef struct BrowseType BrowseType
;
3028 typedef struct BrowseOp BrowseOp
;
3029 typedef struct BrowseInstance BrowseInstance
;
3030 typedef struct BrowseIPAddr BrowseIPAddr
;
3035 DNSServiceRef mainRef
;
3036 DNSServiceRef domainsQuery
;
3037 const char * domain
;
3038 BrowseDomain
* domainList
;
3039 char ** serviceTypes
;
3040 size_t serviceTypesCount
;
3041 dispatch_source_t exitTimer
;
3043 int pendingConnectCount
;
3045 int connectTimeLimitSecs
;
3046 Boolean includeAWDL
;
3047 Boolean useColoredText
;
3053 BrowseDomain
* next
;
3055 DNSServiceRef servicesQuery
;
3056 BrowseAllContext
* context
;
3057 BrowseType
* typeList
;
3064 BrowseOp
* browseList
;
3070 BrowseAllContext
* context
;
3071 DNSServiceRef browse
;
3072 uint64_t startTicks
;
3073 BrowseInstance
* instanceList
;
3078 struct BrowseInstance
3080 BrowseInstance
* next
;
3081 BrowseAllContext
* context
;
3083 uint64_t foundTicks
;
3084 DNSServiceRef resolve
;
3085 uint64_t resolveStartTicks
;
3086 uint64_t resolveDoneTicks
;
3087 DNSServiceRef getAddr
;
3088 uint64_t getAddrStartTicks
;
3089 BrowseIPAddr
* addrList
;
3100 kConnectStatus_None
= 0,
3101 kConnectStatus_Pending
= 1,
3102 kConnectStatus_Succeeded
= 2,
3103 kConnectStatus_Failed
= 3
3109 BrowseIPAddr
* next
;
3112 BrowseAllContext
* context
;
3113 uint64_t foundTicks
;
3114 AsyncConnectionRef connection
;
3115 ConnectStatus connectStatus
;
3116 CFTimeInterval connectTimeSecs
;
3117 OSStatus connectError
;
3120 static void BrowseAllPrintPrologue( const BrowseAllContext
*inContext
);
3121 static void DNSSD_API
3122 BrowseAllQueryDomainsCallback(
3123 DNSServiceRef inSDRef
,
3124 DNSServiceFlags inFlags
,
3125 uint32_t inInterfaceIndex
,
3126 DNSServiceErrorType inError
,
3127 const char * inFullName
,
3130 uint16_t inRDataLen
,
3131 const void * inRDataPtr
,
3134 static void DNSSD_API
3135 BrowseAllQueryCallback(
3136 DNSServiceRef inSDRef
,
3137 DNSServiceFlags inFlags
,
3138 uint32_t inInterfaceIndex
,
3139 DNSServiceErrorType inError
,
3140 const char * inFullName
,
3143 uint16_t inRDataLen
,
3144 const void * inRDataPtr
,
3147 static void DNSSD_API
3148 BrowseAllBrowseCallback(
3149 DNSServiceRef inSDRef
,
3150 DNSServiceFlags inFlags
,
3151 uint32_t inInterfaceIndex
,
3152 DNSServiceErrorType inError
,
3153 const char * inName
,
3154 const char * inRegType
,
3155 const char * inDomain
,
3157 static void DNSSD_API
3158 BrowseAllResolveCallback(
3159 DNSServiceRef inSDRef
,
3160 DNSServiceFlags inFlags
,
3161 uint32_t inInterfaceIndex
,
3162 DNSServiceErrorType inError
,
3163 const char * inFullName
,
3164 const char * inHostname
,
3167 const unsigned char * inTXTPtr
,
3169 static void DNSSD_API
3170 BrowseAllGAICallback(
3171 DNSServiceRef inSDRef
,
3172 DNSServiceFlags inFlags
,
3173 uint32_t inInterfaceIndex
,
3174 DNSServiceErrorType inError
,
3175 const char * inHostname
,
3176 const struct sockaddr
* inSockAddr
,
3179 static void BrowseAllConnectionProgress( int inPhase
, const void *inDetails
, void *inArg
);
3180 static void BrowseAllConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
);
3181 static void BrowseAllStop( void *inContext
);
3182 static void BrowseAllExit( void *inContext
);
3183 static OSStatus
BrowseAllAddDomain( BrowseAllContext
*inContext
, const char *inName
);
3184 static OSStatus
BrowseAllRemoveDomain( BrowseAllContext
*inContext
, const char *inName
);
3185 static void BrowseAllContextRelease( BrowseAllContext
*inContext
);
3187 BrowseAllAddServiceType(
3188 BrowseAllContext
* inContext
,
3189 BrowseDomain
* inDomain
,
3190 const char * inName
,
3192 Boolean inIncludeAWDL
);
3194 BrowseAllRemoveServiceType(
3195 BrowseAllContext
* inContext
,
3196 BrowseDomain
* inDomain
,
3197 const char * inName
,
3198 uint32_t inIfIndex
);
3200 BrowseAllAddServiceInstance(
3201 BrowseAllContext
* inContext
,
3202 BrowseOp
* inBrowse
,
3203 const char * inName
,
3204 const char * inRegType
,
3205 const char * inDomain
,
3206 uint32_t inIfIndex
);
3208 BrowseAllRemoveServiceInstance(
3209 BrowseAllContext
* inContext
,
3210 BrowseOp
* inBrowse
,
3211 const char * inName
,
3212 uint32_t inIfIndex
);
3214 BrowseAllAddIPAddress(
3215 BrowseAllContext
* inContext
,
3216 BrowseInstance
* inInstance
,
3217 const struct sockaddr
* inSockAddr
);
3219 BrowseAllRemoveIPAddress(
3220 BrowseAllContext
* inContext
,
3221 BrowseInstance
* inInstance
,
3222 const struct sockaddr
* inSockAddr
);
3223 static void BrowseDomainFree( BrowseDomain
*inDomain
);
3224 static void BrowseTypeFree( BrowseType
*inType
);
3225 static void BrowseOpFree( BrowseOp
*inBrowse
);
3226 static void BrowseInstanceFree( BrowseInstance
*inInstance
);
3227 static void BrowseIPAddrRelease( BrowseIPAddr
*inAddr
);
3228 static void BrowseIPAddrReleaseList( BrowseIPAddr
*inList
);
3230 #define ForgetIPAddressList( X ) ForgetCustom( X, BrowseIPAddrReleaseList )
3231 #define ForgetBrowseAllContext( X ) ForgetCustom( X, BrowseAllContextRelease )
3233 #define kBrowseAllOpenFileMin 4096
3235 static void BrowseAllCmd( void )
3238 BrowseAllContext
* context
= NULL
;
3240 // Check command parameters.
3242 if( gBrowseAll_BrowseTimeSecs
<= 0 )
3244 FPrintF( stdout
, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs
);
3249 #if( TARGET_OS_POSIX )
3250 // Set open file minimum.
3253 struct rlimit fdLimits
;
3255 err
= getrlimit( RLIMIT_NOFILE
, &fdLimits
);
3256 err
= map_global_noerr_errno( err
);
3257 require_noerr( err
, exit
);
3259 if( fdLimits
.rlim_cur
< kBrowseAllOpenFileMin
)
3261 fdLimits
.rlim_cur
= kBrowseAllOpenFileMin
;
3262 err
= setrlimit( RLIMIT_NOFILE
, &fdLimits
);
3263 err
= map_global_noerr_errno( err
);
3264 require_noerr( err
, exit
);
3269 context
= (BrowseAllContext
*) calloc( 1, sizeof( *context
) );
3270 require_action( context
, exit
, err
= kNoMemoryErr
);
3272 context
->refCount
= 1;
3273 context
->domain
= gBrowseAll_Domain
;
3274 context
->serviceTypes
= gBrowseAll_ServiceTypes
;
3275 context
->serviceTypesCount
= gBrowseAll_ServiceTypesCount
;
3276 gBrowseAll_ServiceTypes
= NULL
;
3277 gBrowseAll_ServiceTypesCount
= 0;
3278 context
->browseTimeSecs
= gBrowseAll_BrowseTimeSecs
;
3279 context
->connectTimeLimitSecs
= gBrowseAll_ConnectTimeLimitSecs
;
3280 context
->includeAWDL
= gBrowseAll_IncludeAWDL
? true : false;
3281 #if( TARGET_OS_POSIX )
3282 context
->useColoredText
= isatty( STDOUT_FILENO
) ? true : false;
3285 err
= DNSServiceCreateConnection( &context
->mainRef
);
3286 require_noerr( err
, exit
);
3288 err
= DNSServiceSetDispatchQueue( context
->mainRef
, dispatch_get_main_queue() );
3289 require_noerr( err
, exit
);
3291 // Set interface index.
3293 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3294 require_noerr_quiet( err
, exit
);
3296 BrowseAllPrintPrologue( context
);
3298 if( context
->domain
)
3300 err
= BrowseAllAddDomain( context
, context
->domain
);
3301 require_noerr( err
, exit
);
3305 DNSServiceRef sdRef
;
3307 sdRef
= context
->mainRef
;
3308 err
= DNSServiceQueryRecord( &sdRef
, kDNSServiceFlagsShareConnection
, kDNSServiceInterfaceIndexLocalOnly
,
3309 "b._dns-sd._udp.local.", kDNSServiceType_PTR
, kDNSServiceClass_IN
, BrowseAllQueryDomainsCallback
, context
);
3310 require_noerr( err
, exit
);
3312 context
->domainsQuery
= sdRef
;
3315 dispatch_after_f( dispatch_time_seconds( context
->browseTimeSecs
), dispatch_get_main_queue(), context
, BrowseAllStop
);
3319 if( context
) BrowseAllContextRelease( context
);
3320 if( err
) exit( 1 );
3323 //===========================================================================================================================
3324 // BrowseAllPrintPrologue
3325 //===========================================================================================================================
3327 static void BrowseAllPrintPrologue( const BrowseAllContext
*inContext
)
3330 char ifName
[ kInterfaceNameBufLen
];
3331 char time
[ kTimestampBufLen
];
3333 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3335 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3336 FPrintF( stdout
, "Service types: ");
3337 if( inContext
->serviceTypesCount
> 0 )
3339 FPrintF( stdout
, "%s", inContext
->serviceTypes
[ 0 ] );
3340 for( i
= 1; i
< inContext
->serviceTypesCount
; ++i
) FPrintF( stdout
, ", %s", inContext
->serviceTypes
[ i
] );
3341 FPrintF( stdout
, "\n" );
3345 FPrintF( stdout
, "all services\n" );
3347 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "default domains" );
3348 FPrintF( stdout
, "Browse time: %d second%?c\n", inContext
->browseTimeSecs
, inContext
->browseTimeSecs
!= 1, 's' );
3349 FPrintF( stdout
, "Connect time limit: %d second%?c\n",
3350 inContext
->connectTimeLimitSecs
, inContext
->connectTimeLimitSecs
!= 1, 's' );
3351 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
3352 FPrintF( stdout
, "---\n" );
3355 //===========================================================================================================================
3356 // BrowseAllQueryDomainsCallback
3357 //===========================================================================================================================
3359 static void DNSSD_API
3360 BrowseAllQueryDomainsCallback(
3361 DNSServiceRef inSDRef
,
3362 DNSServiceFlags inFlags
,
3363 uint32_t inInterfaceIndex
,
3364 DNSServiceErrorType inError
,
3365 const char * inFullName
,
3368 uint16_t inRDataLen
,
3369 const void * inRDataPtr
,
3374 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
3375 char domainStr
[ kDNSServiceMaxDomainName
];
3378 Unused( inInterfaceIndex
);
3379 Unused( inFullName
);
3385 require_noerr( err
, exit
);
3387 err
= DomainNameToString( inRDataPtr
, ( (const uint8_t *) inRDataPtr
) + inRDataLen
, domainStr
, NULL
);
3388 require_noerr( err
, exit
);
3390 if( inFlags
& kDNSServiceFlagsAdd
)
3392 err
= BrowseAllAddDomain( context
, domainStr
);
3393 if( err
== kDuplicateErr
) err
= kNoErr
;
3394 require_noerr( err
, exit
);
3398 err
= BrowseAllRemoveDomain( context
, domainStr
);
3399 if( err
== kNotFoundErr
) err
= kNoErr
;
3400 require_noerr( err
, exit
);
3404 if( err
) exit( 1 );
3407 //===========================================================================================================================
3408 // BrowseAllQueryCallback
3409 //===========================================================================================================================
3411 static void DNSSD_API
3412 BrowseAllQueryCallback(
3413 DNSServiceRef inSDRef
,
3414 DNSServiceFlags inFlags
,
3415 uint32_t inInterfaceIndex
,
3416 DNSServiceErrorType inError
,
3417 const char * inFullName
,
3420 uint16_t inRDataLen
,
3421 const void * inRDataPtr
,
3426 BrowseDomain
* const domain
= (BrowseDomain
*) inContext
;
3427 const uint8_t * firstLabel
;
3428 const uint8_t * secondLabel
;
3429 char * serviceTypeStr
= NULL
;
3430 const uint8_t * const end
= ( (uint8_t * ) inRDataPtr
) + inRDataLen
;
3433 Unused( inFullName
);
3439 require_noerr( err
, exit
);
3441 check( inType
== kDNSServiceType_PTR
);
3442 check( inClass
== kDNSServiceClass_IN
);
3443 require_action( inRDataLen
> 0, exit
, err
= kSizeErr
);
3445 firstLabel
= inRDataPtr
;
3446 require_action_quiet( ( firstLabel
+ 1 + firstLabel
[ 0 ] ) < end
, exit
, err
= kUnderrunErr
);
3448 secondLabel
= firstLabel
+ 1 + firstLabel
[ 0 ];
3449 require_action_quiet( ( secondLabel
+ 1 + secondLabel
[ 0 ] ) < end
, exit
, err
= kUnderrunErr
);
3451 ASPrintF( &serviceTypeStr
, "%#s.%#s", firstLabel
, secondLabel
);
3452 require_action( serviceTypeStr
, exit
, err
= kNoMemoryErr
);
3454 if( inFlags
& kDNSServiceFlagsAdd
)
3456 err
= BrowseAllAddServiceType( domain
->context
, domain
, serviceTypeStr
, inInterfaceIndex
, false );
3457 if( err
== kDuplicateErr
) err
= kNoErr
;
3458 require_noerr( err
, exit
);
3462 err
= BrowseAllRemoveServiceType( domain
->context
, domain
, serviceTypeStr
, inInterfaceIndex
);
3463 if( err
== kNotFoundErr
) err
= kNoErr
;
3464 require_noerr( err
, exit
);
3468 FreeNullSafe( serviceTypeStr
);
3471 //===========================================================================================================================
3472 // BrowseAllBrowseCallback
3473 //===========================================================================================================================
3475 static void DNSSD_API
3476 BrowseAllBrowseCallback(
3477 DNSServiceRef inSDRef
,
3478 DNSServiceFlags inFlags
,
3479 uint32_t inInterfaceIndex
,
3480 DNSServiceErrorType inError
,
3481 const char * inName
,
3482 const char * inRegType
,
3483 const char * inDomain
,
3487 BrowseOp
* const browse
= (BrowseOp
*) inContext
;
3492 require_noerr( err
, exit
);
3494 if( inFlags
& kDNSServiceFlagsAdd
)
3496 err
= BrowseAllAddServiceInstance( browse
->context
, browse
, inName
, inRegType
, inDomain
, inInterfaceIndex
);
3497 if( err
== kDuplicateErr
) err
= kNoErr
;
3498 require_noerr( err
, exit
);
3502 err
= BrowseAllRemoveServiceInstance( browse
->context
, browse
, inName
, inInterfaceIndex
);
3503 if( err
== kNotFoundErr
) err
= kNoErr
;
3504 require_noerr( err
, exit
);
3511 //===========================================================================================================================
3512 // BrowseAllResolveCallback
3513 //===========================================================================================================================
3515 static void DNSSD_API
3516 BrowseAllResolveCallback(
3517 DNSServiceRef inSDRef
,
3518 DNSServiceFlags inFlags
,
3519 uint32_t inInterfaceIndex
,
3520 DNSServiceErrorType inError
,
3521 const char * inFullName
,
3522 const char * inHostname
,
3525 const unsigned char * inTXTPtr
,
3529 const uint64_t nowTicks
= UpTicks();
3530 BrowseInstance
* const instance
= (BrowseInstance
*) inContext
;
3534 Unused( inInterfaceIndex
);
3535 Unused( inFullName
);
3538 require_noerr( err
, exit
);
3540 if( !MemEqual( instance
->txtPtr
, instance
->txtLen
, inTXTPtr
, inTXTLen
) )
3542 FreeNullSafe( instance
->txtPtr
);
3543 instance
->txtPtr
= malloc( inTXTLen
);
3544 require_action( instance
->txtPtr
, exit
, err
= kNoMemoryErr
);
3546 memcpy( instance
->txtPtr
, inTXTPtr
, inTXTLen
);
3547 instance
->txtLen
= inTXTLen
;
3550 instance
->port
= ntohs( inPort
);
3552 if( !instance
->hostname
|| ( strcasecmp( instance
->hostname
, inHostname
) != 0 ) )
3554 DNSServiceRef sdRef
;
3556 if( !instance
->hostname
) instance
->resolveDoneTicks
= nowTicks
;
3557 FreeNullSafe( instance
->hostname
);
3558 instance
->hostname
= strdup( inHostname
);
3559 require_action( instance
->hostname
, exit
, err
= kNoMemoryErr
);
3561 DNSServiceForget( &instance
->getAddr
);
3562 ForgetIPAddressList( &instance
->addrList
);
3564 sdRef
= instance
->context
->mainRef
;
3565 instance
->getAddrStartTicks
= UpTicks();
3566 err
= DNSServiceGetAddrInfo( &sdRef
, kDNSServiceFlagsShareConnection
, instance
->ifIndex
,
3567 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, instance
->hostname
, BrowseAllGAICallback
, instance
);
3568 require_noerr( err
, exit
);
3570 instance
->getAddr
= sdRef
;
3574 if( err
) exit( 1 );
3577 //===========================================================================================================================
3578 // BrowseAllGAICallback
3579 //===========================================================================================================================
3581 static void DNSSD_API
3582 BrowseAllGAICallback(
3583 DNSServiceRef inSDRef
,
3584 DNSServiceFlags inFlags
,
3585 uint32_t inInterfaceIndex
,
3586 DNSServiceErrorType inError
,
3587 const char * inHostname
,
3588 const struct sockaddr
* inSockAddr
,
3593 BrowseInstance
* const instance
= (BrowseInstance
*) inContext
;
3596 Unused( inInterfaceIndex
);
3597 Unused( inHostname
);
3601 require_noerr( err
, exit
);
3603 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
3605 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
3609 if( inFlags
& kDNSServiceFlagsAdd
)
3611 err
= BrowseAllAddIPAddress( instance
->context
, instance
, inSockAddr
);
3612 if( err
== kDuplicateErr
) err
= kNoErr
;
3613 require_noerr( err
, exit
);
3617 err
= BrowseAllRemoveIPAddress( instance
->context
, instance
, inSockAddr
);
3618 if( err
== kNotFoundErr
) err
= kNoErr
;
3619 require_noerr( err
, exit
);
3626 //===========================================================================================================================
3627 // BrowseAllConnectionProgress
3628 //===========================================================================================================================
3630 static void BrowseAllConnectionProgress( int inPhase
, const void *inDetails
, void *inArg
)
3632 BrowseIPAddr
* const addr
= (BrowseIPAddr
*) inArg
;
3634 if( inPhase
== kAsyncConnectionPhase_Connected
)
3636 const AsyncConnectedInfo
* const info
= (AsyncConnectedInfo
*) inDetails
;
3638 addr
->connectTimeSecs
= info
->connectSecs
;
3642 //===========================================================================================================================
3643 // BrowseAllConnectionHandler
3644 //===========================================================================================================================
3646 static void BrowseAllConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
)
3648 BrowseIPAddr
* const addr
= (BrowseIPAddr
*) inArg
;
3649 BrowseAllContext
* const context
= addr
->context
;
3653 addr
->connectStatus
= kConnectStatus_Failed
;
3654 addr
->connectError
= inError
;
3658 addr
->connectStatus
= kConnectStatus_Succeeded
;
3661 check( context
->pendingConnectCount
> 0 );
3662 if( --context
->pendingConnectCount
== 0 )
3664 if( context
->exitTimer
)
3666 dispatch_source_forget( &context
->exitTimer
);
3667 dispatch_async_f( dispatch_get_main_queue(), context
, BrowseAllExit
);
3671 ForgetSocket( &inSock
);
3672 BrowseIPAddrRelease( addr
);
3675 //===========================================================================================================================
3677 //===========================================================================================================================
3679 static void BrowseAllStop( void *inContext
)
3682 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
3683 BrowseDomain
* domain
;
3686 BrowseInstance
* instance
;
3688 DNSServiceForget( &context
->domainsQuery
);
3689 for( domain
= context
->domainList
; domain
; domain
= domain
->next
)
3691 DNSServiceForget( &domain
->servicesQuery
);
3692 for( type
= domain
->typeList
; type
; type
= type
->next
)
3694 for( browse
= type
->browseList
; browse
; browse
= browse
->next
)
3696 DNSServiceForget( &browse
->browse
);
3697 for( instance
= browse
->instanceList
; instance
; instance
= instance
->next
)
3699 DNSServiceForget( &instance
->resolve
);
3700 DNSServiceForget( &instance
->getAddr
);
3705 DNSServiceForget( &context
->mainRef
);
3707 if( ( context
->pendingConnectCount
> 0 ) && ( context
->connectTimeLimitSecs
> 0 ) )
3709 check( !context
->exitTimer
);
3710 err
= DispatchTimerCreate( dispatch_time_seconds( context
->connectTimeLimitSecs
), DISPATCH_TIME_FOREVER
,
3711 100 * kNanosecondsPerMillisecond
, BrowseAllExit
, NULL
, context
, &context
->exitTimer
);
3712 require_noerr( err
, exit
);
3713 dispatch_resume( context
->exitTimer
);
3717 dispatch_async_f( dispatch_get_main_queue(), context
, BrowseAllExit
);
3722 if( err
) exit( 1 );
3725 //===========================================================================================================================
3727 //===========================================================================================================================
3729 #define kStatusStr_CouldConnect "connected"
3730 #define kStatusStr_CouldConnectColored kANSIGreen kStatusStr_CouldConnect kANSINormal
3731 #define kStatusStr_CouldNotConnect "could not connect"
3732 #define kStatusStr_CouldNotConnectColored kANSIRed kStatusStr_CouldNotConnect kANSINormal
3733 #define kStatusStr_NoConnectionAttempted "no connection attempted"
3734 #define kStatusStr_Unknown "unknown"
3736 #define Indent( X ) ( (X) * 4 ), ""
3738 static void BrowseAllExit( void *inContext
)
3740 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
3741 BrowseDomain
* domain
;
3744 BrowseInstance
* instance
;
3745 BrowseIPAddr
* addr
;
3747 dispatch_source_forget( &context
->exitTimer
);
3749 for( domain
= context
->domainList
; domain
; domain
= domain
->next
)
3751 FPrintF( stdout
, "%s\n\n", domain
->name
);
3753 for( type
= domain
->typeList
; type
; type
= type
->next
)
3757 desc
= ServiceTypeDescription( type
->name
);
3758 if( desc
) FPrintF( stdout
, "%*s" "%s (%s)\n\n", Indent( 1 ), desc
, type
->name
);
3759 else FPrintF( stdout
, "%*s" "%s\n\n", Indent( 1 ), type
->name
);
3761 for( browse
= type
->browseList
; browse
; browse
= browse
->next
)
3763 for( instance
= browse
->instanceList
; instance
; instance
= instance
->next
)
3765 char ifname
[ IF_NAMESIZE
+ 1 ];
3767 FPrintF( stdout
, "%*s" "%s via ", Indent( 2 ), instance
->name
);
3768 FPrintF( stdout
, "%*s" "%s via ", Indent( 2 ), instance
->name
);
3769 if( instance
->ifIndex
== 0 )
3771 FPrintF( stdout
, "the Internet" );
3773 else if( if_indextoname( instance
->ifIndex
, ifname
) )
3775 NetTransportType netType
;
3777 SocketGetInterfaceInfo( kInvalidSocketRef
, ifname
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
3779 FPrintF( stdout
, "%s (%s)",
3780 ( netType
== kNetTransportType_Ethernet
) ? "ethernet" : NetTransportTypeToString( netType
),
3785 FPrintF( stdout
, "interface index %u", instance
->ifIndex
);
3787 FPrintF( stdout
, "\n\n" );
3789 if( instance
->hostname
)
3793 SNPrintF( buffer
, sizeof( buffer
), "%s:%u", instance
->hostname
, instance
->port
);
3794 FPrintF( stdout
, "%*s" "%-51s %4llu ms\n", Indent( 3 ), buffer
,
3795 UpTicksToMilliseconds( instance
->resolveDoneTicks
- instance
->resolveStartTicks
) );
3799 FPrintF( stdout
, "%*s" "%s:%u\n", Indent( 3 ), instance
->hostname
, instance
->port
);
3802 for( addr
= instance
->addrList
; addr
; addr
= addr
->next
)
3804 AsyncConnection_Forget( &addr
->connection
);
3806 if( addr
->connectStatus
== kConnectStatus_Pending
)
3808 addr
->connectStatus
= kConnectStatus_Failed
;
3809 addr
->connectError
= kTimeoutErr
;
3812 FPrintF( stdout
, "%*s" "%-##47a %4llu ms (", Indent( 4 ),
3813 &addr
->sip
.sa
, UpTicksToMilliseconds( addr
->foundTicks
- instance
->getAddrStartTicks
) );
3814 switch( addr
->connectStatus
)
3816 case kConnectStatus_None
:
3817 FPrintF( stdout
, "%s", kStatusStr_NoConnectionAttempted
);
3820 case kConnectStatus_Succeeded
:
3821 FPrintF( stdout
, "%s in %.2f ms",
3822 context
->useColoredText
? kStatusStr_CouldConnectColored
: kStatusStr_CouldConnect
,
3823 addr
->connectTimeSecs
* 1000 );
3826 case kConnectStatus_Failed
:
3827 FPrintF( stdout
, "%s: %m",
3828 context
->useColoredText
? kStatusStr_CouldNotConnectColored
: kStatusStr_CouldNotConnect
,
3829 addr
->connectError
);
3833 FPrintF( stdout
, "%s", kStatusStr_Unknown
);
3836 FPrintF( stdout
, ")\n" );
3839 FPrintF( stdout
, "\n" );
3840 if( instance
->txtLen
== 0 ) continue;
3842 FPrintF( stdout
, "%*s" "TXT record:\n", Indent( 3 ) );
3843 if( instance
->txtLen
> 1 )
3845 FPrintF( stdout
, "%3{txt}", instance
->txtPtr
, instance
->txtLen
);
3849 FPrintF( stdout
, "%*s" "%#H\n", Indent( 3 ), instance
->txtPtr
, (int) instance
->txtLen
, INT_MAX
);
3851 FPrintF( stdout
, "\n" );
3854 FPrintF( stdout
, "\n" );
3858 while( ( domain
= context
->domainList
) != NULL
)
3860 context
->domainList
= domain
->next
;
3861 BrowseDomainFree( domain
);
3864 BrowseAllContextRelease( context
);
3868 //===========================================================================================================================
3869 // BrowseAllAddDomain
3870 //===========================================================================================================================
3872 static OSStatus
BrowseAllAddDomain( BrowseAllContext
*inContext
, const char *inName
)
3875 BrowseDomain
* domain
;
3877 BrowseDomain
* newDomain
= NULL
;
3879 for( p
= &inContext
->domainList
; ( domain
= *p
) != NULL
; p
= &domain
->next
)
3881 if( strcasecmp( domain
->name
, inName
) == 0 ) break;
3883 require_action_quiet( !domain
, exit
, err
= kDuplicateErr
);
3885 newDomain
= (BrowseDomain
*) calloc( 1, sizeof( *newDomain
) );
3886 require_action( newDomain
, exit
, err
= kNoMemoryErr
);
3888 ++inContext
->refCount
;
3889 newDomain
->context
= inContext
;
3891 newDomain
->name
= strdup( inName
);
3892 require_action( newDomain
->name
, exit
, err
= kNoMemoryErr
);
3894 if( inContext
->serviceTypesCount
> 0 )
3898 for( i
= 0; i
< inContext
->serviceTypesCount
; ++i
)
3900 err
= BrowseAllAddServiceType( inContext
, newDomain
, inContext
->serviceTypes
[ i
], inContext
->ifIndex
,
3901 inContext
->includeAWDL
);
3902 if( err
== kDuplicateErr
) err
= kNoErr
;
3903 require_noerr( err
, exit
);
3909 DNSServiceFlags flags
;
3910 DNSServiceRef sdRef
;
3912 ASPrintF( &recordName
, "_services._dns-sd._udp.%s", newDomain
->name
);
3913 require_action( recordName
, exit
, err
= kNoMemoryErr
);
3915 flags
= kDNSServiceFlagsShareConnection
;
3916 if( inContext
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
3918 sdRef
= newDomain
->context
->mainRef
;
3919 err
= DNSServiceQueryRecord( &sdRef
, flags
, inContext
->ifIndex
, recordName
, kDNSServiceType_PTR
, kDNSServiceClass_IN
,
3920 BrowseAllQueryCallback
, newDomain
);
3922 require_noerr( err
, exit
);
3924 newDomain
->servicesQuery
= sdRef
;
3932 if( newDomain
) BrowseDomainFree( newDomain
);
3936 //===========================================================================================================================
3937 // BrowseAllRemoveDomain
3938 //===========================================================================================================================
3940 static OSStatus
BrowseAllRemoveDomain( BrowseAllContext
*inContext
, const char *inName
)
3943 BrowseDomain
* domain
;
3946 for( p
= &inContext
->domainList
; ( domain
= *p
) != NULL
; p
= &domain
->next
)
3948 if( strcasecmp( domain
->name
, inName
) == 0 ) break;
3954 BrowseDomainFree( domain
);
3965 //===========================================================================================================================
3966 // BrowseAllContextRelease
3967 //===========================================================================================================================
3969 static void BrowseAllContextRelease( BrowseAllContext
*inContext
)
3971 if( --inContext
->refCount
== 0 )
3973 check( !inContext
->domainsQuery
);
3974 check( !inContext
->domainList
);
3975 check( !inContext
->exitTimer
);
3976 check( !inContext
->pendingConnectCount
);
3977 DNSServiceForget( &inContext
->mainRef
);
3978 if( inContext
->serviceTypes
)
3980 StringArray_Free( inContext
->serviceTypes
, inContext
->serviceTypesCount
);
3981 inContext
->serviceTypes
= NULL
;
3982 inContext
->serviceTypesCount
= 0;
3988 //===========================================================================================================================
3989 // BrowseAllAddServiceType
3990 //===========================================================================================================================
3993 BrowseAllAddServiceType(
3994 BrowseAllContext
* inContext
,
3995 BrowseDomain
* inDomain
,
3996 const char * inName
,
3998 Boolean inIncludeAWDL
)
4001 DNSServiceRef sdRef
;
4002 DNSServiceFlags flags
;
4004 BrowseType
** typePtr
;
4005 BrowseType
* newType
= NULL
;
4007 BrowseOp
** browsePtr
;
4008 BrowseOp
* newBrowse
= NULL
;
4010 for( typePtr
= &inDomain
->typeList
; ( type
= *typePtr
) != NULL
; typePtr
= &type
->next
)
4012 if( strcasecmp( type
->name
, inName
) == 0 ) break;
4016 newType
= (BrowseType
*) calloc( 1, sizeof( *newType
) );
4017 require_action( newType
, exit
, err
= kNoMemoryErr
);
4019 newType
->name
= strdup( inName
);
4020 require_action( newType
->name
, exit
, err
= kNoMemoryErr
);
4025 for( browsePtr
= &type
->browseList
; ( browse
= *browsePtr
) != NULL
; browsePtr
= &browse
->next
)
4027 if( browse
->ifIndex
== inIfIndex
) break;
4029 require_action_quiet( !browse
, exit
, err
= kDuplicateErr
);
4031 newBrowse
= (BrowseOp
*) calloc( 1, sizeof( *newBrowse
) );
4032 require_action( newBrowse
, exit
, err
= kNoMemoryErr
);
4034 ++inContext
->refCount
;
4035 newBrowse
->context
= inContext
;
4036 newBrowse
->ifIndex
= inIfIndex
;
4037 if( stricmp_suffix( inName
, "._tcp" ) == 0 ) newBrowse
->isTCP
= true;
4039 flags
= kDNSServiceFlagsShareConnection
;
4040 if( inIncludeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
4042 newBrowse
->startTicks
= UpTicks();
4044 sdRef
= inContext
->mainRef
;
4045 err
= DNSServiceBrowse( &sdRef
, flags
, newBrowse
->ifIndex
, type
->name
, inDomain
->name
, BrowseAllBrowseCallback
,
4047 require_noerr( err
, exit
);
4049 newBrowse
->browse
= sdRef
;
4050 *browsePtr
= newBrowse
;
4060 if( newBrowse
) BrowseOpFree( newBrowse
);
4061 if( newType
) BrowseTypeFree( newType
);
4065 //===========================================================================================================================
4066 // BrowseAllRemoveServiceType
4067 //===========================================================================================================================
4070 BrowseAllRemoveServiceType(
4071 BrowseAllContext
* inContext
,
4072 BrowseDomain
* inDomain
,
4073 const char * inName
,
4074 uint32_t inIfIndex
)
4078 BrowseType
** typePtr
;
4080 BrowseOp
** browsePtr
;
4082 Unused( inContext
);
4084 for( typePtr
= &inDomain
->typeList
; ( type
= *typePtr
) != NULL
; typePtr
= &type
->next
)
4086 if( strcasecmp( type
->name
, inName
) == 0 ) break;
4088 require_action_quiet( type
, exit
, err
= kNotFoundErr
);
4090 for( browsePtr
= &type
->browseList
; ( browse
= *browsePtr
) != NULL
; browsePtr
= &browse
->next
)
4092 if( browse
->ifIndex
== inIfIndex
) break;
4094 require_action_quiet( browse
, exit
, err
= kNotFoundErr
);
4096 *browsePtr
= browse
->next
;
4097 BrowseOpFree( browse
);
4098 if( !type
->browseList
)
4100 *typePtr
= type
->next
;
4101 BrowseTypeFree( type
);
4109 //===========================================================================================================================
4110 // BrowseAllAddServiceInstance
4111 //===========================================================================================================================
4114 BrowseAllAddServiceInstance(
4115 BrowseAllContext
* inContext
,
4116 BrowseOp
* inBrowse
,
4117 const char * inName
,
4118 const char * inRegType
,
4119 const char * inDomain
,
4120 uint32_t inIfIndex
)
4123 DNSServiceRef sdRef
;
4124 BrowseInstance
* instance
;
4125 BrowseInstance
** p
;
4126 const uint64_t nowTicks
= UpTicks();
4127 BrowseInstance
* newInstance
= NULL
;
4129 for( p
= &inBrowse
->instanceList
; ( instance
= *p
) != NULL
; p
= &instance
->next
)
4131 if( ( instance
->ifIndex
== inIfIndex
) && ( strcasecmp( instance
->name
, inName
) == 0 ) ) break;
4133 require_action_quiet( !instance
, exit
, err
= kDuplicateErr
);
4135 newInstance
= (BrowseInstance
*) calloc( 1, sizeof( *newInstance
) );
4136 require_action( newInstance
, exit
, err
= kNoMemoryErr
);
4138 ++inContext
->refCount
;
4139 newInstance
->context
= inContext
;
4140 newInstance
->foundTicks
= nowTicks
;
4141 newInstance
->ifIndex
= inIfIndex
;
4142 newInstance
->isTCP
= inBrowse
->isTCP
;
4144 newInstance
->name
= strdup( inName
);
4145 require_action( newInstance
->name
, exit
, err
= kNoMemoryErr
);
4147 sdRef
= inContext
->mainRef
;
4148 newInstance
->resolveStartTicks
= UpTicks();
4149 err
= DNSServiceResolve( &sdRef
, kDNSServiceFlagsShareConnection
, newInstance
->ifIndex
, inName
, inRegType
, inDomain
,
4150 BrowseAllResolveCallback
, newInstance
);
4151 require_noerr( err
, exit
);
4153 newInstance
->resolve
= sdRef
;
4158 if( newInstance
) BrowseInstanceFree( newInstance
);
4162 //===========================================================================================================================
4163 // BrowseAllRemoveServiceInstance
4164 //===========================================================================================================================
4167 BrowseAllRemoveServiceInstance(
4168 BrowseAllContext
* inContext
,
4169 BrowseOp
* inBrowse
,
4170 const char * inName
,
4171 uint32_t inIfIndex
)
4174 BrowseInstance
* instance
;
4175 BrowseInstance
** p
;
4177 Unused( inContext
);
4179 for( p
= &inBrowse
->instanceList
; ( instance
= *p
) != NULL
; p
= &instance
->next
)
4181 if( ( instance
->ifIndex
== inIfIndex
) && ( strcasecmp( instance
->name
, inName
) == 0 ) ) break;
4183 require_action_quiet( instance
, exit
, err
= kNotFoundErr
);
4185 *p
= instance
->next
;
4186 BrowseInstanceFree( instance
);
4193 //===========================================================================================================================
4194 // BrowseAllAddIPAddress
4195 //===========================================================================================================================
4197 #define kDiscardProtocolPort 9
4200 BrowseAllAddIPAddress(
4201 BrowseAllContext
* inContext
,
4202 BrowseInstance
* inInstance
,
4203 const struct sockaddr
* inSockAddr
)
4206 BrowseIPAddr
* addr
;
4208 const uint64_t nowTicks
= UpTicks();
4209 BrowseIPAddr
* newAddr
= NULL
;
4211 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
4213 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
4218 for( p
= &inInstance
->addrList
; ( addr
= *p
) != NULL
; p
= &addr
->next
)
4220 if( SockAddrCompareAddr( &addr
->sip
, inSockAddr
) == 0 ) break;
4222 require_action_quiet( !addr
, exit
, err
= kDuplicateErr
);
4224 newAddr
= (BrowseIPAddr
*) calloc( 1, sizeof( *newAddr
) );
4225 require_action( newAddr
, exit
, err
= kNoMemoryErr
);
4227 ++inContext
->refCount
;
4228 newAddr
->refCount
= 1;
4229 newAddr
->context
= inContext
;
4230 newAddr
->foundTicks
= nowTicks
;
4231 SockAddrCopy( inSockAddr
, &newAddr
->sip
.sa
);
4233 if( inInstance
->isTCP
&& ( inInstance
->port
!= kDiscardProtocolPort
) )
4235 char destination
[ kSockAddrStringMaxSize
];
4237 err
= SockAddrToString( &newAddr
->sip
, kSockAddrStringFlagsNoPort
, destination
);
4238 require_noerr( err
, exit
);
4240 err
= AsyncConnection_Connect( &newAddr
->connection
, destination
, -inInstance
->port
, kAsyncConnectionFlag_P2P
,
4241 kAsyncConnectionNoTimeout
, kSocketBufferSize_DontSet
, kSocketBufferSize_DontSet
,
4242 BrowseAllConnectionProgress
, newAddr
, BrowseAllConnectionHandler
, newAddr
, dispatch_get_main_queue() );
4243 require_noerr( err
, exit
);
4245 ++newAddr
->refCount
;
4246 newAddr
->connectStatus
= kConnectStatus_Pending
;
4247 ++inContext
->pendingConnectCount
;
4255 if( newAddr
) BrowseIPAddrRelease( newAddr
);
4259 //===========================================================================================================================
4260 // BrowseAllRemoveIPAddress
4261 //===========================================================================================================================
4264 BrowseAllRemoveIPAddress(
4265 BrowseAllContext
* inContext
,
4266 BrowseInstance
* inInstance
,
4267 const struct sockaddr
* inSockAddr
)
4270 BrowseIPAddr
* addr
;
4273 Unused( inContext
);
4275 for( p
= &inInstance
->addrList
; ( addr
= *p
) != NULL
; p
= &addr
->next
)
4277 if( SockAddrCompareAddr( &addr
->sip
.sa
, inSockAddr
) == 0 ) break;
4279 require_action_quiet( addr
, exit
, err
= kNotFoundErr
);
4282 BrowseIPAddrRelease( addr
);
4289 //===========================================================================================================================
4291 //===========================================================================================================================
4293 static void BrowseDomainFree( BrowseDomain
*inDomain
)
4297 ForgetBrowseAllContext( &inDomain
->context
);
4298 ForgetMem( &inDomain
->name
);
4299 DNSServiceForget( &inDomain
->servicesQuery
);
4300 while( ( type
= inDomain
->typeList
) != NULL
)
4302 inDomain
->typeList
= type
->next
;
4303 BrowseTypeFree( type
);
4308 //===========================================================================================================================
4310 //===========================================================================================================================
4312 static void BrowseTypeFree( BrowseType
*inType
)
4316 ForgetMem( &inType
->name
);
4317 while( ( browse
= inType
->browseList
) != NULL
)
4319 inType
->browseList
= browse
->next
;
4320 BrowseOpFree( browse
);
4325 //===========================================================================================================================
4327 //===========================================================================================================================
4329 static void BrowseOpFree( BrowseOp
*inBrowse
)
4331 BrowseInstance
* instance
;
4333 ForgetBrowseAllContext( &inBrowse
->context
);
4334 DNSServiceForget( &inBrowse
->browse
);
4335 while( ( instance
= inBrowse
->instanceList
) != NULL
)
4337 inBrowse
->instanceList
= instance
->next
;
4338 BrowseInstanceFree( instance
);
4343 //===========================================================================================================================
4344 // BrowseInstanceFree
4345 //===========================================================================================================================
4347 static void BrowseInstanceFree( BrowseInstance
*inInstance
)
4349 ForgetBrowseAllContext( &inInstance
->context
);
4350 ForgetMem( &inInstance
->name
);
4351 DNSServiceForget( &inInstance
->resolve
);
4352 DNSServiceForget( &inInstance
->getAddr
);
4353 ForgetMem( &inInstance
->txtPtr
);
4354 ForgetMem( &inInstance
->hostname
);
4355 ForgetIPAddressList( &inInstance
->addrList
);
4359 //===========================================================================================================================
4360 // BrowseIPAddrRelease
4361 //===========================================================================================================================
4363 static void BrowseIPAddrRelease( BrowseIPAddr
*inAddr
)
4365 AsyncConnection_Forget( &inAddr
->connection
);
4366 if( --inAddr
->refCount
== 0 )
4368 ForgetBrowseAllContext( &inAddr
->context
);
4373 //===========================================================================================================================
4374 // BrowseIPAddrReleaseList
4375 //===========================================================================================================================
4377 static void BrowseIPAddrReleaseList( BrowseIPAddr
*inList
)
4379 BrowseIPAddr
* addr
;
4381 while( ( addr
= inList
) != NULL
)
4383 inList
= addr
->next
;
4384 BrowseIPAddrRelease( addr
);
4388 //===========================================================================================================================
4389 // GetAddrInfoStressCmd
4390 //===========================================================================================================================
4394 DNSServiceRef mainRef
;
4395 DNSServiceRef sdRef
;
4396 DNSServiceFlags flags
;
4397 unsigned int interfaceIndex
;
4398 unsigned int connectionNumber
;
4399 unsigned int requestCount
;
4400 unsigned int requestCountMax
;
4401 unsigned int requestCountLimit
;
4402 unsigned int durationMinMs
;
4403 unsigned int durationMaxMs
;
4407 static void GetAddrInfoStressEvent( void *inContext
);
4408 static void DNSSD_API
4409 GetAddrInfoStressCallback(
4410 DNSServiceRef inSDRef
,
4411 DNSServiceFlags inFlags
,
4412 uint32_t inInterfaceIndex
,
4413 DNSServiceErrorType inError
,
4414 const char * inHostname
,
4415 const struct sockaddr
* inSockAddr
,
4419 static void GetAddrInfoStressCmd( void )
4422 GAIStressContext
* context
= NULL
;
4424 DNSServiceFlags flags
;
4426 char ifName
[ kInterfaceNameBufLen
];
4427 char time
[ kTimestampBufLen
];
4429 if( gGAIStress_TestDurationSecs
< 0 )
4431 FPrintF( stdout
, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs
);
4435 if( gGAIStress_ConnectionCount
<= 0 )
4437 FPrintF( stdout
, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount
);
4441 if( gGAIStress_DurationMinMs
<= 0 )
4443 FPrintF( stdout
, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs
);
4447 if( gGAIStress_DurationMaxMs
<= 0 )
4449 FPrintF( stdout
, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs
);
4453 if( gGAIStress_DurationMinMs
> gGAIStress_DurationMaxMs
)
4455 FPrintF( stdout
, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
4456 gGAIStress_DurationMinMs
, gGAIStress_DurationMaxMs
);
4460 if( gGAIStress_RequestCountMax
<= 0 )
4462 FPrintF( stdout
, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax
);
4469 flags
= GetDNSSDFlagsFromOpts();
4471 // Set interface index.
4473 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
4474 require_noerr_quiet( err
, exit
);
4476 for( i
= 0; i
< gGAIStress_ConnectionCount
; ++i
)
4478 context
= (GAIStressContext
*) calloc( 1, sizeof( *context
) );
4479 require_action( context
, exit
, err
= kNoMemoryErr
);
4481 context
->flags
= flags
;
4482 context
->interfaceIndex
= ifIndex
;
4483 context
->connectionNumber
= (unsigned int)( i
+ 1 );
4484 context
->requestCountMax
= (unsigned int) gGAIStress_RequestCountMax
;
4485 context
->durationMinMs
= (unsigned int) gGAIStress_DurationMinMs
;
4486 context
->durationMaxMs
= (unsigned int) gGAIStress_DurationMaxMs
;
4488 dispatch_async_f( dispatch_get_main_queue(), context
, GetAddrInfoStressEvent
);
4492 if( gGAIStress_TestDurationSecs
> 0 )
4494 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs
), dispatch_get_main_queue(), NULL
, Exit
);
4497 FPrintF( stdout
, "Flags: %#{flags}\n", flags
, kDNSServiceFlagsDescriptors
);
4498 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
4499 FPrintF( stdout
, "Test duration: " );
4500 if( gGAIStress_TestDurationSecs
== 0 )
4502 FPrintF( stdout
, "∞\n" );
4506 FPrintF( stdout
, "%d s\n", gGAIStress_TestDurationSecs
);
4508 FPrintF( stdout
, "Connection count: %d\n", gGAIStress_ConnectionCount
);
4509 FPrintF( stdout
, "Request duration min: %d ms\n", gGAIStress_DurationMinMs
);
4510 FPrintF( stdout
, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs
);
4511 FPrintF( stdout
, "Request count max: %d\n", gGAIStress_RequestCountMax
);
4512 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
4513 FPrintF( stdout
, "---\n" );
4518 FreeNullSafe( context
);
4519 if( err
) exit( 1 );
4522 //===========================================================================================================================
4523 // GetAddrInfoStressEvent
4524 //===========================================================================================================================
4526 #define kStressRandStrLen 5
4528 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
4530 static void GetAddrInfoStressEvent( void *inContext
)
4532 GAIStressContext
* const context
= (GAIStressContext
*) inContext
;
4534 DNSServiceRef sdRef
;
4535 unsigned int nextMs
;
4536 char randomStr
[ kStressRandStrLen
+ 1 ];
4537 char hostname
[ kStressRandStrLen
+ 4 + 1 ];
4538 char time
[ kTimestampBufLen
];
4539 Boolean isConnectionNew
= false;
4540 static Boolean printedHeader
= false;
4542 if( !context
->mainRef
|| ( context
->requestCount
>= context
->requestCountLimit
) )
4544 DNSServiceForget( &context
->mainRef
);
4545 context
->sdRef
= NULL
;
4546 context
->requestCount
= 0;
4547 context
->requestCountLimit
= RandomRange( 1, context
->requestCountMax
);
4549 err
= DNSServiceCreateConnection( &context
->mainRef
);
4550 require_noerr( err
, exit
);
4552 err
= DNSServiceSetDispatchQueue( context
->mainRef
, dispatch_get_main_queue() );
4553 require_noerr( err
, exit
);
4555 isConnectionNew
= true;
4558 RandomString( kLowercaseAlphaCharSet
, sizeof_string( kLowercaseAlphaCharSet
), 2, kStressRandStrLen
, randomStr
);
4559 SNPrintF( hostname
, sizeof( hostname
), "%s.com", randomStr
);
4561 nextMs
= RandomRange( context
->durationMinMs
, context
->durationMaxMs
);
4563 if( !printedHeader
)
4565 FPrintF( stdout
, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
4566 printedHeader
= true;
4568 FPrintF( stdout
, "%-26s %3u%c %9s %8u\n",
4569 GetTimestampStr( time
), context
->connectionNumber
, isConnectionNew
? '*': ' ', hostname
, nextMs
);
4571 DNSServiceForget( &context
->sdRef
);
4572 sdRef
= context
->mainRef
;
4573 err
= DNSServiceGetAddrInfo( &sdRef
, context
->flags
| kDNSServiceFlagsShareConnection
, context
->interfaceIndex
,
4574 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, hostname
, GetAddrInfoStressCallback
, NULL
);
4575 require_noerr( err
, exit
);
4576 context
->sdRef
= sdRef
;
4578 context
->requestCount
++;
4580 dispatch_after_f( dispatch_time_milliseconds( nextMs
), dispatch_get_main_queue(), context
, GetAddrInfoStressEvent
);
4583 if( err
) exit( 1 );
4586 //===========================================================================================================================
4587 // GetAddrInfoStressCallback
4588 //===========================================================================================================================
4590 static void DNSSD_API
4591 GetAddrInfoStressCallback(
4592 DNSServiceRef inSDRef
,
4593 DNSServiceFlags inFlags
,
4594 uint32_t inInterfaceIndex
,
4595 DNSServiceErrorType inError
,
4596 const char * inHostname
,
4597 const struct sockaddr
* inSockAddr
,
4603 Unused( inInterfaceIndex
);
4605 Unused( inHostname
);
4606 Unused( inSockAddr
);
4608 Unused( inContext
);
4611 //===========================================================================================================================
4613 //===========================================================================================================================
4619 sockaddr_ip serverAddr
;
4625 dispatch_source_t readSource
;
4632 Boolean printRawRData
; // True if RDATA results are not to be formatted.
4633 uint8_t msgBuf
[ 512 ];
4637 static void DNSQueryPrintPrologue( const DNSQueryContext
*inContext
);
4638 static void DNSQueryReadHandler( void *inContext
);
4639 static void DNSQueryCancelHandler( void *inContext
);
4641 static void DNSQueryCmd( void )
4644 DNSQueryContext
* context
= NULL
;
4646 size_t msgLen
, sendLen
;
4648 // Check command parameters.
4650 if( gDNSQuery_TimeLimitSecs
< -1 )
4652 FPrintF( stdout
, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs
);
4656 if( ( gDNSQuery_Flags
< INT16_MIN
) || ( gDNSQuery_Flags
> UINT16_MAX
) )
4658 FPrintF( stdout
, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags
);
4665 context
= (DNSQueryContext
*) calloc( 1, sizeof( *context
) );
4666 require_action( context
, exit
, err
= kNoMemoryErr
);
4668 context
->name
= gDNSQuery_Name
;
4669 context
->sock
= kInvalidSocketRef
;
4670 context
->timeLimitSecs
= gDNSQuery_TimeLimitSecs
;
4671 context
->queryID
= (uint16_t) Random32();
4672 context
->useTCP
= gDNSQuery_UseTCP
? true : false;
4673 context
->printRawRData
= gDNSQuery_RawRData
? true : false;
4675 #if( TARGET_OS_DARWIN )
4676 if( gDNSQuery_Server
)
4679 err
= StringToSockAddr( gDNSQuery_Server
, &context
->serverAddr
, sizeof( context
->serverAddr
), NULL
);
4680 require_noerr( err
, exit
);
4682 #if( TARGET_OS_DARWIN )
4685 err
= GetDefaultDNSServer( &context
->serverAddr
);
4686 require_noerr( err
, exit
);
4689 if( SockAddrGetPort( &context
->serverAddr
) == 0 ) SockAddrSetPort( &context
->serverAddr
, kDNSPort
);
4691 err
= RecordTypeFromArgString( gDNSQuery_Type
, &context
->type
);
4692 require_noerr( err
, exit
);
4694 // Write query message.
4696 check_compile_time_code( sizeof( context
->msgBuf
) >= ( kDNSQueryMessageMaxLen
+ 2 ) );
4698 msgPtr
= context
->useTCP
? &context
->msgBuf
[ 2 ] : context
->msgBuf
;
4699 err
= WriteDNSQueryMessage( msgPtr
, context
->queryID
, (uint16_t) gDNSQuery_Flags
, context
->name
, context
->type
,
4700 kDNSServiceClass_IN
, &msgLen
);
4701 require_noerr( err
, exit
);
4702 check( msgLen
<= UINT16_MAX
);
4704 if( context
->useTCP
)
4706 WriteBig16( context
->msgBuf
, msgLen
);
4707 sendLen
= 2 + msgLen
;
4714 DNSQueryPrintPrologue( context
);
4716 if( gDNSQuery_Verbose
)
4718 FPrintF( stdout
, "DNS message to send:\n\n" );
4719 PrintUDNSMessage( msgPtr
, msgLen
, false );
4720 FPrintF( stdout
, "---\n" );
4723 if( context
->useTCP
)
4725 // Create TCP socket.
4727 context
->sock
= socket( context
->serverAddr
.sa
.sa_family
, SOCK_STREAM
, IPPROTO_TCP
);
4728 err
= map_socket_creation_errno( context
->sock
);
4729 require_noerr( err
, exit
);
4731 err
= SocketConnect( context
->sock
, &context
->serverAddr
, 5 );
4732 require_noerr( err
, exit
);
4736 // Create UDP socket.
4738 err
= UDPClientSocketOpen( AF_UNSPEC
, &context
->serverAddr
, 0, -1, NULL
, &context
->sock
);
4739 require_noerr( err
, exit
);
4742 context
->sendTicks
= UpTicks();
4743 err
= SocketWriteAll( context
->sock
, context
->msgBuf
, sendLen
, 5 );
4744 require_noerr( err
, exit
);
4746 if( context
->timeLimitSecs
== 0 ) goto exit
;
4748 err
= DispatchReadSourceCreate( context
->sock
, DNSQueryReadHandler
, DNSQueryCancelHandler
, context
,
4749 &context
->readSource
);
4750 require_noerr( err
, exit
);
4751 dispatch_resume( context
->readSource
);
4753 if( context
->timeLimitSecs
> 0 )
4755 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
4763 dispatch_source_forget( &context
->readSource
);
4764 ForgetSocket( &context
->sock
);
4767 if( err
) exit( 1 );
4770 //===========================================================================================================================
4771 // DNSQueryPrintPrologue
4772 //===========================================================================================================================
4774 static void DNSQueryPrintPrologue( const DNSQueryContext
*inContext
)
4776 const int timeLimitSecs
= inContext
->timeLimitSecs
;
4777 char time
[ kTimestampBufLen
];
4779 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
4780 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->type
), inContext
->type
);
4781 FPrintF( stdout
, "Server: %##a\n", &inContext
->serverAddr
);
4782 FPrintF( stdout
, "Transport: %s\n", inContext
->useTCP
? "TCP" : "UDP" );
4783 FPrintF( stdout
, "Time limit: " );
4784 if( timeLimitSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
4785 else FPrintF( stdout
, "∞\n" );
4786 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
4787 FPrintF( stdout
, "---\n" );
4790 //===========================================================================================================================
4791 // DNSQueryReadHandler
4792 //===========================================================================================================================
4794 static void DNSQueryReadHandler( void *inContext
)
4797 const uint64_t nowTicks
= UpTicks();
4798 DNSQueryContext
* const context
= (DNSQueryContext
*) inContext
;
4799 char time
[ kTimestampBufLen
];
4801 GetTimestampStr( time
);
4803 if( context
->useTCP
)
4805 if( !context
->haveTCPLen
)
4807 err
= SocketReadData( context
->sock
, &context
->msgBuf
, 2, &context
->msgOffset
);
4808 if( err
== EWOULDBLOCK
) { err
= kNoErr
; goto exit
; }
4809 require_noerr( err
, exit
);
4811 context
->msgOffset
= 0;
4812 context
->msgLen
= ReadBig16( context
->msgBuf
);
4813 context
->haveTCPLen
= true;
4814 if( context
->msgLen
<= sizeof( context
->msgBuf
) )
4816 context
->msgPtr
= context
->msgBuf
;
4820 context
->msgPtr
= (uint8_t *) malloc( context
->msgLen
);
4821 require_action( context
->msgPtr
, exit
, err
= kNoMemoryErr
);
4825 err
= SocketReadData( context
->sock
, context
->msgPtr
, context
->msgLen
, &context
->msgOffset
);
4826 if( err
== EWOULDBLOCK
) { err
= kNoErr
; goto exit
; }
4827 require_noerr( err
, exit
);
4828 context
->msgOffset
= 0;
4829 context
->haveTCPLen
= false;
4833 sockaddr_ip fromAddr
;
4835 context
->msgPtr
= context
->msgBuf
;
4836 err
= SocketRecvFrom( context
->sock
, context
->msgPtr
, sizeof( context
->msgBuf
), &context
->msgLen
, &fromAddr
,
4837 sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
4838 require_noerr( err
, exit
);
4840 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
4843 FPrintF( stdout
, "Receive time: %s\n", time
);
4844 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
4845 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
4846 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
4847 PrintUDNSMessage( context
->msgPtr
, context
->msgLen
, context
->printRawRData
);
4849 if( ( context
->msgLen
>= kDNSHeaderLength
) && ( DNSHeaderGetID( (DNSHeader
*) context
->msgPtr
) == context
->queryID
) )
4851 Exit( kExitReason_ReceivedResponse
);
4855 if( err
) dispatch_source_forget( &context
->readSource
);
4858 //===========================================================================================================================
4859 // DNSQueryCancelHandler
4860 //===========================================================================================================================
4862 static void DNSQueryCancelHandler( void *inContext
)
4864 DNSQueryContext
* const context
= (DNSQueryContext
*) inContext
;
4866 check( !context
->readSource
);
4867 ForgetSocket( &context
->sock
);
4868 if( context
->msgPtr
!= context
->msgBuf
) ForgetMem( &context
->msgPtr
);
4870 dispatch_async_f( dispatch_get_main_queue(), NULL
, Exit
);
4873 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
4874 //===========================================================================================================================
4876 //===========================================================================================================================
4878 #define kDNSCryptPort 443
4880 #define kDNSCryptMinPadLength 8
4881 #define kDNSCryptMaxPadLength 256
4882 #define kDNSCryptBlockSize 64
4883 #define kDNSCryptCertMinimumLength 124
4884 #define kDNSCryptClientMagicLength 8
4885 #define kDNSCryptResolverMagicLength 8
4886 #define kDNSCryptHalfNonceLength 12
4887 #define kDNSCryptCertMagicLength 4
4889 check_compile_time( ( kDNSCryptHalfNonceLength
* 2 ) == crypto_box_NONCEBYTES
);
4891 static const uint8_t kDNSCryptCertMagic
[ kDNSCryptCertMagicLength
] = { 'D', 'N', 'S', 'C' };
4892 static const uint8_t kDNSCryptResolverMagic
[ kDNSCryptResolverMagicLength
] =
4894 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
4899 uint8_t certMagic
[ kDNSCryptCertMagicLength
];
4900 uint8_t esVersion
[ 2 ];
4901 uint8_t minorVersion
[ 2 ];
4902 uint8_t signature
[ crypto_sign_BYTES
];
4903 uint8_t publicKey
[ crypto_box_PUBLICKEYBYTES
];
4904 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
4905 uint8_t serial
[ 4 ];
4906 uint8_t startTime
[ 4 ];
4907 uint8_t endTime
[ 4 ];
4908 uint8_t extensions
[ 1 ]; // Variably-sized extension data.
4912 check_compile_time( offsetof( DNSCryptCert
, extensions
) == kDNSCryptCertMinimumLength
);
4916 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
4917 uint8_t clientPublicKey
[ crypto_box_PUBLICKEYBYTES
];
4918 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
4919 uint8_t poly1305MAC
[ 16 ];
4921 } DNSCryptQueryHeader
;
4923 check_compile_time( sizeof( DNSCryptQueryHeader
) == 68 );
4924 check_compile_time( sizeof( DNSCryptQueryHeader
) >= crypto_box_ZEROBYTES
);
4925 check_compile_time( ( sizeof( DNSCryptQueryHeader
) - crypto_box_ZEROBYTES
+ crypto_box_BOXZEROBYTES
) ==
4926 offsetof( DNSCryptQueryHeader
, poly1305MAC
) );
4930 uint8_t resolverMagic
[ kDNSCryptResolverMagicLength
];
4931 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
4932 uint8_t resolverNonce
[ kDNSCryptHalfNonceLength
];
4933 uint8_t poly1305MAC
[ 16 ];
4935 } DNSCryptResponseHeader
;
4937 check_compile_time( sizeof( DNSCryptResponseHeader
) == 48 );
4938 check_compile_time( offsetof( DNSCryptResponseHeader
, poly1305MAC
) >= crypto_box_BOXZEROBYTES
);
4939 check_compile_time( ( offsetof( DNSCryptResponseHeader
, poly1305MAC
) - crypto_box_BOXZEROBYTES
+ crypto_box_ZEROBYTES
) ==
4940 sizeof( DNSCryptResponseHeader
) );
4944 sockaddr_ip serverAddr
;
4946 const char * providerName
;
4948 const uint8_t * certPtr
;
4950 dispatch_source_t readSource
;
4955 Boolean printRawRData
;
4956 uint8_t serverPublicSignKey
[ crypto_sign_PUBLICKEYBYTES
];
4957 uint8_t serverPublicKey
[ crypto_box_PUBLICKEYBYTES
];
4958 uint8_t clientPublicKey
[ crypto_box_PUBLICKEYBYTES
];
4959 uint8_t clientSecretKey
[ crypto_box_SECRETKEYBYTES
];
4960 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
4961 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
4962 uint8_t nmKey
[ crypto_box_BEFORENMBYTES
];
4963 uint8_t msgBuf
[ 512 ];
4967 static void DNSCryptReceiveCertHandler( void *inContext
);
4968 static void DNSCryptReceiveResponseHandler( void *inContext
);
4969 static void DNSCryptProceed( void *inContext
);
4970 static OSStatus
DNSCryptProcessCert( DNSCryptContext
*inContext
);
4971 static OSStatus
DNSCryptBuildQuery( DNSCryptContext
*inContext
);
4972 static OSStatus
DNSCryptSendQuery( DNSCryptContext
*inContext
);
4973 static void DNSCryptPrintCertificate( const DNSCryptCert
*inCert
, size_t inLen
);
4975 static void DNSCryptCmd( void )
4978 DNSCryptContext
* context
= NULL
;
4979 size_t writtenBytes
;
4981 SocketContext
* sockContext
;
4982 SocketRef sock
= kInvalidSocketRef
;
4985 // Check command parameters.
4987 if( gDNSCrypt_TimeLimitSecs
< -1 )
4989 FPrintF( stdout
, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs
);
4996 context
= (DNSCryptContext
*) calloc( 1, sizeof( *context
) );
4997 require_action( context
, exit
, err
= kNoMemoryErr
);
4999 context
->providerName
= gDNSCrypt_ProviderName
;
5000 context
->qname
= gDNSCrypt_Name
;
5001 context
->timeLimitSecs
= gDNSCrypt_TimeLimitSecs
;
5002 context
->printRawRData
= gDNSCrypt_RawRData
? true : false;
5004 err
= crypto_box_keypair( context
->clientPublicKey
, context
->clientSecretKey
);
5005 require_noerr( err
, exit
);
5007 err
= HexToData( gDNSCrypt_ProviderKey
, kSizeCString
, kHexToData_DefaultFlags
,
5008 context
->serverPublicSignKey
, sizeof( context
->serverPublicSignKey
), &writtenBytes
, &totalBytes
, &ptr
);
5009 if( err
|| ( *ptr
!= '\0' ) )
5011 FPrintF( stderr
, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey
);
5014 else if( totalBytes
!= sizeof( context
->serverPublicSignKey
) )
5016 FPrintF( stderr
, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
5017 totalBytes
, sizeof( context
->serverPublicSignKey
) );
5021 check( writtenBytes
== totalBytes
);
5023 err
= StringToSockAddr( gDNSCrypt_Server
, &context
->serverAddr
, sizeof( context
->serverAddr
), NULL
);
5024 require_noerr( err
, exit
);
5025 if( SockAddrGetPort( &context
->serverAddr
) == 0 ) SockAddrSetPort( &context
->serverAddr
, kDNSCryptPort
);
5027 err
= RecordTypeFromArgString( gDNSCrypt_Type
, &context
->qtype
);
5028 require_noerr( err
, exit
);
5030 // Write query message.
5032 context
->queryID
= (uint16_t) Random32();
5033 err
= WriteDNSQueryMessage( context
->msgBuf
, context
->queryID
, kDNSHeaderFlag_RecursionDesired
, context
->providerName
,
5034 kDNSServiceType_TXT
, kDNSServiceClass_IN
, &context
->msgLen
);
5035 require_noerr( err
, exit
);
5037 // Create UDP socket.
5039 err
= UDPClientSocketOpen( AF_UNSPEC
, &context
->serverAddr
, 0, -1, NULL
, &sock
);
5040 require_noerr( err
, exit
);
5044 context
->sendTicks
= UpTicks();
5045 err
= SocketWriteAll( sock
, context
->msgBuf
, context
->msgLen
, 5 );
5046 require_noerr( err
, exit
);
5048 sockContext
= (SocketContext
*) calloc( 1, sizeof( *sockContext
) );
5049 require_action( sockContext
, exit
, err
= kNoMemoryErr
);
5051 err
= DispatchReadSourceCreate( sock
, DNSCryptReceiveCertHandler
, SocketContextCancelHandler
, sockContext
,
5052 &context
->readSource
);
5053 if( err
) ForgetMem( &sockContext
);
5054 require_noerr( err
, exit
);
5056 sockContext
->context
= context
;
5057 sockContext
->sock
= sock
;
5058 sock
= kInvalidSocketRef
;
5059 dispatch_resume( context
->readSource
);
5061 if( context
->timeLimitSecs
> 0 )
5063 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
5069 if( context
) free( context
);
5070 ForgetSocket( &sock
);
5071 if( err
) exit( 1 );
5074 //===========================================================================================================================
5075 // DNSCryptReceiveCertHandler
5076 //===========================================================================================================================
5078 static void DNSCryptReceiveCertHandler( void *inContext
)
5081 const uint64_t nowTicks
= UpTicks();
5082 SocketContext
* const sockContext
= (SocketContext
*) inContext
;
5083 DNSCryptContext
* const context
= (DNSCryptContext
*) sockContext
->context
;
5084 const DNSHeader
* hdr
;
5085 sockaddr_ip fromAddr
;
5086 const uint8_t * ptr
;
5087 const uint8_t * txtPtr
;
5089 unsigned int answerCount
, i
;
5090 uint8_t targetName
[ kDomainNameLengthMax
];
5091 char time
[ kTimestampBufLen
];
5093 GetTimestampStr( time
);
5095 dispatch_source_forget( &context
->readSource
);
5097 err
= SocketRecvFrom( sockContext
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &context
->msgLen
,
5098 &fromAddr
, sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
5099 require_noerr( err
, exit
);
5100 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
5102 FPrintF( stdout
, "Receive time: %s\n", time
);
5103 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
5104 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
5105 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
5107 PrintUDNSMessage( context
->msgBuf
, context
->msgLen
, context
->printRawRData
);
5109 require_action_quiet( context
->msgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
5111 hdr
= (DNSHeader
*) context
->msgBuf
;
5112 require_action_quiet( DNSHeaderGetID( hdr
) == context
->queryID
, exit
, err
= kMismatchErr
);
5114 err
= DNSMessageGetAnswerSection( context
->msgBuf
, context
->msgLen
, &ptr
);
5115 require_noerr( err
, exit
);
5117 targetName
[ 0 ] = 0;
5118 err
= DomainNameAppendString( targetName
, context
->providerName
, NULL
);
5119 require_noerr( err
, exit
);
5121 answerCount
= DNSHeaderGetAnswerCount( hdr
);
5122 for( i
= 0; i
< answerCount
; ++i
)
5126 uint8_t name
[ kDomainNameLengthMax
];
5128 err
= DNSMessageExtractRecord( context
->msgBuf
, context
->msgLen
, ptr
, name
, &type
, &class, NULL
, &txtPtr
, &txtLen
,
5130 require_noerr( err
, exit
);
5132 if( ( type
== kDNSServiceType_TXT
) && ( class == kDNSServiceClass_IN
) && DomainNameEqual( name
, targetName
) )
5138 if( txtLen
< ( 1 + kDNSCryptCertMinimumLength
) )
5140 FPrintF( stderr
, "TXT record length is too short (%u < %u)\n", txtLen
, kDNSCryptCertMinimumLength
+ 1 );
5144 if( txtPtr
[ 0 ] < kDNSCryptCertMinimumLength
)
5146 FPrintF( stderr
, "TXT record value length is too short (%u < %u)\n", txtPtr
[ 0 ], kDNSCryptCertMinimumLength
);
5151 context
->certLen
= txtPtr
[ 0 ];
5152 context
->certPtr
= &txtPtr
[ 1 ];
5154 dispatch_async_f( dispatch_get_main_queue(), context
, DNSCryptProceed
);
5157 if( err
) Exit( NULL
);
5160 //===========================================================================================================================
5161 // DNSCryptReceiveResponseHandler
5162 //===========================================================================================================================
5164 static void DNSCryptReceiveResponseHandler( void *inContext
)
5167 const uint64_t nowTicks
= UpTicks();
5168 SocketContext
* const sockContext
= (SocketContext
*) inContext
;
5169 DNSCryptContext
* const context
= (DNSCryptContext
*) sockContext
->context
;
5170 sockaddr_ip fromAddr
;
5171 DNSCryptResponseHeader
* hdr
;
5172 const uint8_t * end
;
5173 uint8_t * ciphertext
;
5174 uint8_t * plaintext
;
5175 const uint8_t * response
;
5176 char time
[ kTimestampBufLen
];
5177 uint8_t nonce
[ crypto_box_NONCEBYTES
];
5179 GetTimestampStr( time
);
5181 dispatch_source_forget( &context
->readSource
);
5183 err
= SocketRecvFrom( sockContext
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &context
->msgLen
,
5184 &fromAddr
, sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
5185 require_noerr( err
, exit
);
5186 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
5188 FPrintF( stdout
, "Receive time: %s\n", time
);
5189 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
5190 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
5191 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
5193 if( context
->msgLen
< sizeof( DNSCryptResponseHeader
) )
5195 FPrintF( stderr
, "DNSCrypt response is too short.\n" );
5200 hdr
= (DNSCryptResponseHeader
*) context
->msgBuf
;
5202 if( memcmp( hdr
->resolverMagic
, kDNSCryptResolverMagic
, kDNSCryptResolverMagicLength
) != 0 )
5204 FPrintF( stderr
, "DNSCrypt response resolver magic %#H != %#H\n",
5205 hdr
->resolverMagic
, kDNSCryptResolverMagicLength
, INT_MAX
,
5206 kDNSCryptResolverMagic
, kDNSCryptResolverMagicLength
, INT_MAX
);
5211 if( memcmp( hdr
->clientNonce
, context
->clientNonce
, kDNSCryptHalfNonceLength
) != 0 )
5213 FPrintF( stderr
, "DNSCrypt response client nonce mismatch.\n" );
5218 memcpy( nonce
, hdr
->clientNonce
, crypto_box_NONCEBYTES
);
5220 ciphertext
= hdr
->poly1305MAC
- crypto_box_BOXZEROBYTES
;
5221 memset( ciphertext
, 0, crypto_box_BOXZEROBYTES
);
5223 plaintext
= (uint8_t *)( hdr
+ 1 ) - crypto_box_ZEROBYTES
;
5224 check( plaintext
== ciphertext
);
5226 end
= context
->msgBuf
+ context
->msgLen
;
5228 err
= crypto_box_open_afternm( plaintext
, ciphertext
, (size_t)( end
- ciphertext
), nonce
, context
->nmKey
);
5229 require_noerr( err
, exit
);
5231 response
= plaintext
+ crypto_box_ZEROBYTES
;
5232 PrintUDNSMessage( response
, (size_t)( end
- response
), context
->printRawRData
);
5233 Exit( kExitReason_ReceivedResponse
);
5236 if( err
) Exit( NULL
);
5239 //===========================================================================================================================
5241 //===========================================================================================================================
5243 static void DNSCryptProceed( void *inContext
)
5246 DNSCryptContext
* const context
= (DNSCryptContext
*) inContext
;
5248 err
= DNSCryptProcessCert( context
);
5249 require_noerr_quiet( err
, exit
);
5251 err
= DNSCryptBuildQuery( context
);
5252 require_noerr_quiet( err
, exit
);
5254 err
= DNSCryptSendQuery( context
);
5255 require_noerr_quiet( err
, exit
);
5258 if( err
) Exit( NULL
);
5261 //===========================================================================================================================
5262 // DNSCryptProcessCert
5263 //===========================================================================================================================
5265 static OSStatus
DNSCryptProcessCert( DNSCryptContext
*inContext
)
5268 const DNSCryptCert
* const cert
= (DNSCryptCert
*) inContext
->certPtr
;
5269 const uint8_t * const certEnd
= inContext
->certPtr
+ inContext
->certLen
;
5271 time_t startTimeSecs
, endTimeSecs
;
5274 unsigned long long tempLen
;
5276 DNSCryptPrintCertificate( cert
, inContext
->certLen
);
5278 if( memcmp( cert
->certMagic
, kDNSCryptCertMagic
, kDNSCryptCertMagicLength
) != 0 )
5280 FPrintF( stderr
, "DNSCrypt certificate magic %#H != %#H\n",
5281 cert
->certMagic
, kDNSCryptCertMagicLength
, INT_MAX
,
5282 kDNSCryptCertMagic
, kDNSCryptCertMagicLength
, INT_MAX
);
5287 startTimeSecs
= (time_t) ReadBig32( cert
->startTime
);
5288 endTimeSecs
= (time_t) ReadBig32( cert
->endTime
);
5290 gettimeofday( &now
, NULL
);
5291 if( now
.tv_sec
< startTimeSecs
)
5293 FPrintF( stderr
, "DNSCrypt certificate start time is in the future.\n" );
5297 if( now
.tv_sec
>= endTimeSecs
)
5299 FPrintF( stderr
, "DNSCrypt certificate has expired.\n" );
5304 signedLen
= (size_t)( certEnd
- cert
->signature
);
5305 tempBuf
= (uint8_t *) malloc( signedLen
);
5306 require_action( tempBuf
, exit
, err
= kNoMemoryErr
);
5307 err
= crypto_sign_open( tempBuf
, &tempLen
, cert
->signature
, signedLen
, inContext
->serverPublicSignKey
);
5311 FPrintF( stderr
, "DNSCrypt certificate failed verification.\n" );
5312 err
= kAuthenticationErr
;
5316 memcpy( inContext
->serverPublicKey
, cert
->publicKey
, crypto_box_PUBLICKEYBYTES
);
5317 memcpy( inContext
->clientMagic
, cert
->clientMagic
, kDNSCryptClientMagicLength
);
5319 err
= crypto_box_beforenm( inContext
->nmKey
, inContext
->serverPublicKey
, inContext
->clientSecretKey
);
5320 require_noerr( err
, exit
);
5322 inContext
->certPtr
= NULL
;
5323 inContext
->certLen
= 0;
5324 inContext
->msgLen
= 0;
5330 //===========================================================================================================================
5331 // DNSCryptBuildQuery
5332 //===========================================================================================================================
5334 static OSStatus
DNSCryptPadQuery( uint8_t *inMsgPtr
, size_t inMsgLen
, size_t inMaxLen
, size_t *outPaddedLen
);
5336 static OSStatus
DNSCryptBuildQuery( DNSCryptContext
*inContext
)
5339 DNSCryptQueryHeader
* const hdr
= (DNSCryptQueryHeader
*) inContext
->msgBuf
;
5340 uint8_t * const queryPtr
= (uint8_t *)( hdr
+ 1 );
5342 size_t paddedQueryLen
;
5343 const uint8_t * const msgLimit
= inContext
->msgBuf
+ sizeof( inContext
->msgBuf
);
5344 const uint8_t * padLimit
;
5345 uint8_t nonce
[ crypto_box_NONCEBYTES
];
5347 check_compile_time_code( sizeof( inContext
->msgBuf
) >= ( sizeof( DNSCryptQueryHeader
) + kDNSQueryMessageMaxLen
) );
5349 inContext
->queryID
= (uint16_t) Random32();
5350 err
= WriteDNSQueryMessage( queryPtr
, inContext
->queryID
, kDNSHeaderFlag_RecursionDesired
, inContext
->qname
,
5351 inContext
->qtype
, kDNSServiceClass_IN
, &queryLen
);
5352 require_noerr( err
, exit
);
5354 padLimit
= &queryPtr
[ queryLen
+ kDNSCryptMaxPadLength
];
5355 if( padLimit
> msgLimit
) padLimit
= msgLimit
;
5357 err
= DNSCryptPadQuery( queryPtr
, queryLen
, (size_t)( padLimit
- queryPtr
), &paddedQueryLen
);
5358 require_noerr( err
, exit
);
5360 memset( queryPtr
- crypto_box_ZEROBYTES
, 0, crypto_box_ZEROBYTES
);
5361 RandomBytes( inContext
->clientNonce
, kDNSCryptHalfNonceLength
);
5362 memcpy( nonce
, inContext
->clientNonce
, kDNSCryptHalfNonceLength
);
5363 memset( &nonce
[ kDNSCryptHalfNonceLength
], 0, kDNSCryptHalfNonceLength
);
5365 err
= crypto_box_afternm( queryPtr
- crypto_box_ZEROBYTES
, queryPtr
- crypto_box_ZEROBYTES
,
5366 paddedQueryLen
+ crypto_box_ZEROBYTES
, nonce
, inContext
->nmKey
);
5367 require_noerr( err
, exit
);
5369 memcpy( hdr
->clientMagic
, inContext
->clientMagic
, kDNSCryptClientMagicLength
);
5370 memcpy( hdr
->clientPublicKey
, inContext
->clientPublicKey
, crypto_box_PUBLICKEYBYTES
);
5371 memcpy( hdr
->clientNonce
, nonce
, kDNSCryptHalfNonceLength
);
5373 inContext
->msgLen
= (size_t)( &queryPtr
[ paddedQueryLen
] - inContext
->msgBuf
);
5379 static OSStatus
DNSCryptPadQuery( uint8_t *inMsgPtr
, size_t inMsgLen
, size_t inMaxLen
, size_t *outPaddedLen
)
5384 require_action_quiet( ( inMsgLen
+ kDNSCryptMinPadLength
) <= inMaxLen
, exit
, err
= kSizeErr
);
5386 paddedLen
= inMsgLen
+ kDNSCryptMinPadLength
+
5387 arc4random_uniform( (uint32_t)( inMaxLen
- ( inMsgLen
+ kDNSCryptMinPadLength
) + 1 ) );
5388 paddedLen
+= ( kDNSCryptBlockSize
- ( paddedLen
% kDNSCryptBlockSize
) );
5389 if( paddedLen
> inMaxLen
) paddedLen
= inMaxLen
;
5391 inMsgPtr
[ inMsgLen
] = 0x80;
5392 memset( &inMsgPtr
[ inMsgLen
+ 1 ], 0, paddedLen
- ( inMsgLen
+ 1 ) );
5394 if( outPaddedLen
) *outPaddedLen
= paddedLen
;
5401 //===========================================================================================================================
5402 // DNSCryptSendQuery
5403 //===========================================================================================================================
5405 static OSStatus
DNSCryptSendQuery( DNSCryptContext
*inContext
)
5408 SocketContext
* sockContext
;
5409 SocketRef sock
= kInvalidSocketRef
;
5411 check( inContext
->msgLen
> 0 );
5412 check( !inContext
->readSource
);
5414 err
= UDPClientSocketOpen( AF_UNSPEC
, &inContext
->serverAddr
, 0, -1, NULL
, &sock
);
5415 require_noerr( err
, exit
);
5417 inContext
->sendTicks
= UpTicks();
5418 err
= SocketWriteAll( sock
, inContext
->msgBuf
, inContext
->msgLen
, 5 );
5419 require_noerr( err
, exit
);
5421 sockContext
= (SocketContext
*) calloc( 1, sizeof( *sockContext
) );
5422 require_action( sockContext
, exit
, err
= kNoMemoryErr
);
5424 err
= DispatchReadSourceCreate( sock
, DNSCryptReceiveResponseHandler
, SocketContextCancelHandler
, sockContext
,
5425 &inContext
->readSource
);
5426 if( err
) ForgetMem( &sockContext
);
5427 require_noerr( err
, exit
);
5429 sockContext
->context
= inContext
;
5430 sockContext
->sock
= sock
;
5431 sock
= kInvalidSocketRef
;
5433 dispatch_resume( inContext
->readSource
);
5436 ForgetSocket( &sock
);
5440 //===========================================================================================================================
5441 // DNSCryptPrintCertificate
5442 //===========================================================================================================================
5444 #define kCertTimeStrBufLen 32
5446 static char * CertTimeStr( time_t inTime
, char inBuffer
[ kCertTimeStrBufLen
] );
5448 static void DNSCryptPrintCertificate( const DNSCryptCert
*inCert
, size_t inLen
)
5450 time_t startTime
, endTime
;
5452 char timeBuf
[ kCertTimeStrBufLen
];
5454 check( inLen
>= kDNSCryptCertMinimumLength
);
5456 startTime
= (time_t) ReadBig32( inCert
->startTime
);
5457 endTime
= (time_t) ReadBig32( inCert
->endTime
);
5459 FPrintF( stdout
, "DNSCrypt certificate (%zu bytes):\n", inLen
);
5460 FPrintF( stdout
, "Cert Magic: %#H\n", inCert
->certMagic
, kDNSCryptCertMagicLength
, INT_MAX
);
5461 FPrintF( stdout
, "ES Version: %u\n", ReadBig16( inCert
->esVersion
) );
5462 FPrintF( stdout
, "Minor Version: %u\n", ReadBig16( inCert
->minorVersion
) );
5463 FPrintF( stdout
, "Signature: %H\n", inCert
->signature
, crypto_sign_BYTES
/ 2, INT_MAX
);
5464 FPrintF( stdout
, " %H\n", &inCert
->signature
[ crypto_sign_BYTES
/ 2 ], crypto_sign_BYTES
/ 2, INT_MAX
);
5465 FPrintF( stdout
, "Public Key: %H\n", inCert
->publicKey
, sizeof( inCert
->publicKey
), INT_MAX
);
5466 FPrintF( stdout
, "Client Magic: %H\n", inCert
->clientMagic
, kDNSCryptClientMagicLength
, INT_MAX
);
5467 FPrintF( stdout
, "Serial: %u\n", ReadBig32( inCert
->serial
) );
5468 FPrintF( stdout
, "Start Time: %u (%s)\n", (uint32_t) startTime
, CertTimeStr( startTime
, timeBuf
) );
5469 FPrintF( stdout
, "End Time: %u (%s)\n", (uint32_t) endTime
, CertTimeStr( endTime
, timeBuf
) );
5471 if( inLen
> kDNSCryptCertMinimumLength
)
5473 extLen
= (int)( inLen
- kDNSCryptCertMinimumLength
);
5474 FPrintF( stdout
, "Extensions: %.1H\n", inCert
->extensions
, extLen
, extLen
);
5476 FPrintF( stdout
, "\n" );
5479 static char * CertTimeStr( time_t inTime
, char inBuffer
[ kCertTimeStrBufLen
] )
5483 tm
= localtime( &inTime
);
5486 dlogassert( "localtime() returned a NULL pointer.\n" );
5491 strftime( inBuffer
, kCertTimeStrBufLen
, "%a %b %d %H:%M:%S %Z %Y", tm
);
5497 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
5499 //===========================================================================================================================
5501 //===========================================================================================================================
5503 #define kMDNSPort 5353
5505 #define kDefaultMDNSMessageID 0
5506 #define kDefaultMDNSQueryFlags 0
5510 const char * qnameStr
; // Name (QNAME) of the record being queried as a C string.
5511 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
5512 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
5513 int localPort
; // The port number to which the sockets are bound.
5514 int receiveSecs
; // After send, the amount of time to spend receiving.
5515 uint32_t ifIndex
; // Index of the interface over which to send the query.
5516 uint16_t qtype
; // The type (QTYPE) of the record being queried.
5517 Boolean isQU
; // True if the query is QU, i.e., requests unicast responses.
5518 Boolean allResponses
; // True if all mDNS messages received should be printed.
5519 Boolean printRawRData
; // True if RDATA should be printed as hexdumps.
5520 Boolean useIPv4
; // True if the query should be sent via IPv4 multicast.
5521 Boolean useIPv6
; // True if the query should be sent via IPv6 multicast.
5522 char ifName
[ IF_NAMESIZE
+ 1 ]; // Name of the interface over which to send the query.
5523 uint8_t qname
[ kDomainNameLengthMax
]; // Buffer to hold the QNAME in label format.
5524 uint8_t msgBuf
[ 8940 ]; // Message buffer. 8940 is max size used by mDNSResponder.
5528 static void MDNSQueryPrintPrologue( const MDNSQueryContext
*inContext
);
5529 static void MDNSQueryReadHandler( void *inContext
);
5531 static void MDNSQueryCmd( void )
5534 MDNSQueryContext
* context
;
5535 struct sockaddr_in mcastAddr4
;
5536 struct sockaddr_in6 mcastAddr6
;
5537 SocketRef sockV4
= kInvalidSocketRef
;
5538 SocketRef sockV6
= kInvalidSocketRef
;
5540 const char * ifNamePtr
;
5542 unsigned int sendCount
;
5544 // Check command parameters.
5546 if( gMDNSQuery_ReceiveSecs
< -1 )
5548 FPrintF( stdout
, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs
);
5553 context
= (MDNSQueryContext
*) calloc( 1, sizeof( *context
) );
5554 require_action( context
, exit
, err
= kNoMemoryErr
);
5556 context
->qnameStr
= gMDNSQuery_Name
;
5557 context
->receiveSecs
= gMDNSQuery_ReceiveSecs
;
5558 context
->isQU
= gMDNSQuery_IsQU
? true : false;
5559 context
->allResponses
= gMDNSQuery_AllResponses
? true : false;
5560 context
->printRawRData
= gMDNSQuery_RawRData
? true : false;
5561 context
->useIPv4
= ( gMDNSQuery_UseIPv4
|| !gMDNSQuery_UseIPv6
) ? true : false;
5562 context
->useIPv6
= ( gMDNSQuery_UseIPv6
|| !gMDNSQuery_UseIPv4
) ? true : false;
5564 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
5565 require_noerr_quiet( err
, exit
);
5567 ifNamePtr
= if_indextoname( context
->ifIndex
, context
->ifName
);
5568 require_action( ifNamePtr
, exit
, err
= kNameErr
);
5570 err
= RecordTypeFromArgString( gMDNSQuery_Type
, &context
->qtype
);
5571 require_noerr( err
, exit
);
5573 // Set up IPv4 socket.
5575 if( context
->useIPv4
)
5577 err
= ServerSocketOpen( AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
,
5578 gMDNSQuery_SourcePort
? gMDNSQuery_SourcePort
: ( context
->isQU
? context
->localPort
: kMDNSPort
),
5579 &context
->localPort
, kSocketBufferSize_DontSet
, &sockV4
);
5580 require_noerr( err
, exit
);
5582 err
= SocketSetMulticastInterface( sockV4
, ifNamePtr
, context
->ifIndex
);
5583 require_noerr( err
, exit
);
5585 err
= setsockopt( sockV4
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *) &(uint8_t){ 1 }, (socklen_t
) sizeof( uint8_t ) );
5586 err
= map_socket_noerr_errno( sockV4
, err
);
5587 require_noerr( err
, exit
);
5589 memset( &mcastAddr4
, 0, sizeof( mcastAddr4
) );
5590 SIN_LEN_SET( &mcastAddr4
);
5591 mcastAddr4
.sin_family
= AF_INET
;
5592 mcastAddr4
.sin_port
= htons( kMDNSPort
);
5593 mcastAddr4
.sin_addr
.s_addr
= htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251
5595 if( !context
->isQU
&& ( context
->localPort
== kMDNSPort
) )
5597 SocketJoinMulticast( sockV4
, &mcastAddr4
, ifNamePtr
, context
->ifIndex
);
5598 require_noerr( err
, exit
);
5602 // Set up IPv6 socket.
5604 if( context
->useIPv6
)
5606 err
= ServerSocketOpen( AF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
,
5607 gMDNSQuery_SourcePort
? gMDNSQuery_SourcePort
: ( context
->isQU
? context
->localPort
: kMDNSPort
),
5608 &context
->localPort
, kSocketBufferSize_DontSet
, &sockV6
);
5609 require_noerr( err
, exit
);
5611 err
= SocketSetMulticastInterface( sockV6
, ifNamePtr
, context
->ifIndex
);
5612 require_noerr( err
, exit
);
5614 err
= setsockopt( sockV6
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *) &(int){ 1 }, (socklen_t
) sizeof( int ) );
5615 err
= map_socket_noerr_errno( sockV6
, err
);
5616 require_noerr( err
, exit
);
5618 memset( &mcastAddr6
, 0, sizeof( mcastAddr6
) );
5619 SIN6_LEN_SET( &mcastAddr6
);
5620 mcastAddr6
.sin6_family
= AF_INET6
;
5621 mcastAddr6
.sin6_port
= htons( kMDNSPort
);
5622 mcastAddr6
.sin6_addr
.s6_addr
[ 0 ] = 0xFF; // mDNS IPv6 multicast address FF02::FB
5623 mcastAddr6
.sin6_addr
.s6_addr
[ 1 ] = 0x02;
5624 mcastAddr6
.sin6_addr
.s6_addr
[ 15 ] = 0xFB;
5626 if( !context
->isQU
&& ( context
->localPort
== kMDNSPort
) )
5628 SocketJoinMulticast( sockV6
, &mcastAddr6
, ifNamePtr
, context
->ifIndex
);
5629 require_noerr( err
, exit
);
5633 // Craft mDNS query message.
5635 check_compile_time_code( sizeof( context
->msgBuf
) >= kDNSQueryMessageMaxLen
);
5636 err
= WriteDNSQueryMessage( context
->msgBuf
, kDefaultMDNSMessageID
, kDefaultMDNSQueryFlags
, context
->qnameStr
,
5637 context
->qtype
, context
->isQU
? ( kDNSServiceClass_IN
| kQClassUnicastResponseBit
) : kDNSServiceClass_IN
, &msgLen
);
5638 require_noerr( err
, exit
);
5642 MDNSQueryPrintPrologue( context
);
5644 // Send mDNS query message.
5647 if( IsValidSocket( sockV4
) )
5649 n
= sendto( sockV4
, context
->msgBuf
, msgLen
, 0, (struct sockaddr
*) &mcastAddr4
, (socklen_t
) sizeof( mcastAddr4
) );
5650 err
= map_socket_value_errno( sockV4
, n
== (ssize_t
) msgLen
, n
);
5653 FPrintF( stderr
, "*** Failed to send query on IPv4 socket with error %#m\n", err
);
5654 ForgetSocket( &sockV4
);
5661 if( IsValidSocket( sockV6
) )
5663 n
= sendto( sockV6
, context
->msgBuf
, msgLen
, 0, (struct sockaddr
*) &mcastAddr6
, (socklen_t
) sizeof( mcastAddr6
) );
5664 err
= map_socket_value_errno( sockV6
, n
== (ssize_t
) msgLen
, n
);
5667 FPrintF( stderr
, "*** Failed to send query on IPv6 socket with error %#m\n", err
);
5668 ForgetSocket( &sockV6
);
5675 require_action_quiet( sendCount
> 0, exit
, err
= kUnexpectedErr
);
5677 // If there's no wait period after the send, then exit.
5679 if( context
->receiveSecs
== 0 ) goto exit
;
5681 // Create dispatch read sources for socket(s).
5683 if( IsValidSocket( sockV4
) )
5685 SocketContext
* sockContext
;
5687 sockContext
= (SocketContext
*) calloc( 1, sizeof( *sockContext
) );
5688 require_action( sockContext
, exit
, err
= kNoMemoryErr
);
5690 err
= DispatchReadSourceCreate( sockV4
, MDNSQueryReadHandler
, SocketContextCancelHandler
, sockContext
,
5691 &context
->readSourceV4
);
5692 if( err
) ForgetMem( &sockContext
);
5693 require_noerr( err
, exit
);
5695 sockContext
->context
= context
;
5696 sockContext
->sock
= sockV4
;
5697 sockV4
= kInvalidSocketRef
;
5698 dispatch_resume( context
->readSourceV4
);
5701 if( IsValidSocket( sockV6
) )
5703 SocketContext
* sockContext
;
5705 sockContext
= (SocketContext
*) calloc( 1, sizeof( *sockContext
) );
5706 require_action( sockContext
, exit
, err
= kNoMemoryErr
);
5708 err
= DispatchReadSourceCreate( sockV6
, MDNSQueryReadHandler
, SocketContextCancelHandler
, sockContext
,
5709 &context
->readSourceV6
);
5710 if( err
) ForgetMem( &sockContext
);
5711 require_noerr( err
, exit
);
5713 sockContext
->context
= context
;
5714 sockContext
->sock
= sockV6
;
5715 sockV6
= kInvalidSocketRef
;
5716 dispatch_resume( context
->readSourceV6
);
5719 if( context
->receiveSecs
> 0 )
5721 dispatch_after_f( dispatch_time_seconds( context
->receiveSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
5727 ForgetSocket( &sockV4
);
5728 ForgetSocket( &sockV6
);
5729 if( err
) exit( 1 );
5732 //===========================================================================================================================
5733 // MDNSQueryPrintPrologue
5734 //===========================================================================================================================
5736 static void MDNSQueryPrintPrologue( const MDNSQueryContext
*inContext
)
5738 const int receiveSecs
= inContext
->receiveSecs
;
5739 char time
[ kTimestampBufLen
];
5741 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, inContext
->ifName
);
5742 FPrintF( stdout
, "Name: %s\n", inContext
->qnameStr
);
5743 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->qtype
), inContext
->qtype
);
5744 FPrintF( stdout
, "Class: IN (%s)\n", inContext
->isQU
? "QU" : "QM" );
5745 FPrintF( stdout
, "Local port: %d\n", inContext
->localPort
);
5746 FPrintF( stdout
, "IP protocols: %?s%?s%?s\n",
5747 inContext
->useIPv4
, "IPv4", ( inContext
->useIPv4
&& inContext
->useIPv6
), ", ", inContext
->useIPv6
, "IPv6" );
5748 FPrintF( stdout
, "Receive duration: " );
5749 if( receiveSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", receiveSecs
, receiveSecs
!= 1, 's' );
5750 else FPrintF( stdout
, "∞\n" );
5751 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
5754 //===========================================================================================================================
5755 // MDNSQueryReadHandler
5756 //===========================================================================================================================
5758 static void MDNSQueryReadHandler( void *inContext
)
5761 SocketContext
* const sockContext
= (SocketContext
*) inContext
;
5762 MDNSQueryContext
* const context
= (MDNSQueryContext
*) sockContext
->context
;
5764 sockaddr_ip fromAddr
;
5765 char time
[ kTimestampBufLen
];
5766 Boolean foundAnswer
= false;
5768 GetTimestampStr( time
);
5770 err
= SocketRecvFrom( sockContext
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &msgLen
, &fromAddr
,
5771 sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
5772 require_noerr( err
, exit
);
5774 if( !context
->allResponses
&& ( msgLen
>= kDNSHeaderLength
) )
5776 const uint8_t * ptr
;
5777 const DNSHeader
* const hdr
= (DNSHeader
*) context
->msgBuf
;
5778 unsigned int rrCount
, i
;
5779 uint16_t type
, class;
5780 uint8_t name
[ kDomainNameLengthMax
];
5782 err
= DNSMessageGetAnswerSection( context
->msgBuf
, msgLen
, &ptr
);
5783 require_noerr( err
, exit
);
5785 if( context
->qname
[ 0 ] == 0 )
5787 err
= DomainNameAppendString( context
->qname
, context
->qnameStr
, NULL
);
5788 require_noerr( err
, exit
);
5791 rrCount
= DNSHeaderGetAnswerCount( hdr
) + DNSHeaderGetAuthorityCount( hdr
) + DNSHeaderGetAdditionalCount( hdr
);
5792 for( i
= 0; i
< rrCount
; ++i
)
5794 err
= DNSMessageExtractRecord( context
->msgBuf
, msgLen
, ptr
, name
, &type
, &class, NULL
, NULL
, NULL
, &ptr
);
5795 require_noerr( err
, exit
);
5797 if( ( ( context
->qtype
== kDNSServiceType_ANY
) || ( type
== context
->qtype
) ) &&
5798 DomainNameEqual( name
, context
->qname
) )
5805 if( context
->allResponses
|| foundAnswer
)
5807 FPrintF( stdout
, "---\n" );
5808 FPrintF( stdout
, "Receive time: %s\n", time
);
5809 FPrintF( stdout
, "Source: %##a\n", &fromAddr
);
5810 FPrintF( stdout
, "Message size: %zu\n\n", msgLen
);
5812 PrintMDNSMessage( context
->msgBuf
, msgLen
, context
->printRawRData
);
5816 if( err
) exit( 1 );
5819 //===========================================================================================================================
5821 //===========================================================================================================================
5823 static void PIDToUUIDCmd( void )
5827 struct proc_uniqidentifierinfo info
;
5829 n
= proc_pidinfo( gPIDToUUID_PID
, PROC_PIDUNIQIDENTIFIERINFO
, 1, &info
, sizeof( info
) );
5830 require_action_quiet( n
== (int) sizeof( info
), exit
, err
= kUnknownErr
);
5832 FPrintF( stdout
, "%#U\n", info
.p_uuid
);
5836 if( err
) exit( 1 );
5839 //===========================================================================================================================
5841 //===========================================================================================================================
5843 static void DaemonVersionCmd( void )
5846 uint32_t size
, version
;
5849 size
= (uint32_t) sizeof( version
);
5850 err
= DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion
, &version
, &size
);
5851 require_noerr( err
, exit
);
5853 FPrintF( stdout
, "Daemon version: %s\n", SourceVersionToCString( version
, strBuf
) );
5856 if( err
) exit( 1 );
5859 //===========================================================================================================================
5861 //===========================================================================================================================
5863 static void Exit( void *inContext
)
5865 const char * const reason
= (const char *) inContext
;
5866 char time
[ kTimestampBufLen
];
5868 FPrintF( stdout
, "---\n" );
5869 FPrintF( stdout
, "End time: %s\n", GetTimestampStr( time
) );
5870 if( reason
) FPrintF( stdout
, "End reason: %s\n", reason
);
5874 //===========================================================================================================================
5876 //===========================================================================================================================
5878 static char * GetTimestampStr( char inBuffer
[ kTimestampBufLen
] )
5884 gettimeofday( &now
, NULL
);
5885 tm
= localtime( &now
.tv_sec
);
5886 require_action( tm
, exit
, *inBuffer
= '\0' );
5888 len
= strftime( inBuffer
, kTimestampBufLen
, "%Y-%m-%d %H:%M:%S", tm
);
5889 SNPrintF( &inBuffer
[ len
], kTimestampBufLen
- len
, ".%06u", (unsigned int) now
.tv_usec
);
5895 //===========================================================================================================================
5896 // GetDNSSDFlagsFromOpts
5897 //===========================================================================================================================
5899 static DNSServiceFlags
GetDNSSDFlagsFromOpts( void )
5901 DNSServiceFlags flags
;
5903 flags
= (DNSServiceFlags
) gDNSSDFlags
;
5904 if( flags
& kDNSServiceFlagsShareConnection
)
5906 FPrintF( stderr
, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
5907 kDNSServiceFlagsShareConnection
);
5910 if( gDNSSDFlag_BrowseDomains
) flags
|= kDNSServiceFlagsBrowseDomains
;
5911 if( gDNSSDFlag_DenyCellular
) flags
|= kDNSServiceFlagsDenyCellular
;
5912 if( gDNSSDFlag_DenyExpensive
) flags
|= kDNSServiceFlagsDenyExpensive
;
5913 if( gDNSSDFlag_ForceMulticast
) flags
|= kDNSServiceFlagsForceMulticast
;
5914 if( gDNSSDFlag_IncludeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
5915 if( gDNSSDFlag_NoAutoRename
) flags
|= kDNSServiceFlagsNoAutoRename
;
5916 if( gDNSSDFlag_PathEvaluationDone
) flags
|= kDNSServiceFlagsPathEvaluationDone
;
5917 if( gDNSSDFlag_RegistrationDomains
) flags
|= kDNSServiceFlagsRegistrationDomains
;
5918 if( gDNSSDFlag_ReturnIntermediates
) flags
|= kDNSServiceFlagsReturnIntermediates
;
5919 if( gDNSSDFlag_Shared
) flags
|= kDNSServiceFlagsShared
;
5920 if( gDNSSDFlag_SuppressUnusable
) flags
|= kDNSServiceFlagsSuppressUnusable
;
5921 if( gDNSSDFlag_Timeout
) flags
|= kDNSServiceFlagsTimeout
;
5922 if( gDNSSDFlag_UnicastResponse
) flags
|= kDNSServiceFlagsUnicastResponse
;
5923 if( gDNSSDFlag_Unique
) flags
|= kDNSServiceFlagsUnique
;
5928 //===========================================================================================================================
5929 // CreateConnectionFromArgString
5930 //===========================================================================================================================
5933 CreateConnectionFromArgString(
5934 const char * inString
,
5935 dispatch_queue_t inQueue
,
5936 DNSServiceRef
* outSDRef
,
5937 ConnectionDesc
* outDesc
)
5940 DNSServiceRef sdRef
= NULL
;
5941 ConnectionType type
;
5942 int32_t pid
= -1; // Initializing because the analyzer claims pid may be used uninitialized.
5945 if( strcasecmp( inString
, kConnectionArg_Normal
) == 0 )
5947 err
= DNSServiceCreateConnection( &sdRef
);
5948 require_noerr( err
, exit
);
5949 type
= kConnectionType_Normal
;
5951 else if( stricmp_prefix( inString
, kConnectionArgPrefix_PID
) == 0 )
5953 const char * const pidStr
= inString
+ sizeof_string( kConnectionArgPrefix_PID
);
5955 err
= StringToInt32( pidStr
, &pid
);
5958 FPrintF( stderr
, "Invalid delegate connection PID value: %s\n", pidStr
);
5963 memset( uuid
, 0, sizeof( uuid
) );
5964 err
= DNSServiceCreateDelegateConnection( &sdRef
, pid
, uuid
);
5967 FPrintF( stderr
, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err
, pid
);
5970 type
= kConnectionType_DelegatePID
;
5972 else if( stricmp_prefix( inString
, kConnectionArgPrefix_UUID
) == 0 )
5974 const char * const uuidStr
= inString
+ sizeof_string( kConnectionArgPrefix_UUID
);
5976 check_compile_time_code( sizeof( uuid
) == sizeof( uuid_t
) );
5978 err
= StringToUUID( uuidStr
, kSizeCString
, false, uuid
);
5981 FPrintF( stderr
, "Invalid delegate connection UUID value: %s\n", uuidStr
);
5986 err
= DNSServiceCreateDelegateConnection( &sdRef
, 0, uuid
);
5989 FPrintF( stderr
, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err
, uuid
);
5992 type
= kConnectionType_DelegateUUID
;
5996 FPrintF( stderr
, "Unrecognized connection string \"%s\".\n", inString
);
6001 err
= DNSServiceSetDispatchQueue( sdRef
, inQueue
);
6002 require_noerr( err
, exit
);
6007 outDesc
->type
= type
;
6008 if( type
== kConnectionType_DelegatePID
) outDesc
->delegate
.pid
= pid
;
6009 else if( type
== kConnectionType_DelegateUUID
) memcpy( outDesc
->delegate
.uuid
, uuid
, 16 );
6014 if( sdRef
) DNSServiceRefDeallocate( sdRef
);
6018 //===========================================================================================================================
6019 // InterfaceIndexFromArgString
6020 //===========================================================================================================================
6022 static OSStatus
InterfaceIndexFromArgString( const char *inString
, uint32_t *outIndex
)
6029 ifIndex
= if_nametoindex( inString
);
6032 err
= StringToUInt32( inString
, &ifIndex
);
6035 FPrintF( stderr
, "Invalid interface value: %s\n", inString
);
6046 *outIndex
= ifIndex
;
6053 //===========================================================================================================================
6054 // RecordDataFromArgString
6055 //===========================================================================================================================
6057 #define kRDataMaxLen UINT16_C( 0xFFFF )
6059 static OSStatus
RecordDataFromArgString( const char *inString
, uint8_t **outDataPtr
, size_t *outDataLen
)
6062 uint8_t * dataPtr
= NULL
;
6066 DataBuffer_Init( &dataBuf
, NULL
, 0, kRDataMaxLen
);
6068 if( stricmp_prefix( inString
, kRDataArgPrefix_String
) == 0 )
6070 const char * const strPtr
= inString
+ sizeof_string( kRDataArgPrefix_String
);
6071 const size_t strLen
= strlen( strPtr
);
6077 require_action( strLen
<= kRDataMaxLen
, exit
, err
= kSizeErr
);
6078 dataPtr
= (uint8_t *) malloc( strLen
);
6079 require_action( dataPtr
, exit
, err
= kNoMemoryErr
);
6082 ParseQuotedEscapedString( strPtr
, strPtr
+ strLen
, "", (char *) dataPtr
, strLen
, &copiedLen
, &totalLen
, NULL
);
6083 check( copiedLen
== totalLen
);
6084 dataLen
= copiedLen
;
6092 else if( stricmp_prefix( inString
, kRDataArgPrefix_HexString
) == 0 )
6094 const char * const strPtr
= inString
+ sizeof_string( kRDataArgPrefix_HexString
);
6096 err
= HexToDataCopy( strPtr
, kSizeCString
, kHexToData_DefaultFlags
, &dataPtr
, &dataLen
, NULL
);
6097 require_noerr( err
, exit
);
6098 require_action( dataLen
<= kRDataMaxLen
, exit
, err
= kSizeErr
);
6100 else if( stricmp_prefix( inString
, kRDataArgPrefix_File
) == 0 )
6102 const char * const path
= inString
+ sizeof_string( kRDataArgPrefix_File
);
6104 err
= CopyFileDataByPath( path
, (char **) &dataPtr
, &dataLen
);
6105 require_noerr( err
, exit
);
6106 require_action( dataLen
<= kRDataMaxLen
, exit
, err
= kSizeErr
);
6108 else if( stricmp_prefix( inString
, kRDataArgPrefix_TXT
) == 0 )
6110 const char * strPtr
= inString
+ sizeof_string( kRDataArgPrefix_TXT
);
6111 const char * const strEnd
= strPtr
+ strlen( strPtr
);
6113 while( strPtr
< strEnd
)
6115 size_t copiedLen
, totalLen
;
6116 uint8_t kvBuf
[ 1 + 255 + 1 ]; // Length byte + max key-value length + 1 for NUL terminator.
6118 err
= ParseEscapedString( strPtr
, strEnd
, ',', (char *) &kvBuf
[ 1 ], sizeof( kvBuf
) - 1,
6119 &copiedLen
, &totalLen
, &strPtr
);
6120 require_noerr_quiet( err
, exit
);
6121 check( copiedLen
== totalLen
);
6122 if( totalLen
> 255 )
6124 FPrintF( stderr
, "TXT key-value pair length %zu is too long (> 255 bytes).\n", totalLen
);
6129 kvBuf
[ 0 ] = (uint8_t) copiedLen
;
6130 err
= DataBuffer_Append( &dataBuf
, kvBuf
, 1 + kvBuf
[ 0 ] );
6131 require_noerr( err
, exit
);
6134 err
= DataBuffer_Commit( &dataBuf
, NULL
, NULL
);
6135 require_noerr( err
, exit
);
6137 err
= DataBuffer_Detach( &dataBuf
, &dataPtr
, &dataLen
);
6138 require_noerr( err
, exit
);
6142 FPrintF( stderr
, "Unrecognized record data string \"%s\".\n", inString
);
6148 *outDataLen
= dataLen
;
6149 *outDataPtr
= dataPtr
;
6153 DataBuffer_Free( &dataBuf
);
6154 FreeNullSafe( dataPtr
);
6158 //===========================================================================================================================
6159 // RecordTypeFromArgString
6160 //===========================================================================================================================
6164 uint16_t value
; // Record type's numeric value.
6165 const char * name
; // Record type's name as a string (e.g., "A", "PTR", "SRV").
6169 static const RecordType kRecordTypes
[] =
6173 { kDNSServiceType_A
, "A" },
6174 { kDNSServiceType_AAAA
, "AAAA" },
6175 { kDNSServiceType_PTR
, "PTR" },
6176 { kDNSServiceType_SRV
, "SRV" },
6177 { kDNSServiceType_TXT
, "TXT" },
6178 { kDNSServiceType_CNAME
, "CNAME" },
6179 { kDNSServiceType_SOA
, "SOA" },
6180 { kDNSServiceType_NSEC
, "NSEC" },
6181 { kDNSServiceType_NS
, "NS" },
6182 { kDNSServiceType_MX
, "MX" },
6183 { kDNSServiceType_ANY
, "ANY" },
6184 { kDNSServiceType_OPT
, "OPT" },
6186 // Less common types.
6188 { kDNSServiceType_MD
, "MD" },
6189 { kDNSServiceType_NS
, "NS" },
6190 { kDNSServiceType_MD
, "MD" },
6191 { kDNSServiceType_MF
, "MF" },
6192 { kDNSServiceType_MB
, "MB" },
6193 { kDNSServiceType_MG
, "MG" },
6194 { kDNSServiceType_MR
, "MR" },
6195 { kDNSServiceType_NULL
, "NULL" },
6196 { kDNSServiceType_WKS
, "WKS" },
6197 { kDNSServiceType_HINFO
, "HINFO" },
6198 { kDNSServiceType_MINFO
, "MINFO" },
6199 { kDNSServiceType_RP
, "RP" },
6200 { kDNSServiceType_AFSDB
, "AFSDB" },
6201 { kDNSServiceType_X25
, "X25" },
6202 { kDNSServiceType_ISDN
, "ISDN" },
6203 { kDNSServiceType_RT
, "RT" },
6204 { kDNSServiceType_NSAP
, "NSAP" },
6205 { kDNSServiceType_NSAP_PTR
, "NSAP_PTR" },
6206 { kDNSServiceType_SIG
, "SIG" },
6207 { kDNSServiceType_KEY
, "KEY" },
6208 { kDNSServiceType_PX
, "PX" },
6209 { kDNSServiceType_GPOS
, "GPOS" },
6210 { kDNSServiceType_LOC
, "LOC" },
6211 { kDNSServiceType_NXT
, "NXT" },
6212 { kDNSServiceType_EID
, "EID" },
6213 { kDNSServiceType_NIMLOC
, "NIMLOC" },
6214 { kDNSServiceType_ATMA
, "ATMA" },
6215 { kDNSServiceType_NAPTR
, "NAPTR" },
6216 { kDNSServiceType_KX
, "KX" },
6217 { kDNSServiceType_CERT
, "CERT" },
6218 { kDNSServiceType_A6
, "A6" },
6219 { kDNSServiceType_DNAME
, "DNAME" },
6220 { kDNSServiceType_SINK
, "SINK" },
6221 { kDNSServiceType_APL
, "APL" },
6222 { kDNSServiceType_DS
, "DS" },
6223 { kDNSServiceType_SSHFP
, "SSHFP" },
6224 { kDNSServiceType_IPSECKEY
, "IPSECKEY" },
6225 { kDNSServiceType_RRSIG
, "RRSIG" },
6226 { kDNSServiceType_DNSKEY
, "DNSKEY" },
6227 { kDNSServiceType_DHCID
, "DHCID" },
6228 { kDNSServiceType_NSEC3
, "NSEC3" },
6229 { kDNSServiceType_NSEC3PARAM
, "NSEC3PARAM" },
6230 { kDNSServiceType_HIP
, "HIP" },
6231 { kDNSServiceType_SPF
, "SPF" },
6232 { kDNSServiceType_UINFO
, "UINFO" },
6233 { kDNSServiceType_UID
, "UID" },
6234 { kDNSServiceType_GID
, "GID" },
6235 { kDNSServiceType_UNSPEC
, "UNSPEC" },
6236 { kDNSServiceType_TKEY
, "TKEY" },
6237 { kDNSServiceType_TSIG
, "TSIG" },
6238 { kDNSServiceType_IXFR
, "IXFR" },
6239 { kDNSServiceType_AXFR
, "AXFR" },
6240 { kDNSServiceType_MAILB
, "MAILB" },
6241 { kDNSServiceType_MAILA
, "MAILA" }
6244 static OSStatus
RecordTypeFromArgString( const char *inString
, uint16_t *outValue
)
6248 const RecordType
* type
;
6249 const RecordType
* const end
= kRecordTypes
+ countof( kRecordTypes
);
6251 for( type
= kRecordTypes
; type
< end
; ++type
)
6253 if( strcasecmp( type
->name
, inString
) == 0 )
6255 *outValue
= type
->value
;
6260 err
= StringToInt32( inString
, &i32
);
6261 require_noerr_quiet( err
, exit
);
6262 require_action_quiet( ( i32
>= 0 ) && ( i32
<= UINT16_MAX
), exit
, err
= kParamErr
);
6264 *outValue
= (uint16_t) i32
;
6270 //===========================================================================================================================
6271 // RecordClassFromArgString
6272 //===========================================================================================================================
6274 static OSStatus
RecordClassFromArgString( const char *inString
, uint16_t *outValue
)
6279 if( strcasecmp( inString
, "IN" ) == 0 )
6281 *outValue
= kDNSServiceClass_IN
;
6286 err
= StringToInt32( inString
, &i32
);
6287 require_noerr_quiet( err
, exit
);
6288 require_action_quiet( ( i32
>= 0 ) && ( i32
<= UINT16_MAX
), exit
, err
= kParamErr
);
6290 *outValue
= (uint16_t) i32
;
6296 //===========================================================================================================================
6297 // InterfaceIndexToName
6298 //===========================================================================================================================
6300 static char * InterfaceIndexToName( uint32_t inIfIndex
, char inNameBuf
[ kInterfaceNameBufLen
] )
6304 case kDNSServiceInterfaceIndexAny
:
6305 strlcpy( inNameBuf
, "Any", kInterfaceNameBufLen
);
6308 case kDNSServiceInterfaceIndexLocalOnly
:
6309 strlcpy( inNameBuf
, "LocalOnly", kInterfaceNameBufLen
);
6312 case kDNSServiceInterfaceIndexUnicast
:
6313 strlcpy( inNameBuf
, "Unicast", kInterfaceNameBufLen
);
6316 case kDNSServiceInterfaceIndexP2P
:
6317 strlcpy( inNameBuf
, "P2P", kInterfaceNameBufLen
);
6320 #if( defined( kDNSServiceInterfaceIndexBLE ) )
6321 case kDNSServiceInterfaceIndexBLE
:
6322 strlcpy( inNameBuf
, "BLE", kInterfaceNameBufLen
);
6330 name
= if_indextoname( inIfIndex
, inNameBuf
);
6331 if( !name
) strlcpy( inNameBuf
, "NO NAME", kInterfaceNameBufLen
);
6336 return( inNameBuf
);
6339 //===========================================================================================================================
6340 // RecordTypeToString
6341 //===========================================================================================================================
6343 static const char * RecordTypeToString( unsigned int inValue
)
6345 const RecordType
* type
;
6346 const RecordType
* const end
= kRecordTypes
+ countof( kRecordTypes
);
6348 for( type
= kRecordTypes
; type
< end
; ++type
)
6350 if( type
->value
== inValue
) return( type
->name
);
6355 //===========================================================================================================================
6356 // DNSMessageExtractDomainName
6357 //===========================================================================================================================
6359 #define IsCompressionByte( X ) ( ( ( X ) & 0xC0 ) == 0xC0 )
6362 DNSMessageExtractDomainName(
6363 const uint8_t * inMsgPtr
,
6365 const uint8_t * inNamePtr
,
6366 uint8_t inBuf
[ kDomainNameLengthMax
],
6367 const uint8_t ** outNextPtr
)
6370 const uint8_t * label
;
6372 const uint8_t * nextLabel
;
6373 const uint8_t * const msgEnd
= inMsgPtr
+ inMsgLen
;
6374 uint8_t * dst
= inBuf
;
6375 const uint8_t * const dstLim
= inBuf
? ( inBuf
+ kDomainNameLengthMax
) : NULL
;
6376 const uint8_t * nameEnd
= NULL
;
6378 require_action( ( inNamePtr
>= inMsgPtr
) && ( inNamePtr
< msgEnd
), exit
, err
= kRangeErr
);
6380 for( label
= inNamePtr
; ( labelLen
= label
[ 0 ] ) != 0; label
= nextLabel
)
6382 if( labelLen
<= kDomainLabelLengthMax
)
6384 nextLabel
= label
+ 1 + labelLen
;
6385 require_action( nextLabel
< msgEnd
, exit
, err
= kUnderrunErr
);
6388 require_action( ( dstLim
- dst
) > ( 1 + labelLen
), exit
, err
= kOverrunErr
);
6389 memcpy( dst
, label
, 1 + labelLen
);
6390 dst
+= ( 1 + labelLen
);
6393 else if( IsCompressionByte( labelLen
) )
6397 require_action( ( msgEnd
- label
) >= 2, exit
, err
= kUnderrunErr
);
6400 nameEnd
= label
+ 2;
6403 offset
= (uint16_t)( ( ( label
[ 0 ] & 0x3F ) << 8 ) | label
[ 1 ] );
6404 nextLabel
= inMsgPtr
+ offset
;
6405 require_action( nextLabel
< msgEnd
, exit
, err
= kUnderrunErr
);
6406 require_action( !IsCompressionByte( nextLabel
[ 0 ] ), exit
, err
= kMalformedErr
);
6410 dlogassert( "Unhandled label length 0x%02X\n", labelLen
);
6411 err
= kMalformedErr
;
6417 if( !nameEnd
) nameEnd
= label
+ 1;
6419 if( outNextPtr
) *outNextPtr
= nameEnd
;
6426 //===========================================================================================================================
6427 // DNSMessageExtractDomainNameString
6428 //===========================================================================================================================
6431 DNSMessageExtractDomainNameString(
6432 const void * inMsgPtr
,
6434 const void * inNamePtr
,
6435 char inBuf
[ kDNSServiceMaxDomainName
],
6436 const uint8_t ** outNextPtr
)
6439 const uint8_t * nextPtr
;
6440 uint8_t domainName
[ kDomainNameLengthMax
];
6442 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, inNamePtr
, domainName
, &nextPtr
);
6443 require_noerr( err
, exit
);
6445 err
= DomainNameToString( domainName
, NULL
, inBuf
, NULL
);
6446 require_noerr( err
, exit
);
6448 if( outNextPtr
) *outNextPtr
= nextPtr
;
6454 //===========================================================================================================================
6455 // DNSMessageExtractRecord
6456 //===========================================================================================================================
6463 uint8_t rdLength
[ 2 ];
6468 check_compile_time( offsetof( DNSRecordFields
, rdata
) == 10 );
6471 DNSMessageExtractRecord(
6472 const uint8_t * inMsgPtr
,
6474 const uint8_t * inPtr
,
6475 uint8_t inNameBuf
[ kDomainNameLengthMax
],
6477 uint16_t * outClass
,
6479 const uint8_t ** outRDataPtr
,
6480 size_t * outRDataLen
,
6481 const uint8_t ** outPtr
)
6484 const uint8_t * const msgEnd
= inMsgPtr
+ inMsgLen
;
6485 const uint8_t * ptr
;
6486 const DNSRecordFields
* record
;
6489 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, inPtr
, inNameBuf
, &ptr
);
6490 require_noerr_quiet( err
, exit
);
6491 require_action_quiet( (size_t)( msgEnd
- ptr
) >= offsetof( DNSRecordFields
, rdata
), exit
, err
= kUnderrunErr
);
6493 record
= (DNSRecordFields
*) ptr
;
6494 rdLength
= ReadBig16( record
->rdLength
);
6495 require_action_quiet( (size_t)( msgEnd
- record
->rdata
) >= rdLength
, exit
, err
= kUnderrunErr
);
6497 if( outType
) *outType
= ReadBig16( record
->type
);
6498 if( outClass
) *outClass
= ReadBig16( record
->class );
6499 if( outTTL
) *outTTL
= ReadBig32( record
->ttl
);
6500 if( outRDataPtr
) *outRDataPtr
= record
->rdata
;
6501 if( outRDataLen
) *outRDataLen
= rdLength
;
6502 if( outPtr
) *outPtr
= record
->rdata
+ rdLength
;
6508 //===========================================================================================================================
6509 // DNSMessageGetAnswerSection
6510 //===========================================================================================================================
6512 static OSStatus
DNSMessageGetAnswerSection( const uint8_t *inMsgPtr
, size_t inMsgLen
, const uint8_t **outPtr
)
6515 const uint8_t * const msgEnd
= inMsgPtr
+ inMsgLen
;
6516 unsigned int questionCount
, i
;
6517 const DNSHeader
* hdr
;
6518 const uint8_t * ptr
;
6520 require_action_quiet( inMsgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
6522 hdr
= (DNSHeader
*) inMsgPtr
;
6523 questionCount
= DNSHeaderGetQuestionCount( hdr
);
6525 ptr
= (uint8_t *)( hdr
+ 1 );
6526 for( i
= 0; i
< questionCount
; ++i
)
6528 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, ptr
, NULL
, &ptr
);
6529 require_noerr( err
, exit
);
6530 require_action_quiet( ( msgEnd
- ptr
) >= 4, exit
, err
= kUnderrunErr
);
6534 if( outPtr
) *outPtr
= ptr
;
6541 //===========================================================================================================================
6542 // DNSRecordDataToString
6543 //===========================================================================================================================
6546 DNSRecordDataToString(
6547 const void * inRDataPtr
,
6549 unsigned int inRDataType
,
6550 const void * inMsgPtr
,
6555 const uint8_t * const rdataPtr
= (uint8_t *) inRDataPtr
;
6556 const uint8_t * const rdataEnd
= rdataPtr
+ inRDataLen
;
6558 const uint8_t * ptr
;
6560 char domainNameStr
[ kDNSServiceMaxDomainName
];
6563 if( inRDataType
== kDNSServiceType_A
)
6565 require_action_quiet( inRDataLen
== 4, exit
, err
= kMalformedErr
);
6567 ASPrintF( &rdataStr
, "%.4a", rdataPtr
);
6568 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
6570 else if( inRDataType
== kDNSServiceType_AAAA
)
6572 require_action_quiet( inRDataLen
== 16, exit
, err
= kMalformedErr
);
6574 ASPrintF( &rdataStr
, "%.16a", rdataPtr
);
6575 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
6577 else if( ( inRDataType
== kDNSServiceType_PTR
) || ( inRDataType
== kDNSServiceType_CNAME
) ||
6578 ( inRDataType
== kDNSServiceType_NS
) )
6582 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, NULL
);
6583 require_noerr( err
, exit
);
6587 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, NULL
);
6588 require_noerr( err
, exit
);
6591 rdataStr
= strdup( domainNameStr
);
6592 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
6594 else if( inRDataType
== kDNSServiceType_SRV
)
6596 uint16_t priority
, weight
, port
;
6597 const uint8_t * target
;
6599 require_action_quiet( ( rdataPtr
+ 6 ) < rdataEnd
, exit
, err
= kMalformedErr
);
6601 priority
= ReadBig16( rdataPtr
);
6602 weight
= ReadBig16( rdataPtr
+ 2 );
6603 port
= ReadBig16( rdataPtr
+ 4 );
6604 target
= rdataPtr
+ 6;
6608 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, target
, domainNameStr
, NULL
);
6609 require_noerr( err
, exit
);
6613 err
= DomainNameToString( target
, rdataEnd
, domainNameStr
, NULL
);
6614 require_noerr( err
, exit
);
6617 ASPrintF( &rdataStr
, "%u %u %u %s", priority
, weight
, port
, domainNameStr
);
6618 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
6620 else if( inRDataType
== kDNSServiceType_TXT
)
6622 require_action_quiet( inRDataLen
> 0, exit
, err
= kMalformedErr
);
6624 if( inRDataLen
== 1 )
6626 ASPrintF( &rdataStr
, "%#H", rdataPtr
, (int) inRDataLen
, INT_MAX
);
6627 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
6631 ASPrintF( &rdataStr
, "%#{txt}", rdataPtr
, inRDataLen
);
6632 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
6635 else if( inRDataType
== kDNSServiceType_SOA
)
6637 uint32_t serial
, refresh
, retry
, expire
, minimum
;
6641 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
6642 require_noerr( err
, exit
);
6644 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
6646 rdataStr
= strdup( domainNameStr
);
6647 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
6649 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, ptr
, domainNameStr
, &ptr
);
6650 require_noerr( err
, exit
);
6654 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
6655 require_noerr( err
, exit
);
6657 rdataStr
= strdup( domainNameStr
);
6658 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
6660 err
= DomainNameToString( ptr
, rdataEnd
, domainNameStr
, &ptr
);
6661 require_noerr( err
, exit
);
6664 require_action_quiet( ( ptr
+ 20 ) == rdataEnd
, exit
, err
= kMalformedErr
);
6666 serial
= ReadBig32( ptr
);
6667 refresh
= ReadBig32( ptr
+ 4 );
6668 retry
= ReadBig32( ptr
+ 8 );
6669 expire
= ReadBig32( ptr
+ 12 );
6670 minimum
= ReadBig32( ptr
+ 16 );
6672 n
= AppendPrintF( &rdataStr
, " %s %u %u %u %u %u\n", domainNameStr
, serial
, refresh
, retry
, expire
, minimum
);
6673 require_action( n
> 0, exit
, err
= kUnknownErr
);
6675 else if( inRDataType
== kDNSServiceType_NSEC
)
6677 unsigned int windowBlock
, bitmapLen
, i
, recordType
;
6678 const uint8_t * bitmapPtr
;
6682 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
6683 require_noerr( err
, exit
);
6687 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
6688 require_noerr( err
, exit
);
6691 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
6693 rdataStr
= strdup( domainNameStr
);
6694 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
6696 for( ; ptr
< rdataEnd
; ptr
+= ( 2 + bitmapLen
) )
6698 require_action_quiet( ( ptr
+ 2 ) < rdataEnd
, exit
, err
= kMalformedErr
);
6700 windowBlock
= ptr
[ 0 ];
6701 bitmapLen
= ptr
[ 1 ];
6702 bitmapPtr
= &ptr
[ 2 ];
6704 require_action_quiet( ( bitmapLen
>= 1 ) && ( bitmapLen
<= 32 ) , exit
, err
= kMalformedErr
);
6705 require_action_quiet( ( bitmapPtr
+ bitmapLen
) <= rdataEnd
, exit
, err
= kMalformedErr
);
6707 for( i
= 0; i
< BitArray_MaxBits( bitmapLen
); ++i
)
6709 if( BitArray_GetBit( bitmapPtr
, bitmapLen
, i
) )
6711 recordType
= ( windowBlock
* 256 ) + i
;
6712 n
= AppendPrintF( &rdataStr
, " %s", RecordTypeToString( recordType
) );
6713 require_action( n
> 0, exit
, err
= kUnknownErr
);
6718 else if( inRDataType
== kDNSServiceType_MX
)
6720 uint16_t preference
;
6721 const uint8_t * exchange
;
6723 require_action_quiet( ( rdataPtr
+ 2 ) < rdataEnd
, exit
, err
= kMalformedErr
);
6725 preference
= ReadBig16( rdataPtr
);
6726 exchange
= &rdataPtr
[ 2 ];
6730 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, exchange
, domainNameStr
, NULL
);
6731 require_noerr( err
, exit
);
6735 err
= DomainNameToString( exchange
, rdataEnd
, domainNameStr
, NULL
);
6736 require_noerr( err
, exit
);
6739 n
= ASPrintF( &rdataStr
, "%u %s", preference
, domainNameStr
);
6740 require_action( n
> 0, exit
, err
= kUnknownErr
);
6744 err
= kNotHandledErr
;
6749 *outString
= rdataStr
;
6754 FreeNullSafe( rdataStr
);
6758 //===========================================================================================================================
6759 // DomainNameAppendString
6760 //===========================================================================================================================
6763 DomainNameAppendString(
6764 uint8_t inDomainName
[ kDomainNameLengthMax
],
6765 const char * inString
,
6766 uint8_t ** outEndPtr
)
6771 const uint8_t * const nameLimit
= inDomainName
+ kDomainNameLengthMax
;
6773 // Find the root label.
6775 for( dst
= inDomainName
; ( dst
< nameLimit
) && *dst
; dst
+= ( 1 + *dst
) ) {}
6776 require_action_quiet( dst
< nameLimit
, exit
, err
= kMalformedErr
);
6778 // Append the string's labels one label at a time.
6783 uint8_t * const label
= dst
++;
6784 const uint8_t * const labelLimit
= Min( dst
+ kDomainLabelLengthMax
, nameLimit
- 1 );
6786 // If the first character is a label separator, then the label is empty. Empty non-root labels are not allowed.
6788 require_action_quiet( *src
!= '.', exit
, err
= kMalformedErr
);
6790 // Write the label characters until the end of the label, a separator or NUL character, is encountered, or until no
6791 // more space is available.
6793 while( ( *src
!= '.' ) && ( *src
!= '\0' ) && ( dst
< labelLimit
) )
6797 value
= (uint8_t) *src
++;
6800 if( *src
== '\0' ) break;
6801 value
= (uint8_t) *src
++;
6802 if( isdigit_safe( value
) && isdigit_safe( src
[ 0 ] ) && isdigit_safe( src
[ 1 ] ) )
6806 decimalValue
= ( ( value
- '0' ) * 100 ) + ( ( src
[ 0 ] - '0' ) * 10 ) + ( src
[ 1 ] - '0' );
6807 if( decimalValue
<= 255 )
6809 value
= (uint8_t) decimalValue
;
6816 if( ( *src
== '.' ) || ( *src
== '\0' ) )
6818 label
[ 0 ] = (uint8_t)( dst
- &label
[ 1 ] ); // Write the label length.
6819 if( *src
== '.' ) ++src
; // Advance the pointer past the label separator.
6829 *dst
++ = 0; // Write the empty root label.
6830 if( outEndPtr
) *outEndPtr
= dst
;
6837 //===========================================================================================================================
6839 //===========================================================================================================================
6841 static Boolean
DomainNameEqual( const uint8_t *inName1
, const uint8_t *inName2
)
6843 const uint8_t * p1
= inName1
;
6844 const uint8_t * p2
= inName2
;
6849 if( ( len
= *p1
++ ) != *p2
++ ) return( false );
6850 if( len
== 0 ) break;
6851 for( ; len
> 0; ++p1
, ++p2
, --len
)
6853 if( tolower_safe( *p1
) != tolower_safe( *p2
) ) return( false );
6859 //===========================================================================================================================
6860 // DomainNameToString
6861 //===========================================================================================================================
6865 const uint8_t * inDomainName
,
6866 const uint8_t * inEnd
,
6867 char inBuf
[ kDNSServiceMaxDomainName
],
6868 const uint8_t ** outNextPtr
)
6871 const uint8_t * label
;
6873 const uint8_t * nextLabel
;
6875 const uint8_t * src
;
6877 require_action( !inEnd
|| ( inDomainName
< inEnd
), exit
, err
= kUnderrunErr
);
6879 // Convert each label up until the root label, i.e., the zero-length label.
6882 for( label
= inDomainName
; ( labelLen
= label
[ 0 ] ) != 0; label
= nextLabel
)
6884 require_action( labelLen
<= kDomainLabelLengthMax
, exit
, err
= kMalformedErr
);
6886 nextLabel
= &label
[ 1 ] + labelLen
;
6887 require_action( ( nextLabel
- inDomainName
) < kDomainNameLengthMax
, exit
, err
= kMalformedErr
);
6888 require_action( !inEnd
|| ( nextLabel
< inEnd
), exit
, err
= kUnderrunErr
);
6890 for( src
= &label
[ 1 ]; src
< nextLabel
; ++src
)
6892 if( isprint_safe( *src
) )
6894 if( ( *src
== '.' ) || ( *src
== '\\' ) || ( *src
== ' ' ) ) *dst
++ = '\\';
6895 *dst
++ = (char) *src
;
6900 *dst
++ = '0' + ( *src
/ 100 );
6901 *dst
++ = '0' + ( ( *src
/ 10 ) % 10 );
6902 *dst
++ = '0' + ( *src
% 10 );
6908 // At this point, label points to the root label.
6909 // If the root label was the only label, then write a dot for it.
6911 if( label
== inDomainName
) *dst
++ = '.';
6913 if( outNextPtr
) *outNextPtr
= label
+ 1;
6920 //===========================================================================================================================
6922 //===========================================================================================================================
6924 #define DNSFlagsOpCodeToString( X ) ( \
6925 ( (X) == kDNSOpCode_Query ) ? "Query" : \
6926 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \
6927 ( (X) == kDNSOpCode_Status ) ? "Status" : \
6928 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \
6929 ( (X) == kDNSOpCode_Update ) ? "Update" : \
6932 #define DNSFlagsRCodeToString( X ) ( \
6933 ( (X) == kDNSRCode_NoError ) ? "NoError" : \
6934 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \
6935 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \
6936 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \
6937 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \
6938 ( (X) == kDNSRCode_Refused ) ? "Refused" : \
6941 #define DNSFlagsGetOpCode( X ) ( ( (X) >> 11 ) & 0x0F )
6942 #define DNSFlagsGetRCode( X ) ( (X) & 0x0F )
6944 static OSStatus
PrintDNSMessage( const uint8_t *inMsgPtr
, size_t inMsgLen
, const Boolean inIsMDNS
, const Boolean inPrintRaw
)
6947 const DNSHeader
* hdr
;
6948 const uint8_t * const msgEnd
= inMsgPtr
+ inMsgLen
;
6949 const uint8_t * ptr
;
6950 unsigned int id
, flags
, opcode
, rcode
;
6951 unsigned int questionCount
, answerCount
, authorityCount
, additionalCount
, i
, totalRRCount
;
6952 char nameStr
[ kDNSServiceMaxDomainName
];
6954 require_action_quiet( inMsgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
6956 hdr
= (DNSHeader
*) inMsgPtr
;
6957 id
= DNSHeaderGetID( hdr
);
6958 flags
= DNSHeaderGetFlags( hdr
);
6959 questionCount
= DNSHeaderGetQuestionCount( hdr
);
6960 answerCount
= DNSHeaderGetAnswerCount( hdr
);
6961 authorityCount
= DNSHeaderGetAuthorityCount( hdr
);
6962 additionalCount
= DNSHeaderGetAdditionalCount( hdr
);
6963 opcode
= DNSFlagsGetOpCode( flags
);
6964 rcode
= DNSFlagsGetRCode( flags
);
6966 FPrintF( stdout
, "ID: 0x%04X (%u)\n", id
, id
);
6967 FPrintF( stdout
, "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA %s\n",
6969 ( flags
& kDNSHeaderFlag_Response
) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode
),
6970 ( flags
& kDNSHeaderFlag_AuthAnswer
) ? ' ' : '!',
6971 ( flags
& kDNSHeaderFlag_Truncation
) ? ' ' : '!',
6972 ( flags
& kDNSHeaderFlag_RecursionDesired
) ? ' ' : '!',
6973 ( flags
& kDNSHeaderFlag_RecursionAvailable
) ? ' ' : '!',
6974 DNSFlagsRCodeToString( rcode
) );
6975 FPrintF( stdout
, "Question count: %u\n", questionCount
);
6976 FPrintF( stdout
, "Answer count: %u\n", answerCount
);
6977 FPrintF( stdout
, "Authority count: %u\n", authorityCount
);
6978 FPrintF( stdout
, "Additional count: %u\n", additionalCount
);
6980 ptr
= (uint8_t *)( hdr
+ 1 );
6981 for( i
= 0; i
< questionCount
; ++i
)
6983 unsigned int qType
, qClass
;
6986 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, ptr
, nameStr
, &ptr
);
6987 require_noerr( err
, exit
);
6989 if( ( msgEnd
- ptr
) < 4 )
6995 qType
= ReadBig16( ptr
);
6997 qClass
= ReadBig16( ptr
);
7000 isQU
= ( inIsMDNS
&& ( qClass
& kQClassUnicastResponseBit
) ) ? true : false;
7001 if( inIsMDNS
) qClass
&= ~kQClassUnicastResponseBit
;
7003 if( i
== 0 ) FPrintF( stdout
, "\nQUESTION SECTION\n" );
7005 FPrintF( stdout
, "%s %2s %?2s%?2u %-5s\n",
7006 nameStr
, inIsMDNS
? ( isQU
? "QU" : "QM" ) : "",
7007 ( qClass
== kDNSServiceClass_IN
), "IN", ( qClass
!= kDNSServiceClass_IN
), qClass
,
7008 RecordTypeToString( qType
) );
7011 totalRRCount
= answerCount
+ authorityCount
+ additionalCount
;
7012 for( i
= 0; i
< totalRRCount
; ++i
)
7017 const uint8_t * rdataPtr
;
7021 uint8_t name
[ kDomainNameLengthMax
];
7023 err
= DNSMessageExtractRecord( inMsgPtr
, inMsgLen
, ptr
, name
, &type
, &class, &ttl
, &rdataPtr
, &rdataLen
, &ptr
);
7024 require_noerr( err
, exit
);
7026 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
7027 require_noerr( err
, exit
);
7029 cacheFlush
= ( inIsMDNS
&& ( class & kRRClassCacheFlushBit
) ) ? true : false;
7030 if( inIsMDNS
) class &= ~kRRClassCacheFlushBit
;
7033 if( !inPrintRaw
) DNSRecordDataToString( rdataPtr
, rdataLen
, type
, inMsgPtr
, inMsgLen
, &rdataStr
);
7036 ASPrintF( &rdataStr
, "%#H", rdataPtr
, (int) rdataLen
, INT_MAX
);
7037 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
7040 if( answerCount
&& ( i
== 0 ) ) FPrintF( stdout
, "\nANSWER SECTION\n" );
7041 else if( authorityCount
&& ( i
== answerCount
) ) FPrintF( stdout
, "\nAUTHORITY SECTION\n" );
7042 else if( additionalCount
&& ( i
== ( answerCount
+ authorityCount
) ) ) FPrintF( stdout
, "\nADDITIONAL SECTION\n" );
7044 FPrintF( stdout
, "%-42s %6u %2s %?2s%?2u %-5s %s\n",
7045 nameStr
, ttl
, cacheFlush
? "CF" : "",
7046 ( class == kDNSServiceClass_IN
), "IN", ( class != kDNSServiceClass_IN
), class,
7047 RecordTypeToString( type
), rdataStr
);
7050 FPrintF( stdout
, "\n" );
7057 //===========================================================================================================================
7058 // WriteDNSQueryMessage
7059 //===========================================================================================================================
7062 WriteDNSQueryMessage(
7063 uint8_t inMsg
[ kDNSQueryMessageMaxLen
],
7066 const char * inQName
,
7069 size_t * outMsgLen
)
7072 DNSHeader
* const hdr
= (DNSHeader
*) inMsg
;
7076 WriteBig16( hdr
->id
, inMsgID
);
7077 WriteBig16( hdr
->flags
, inFlags
);
7078 WriteBig16( hdr
->questionCount
, 1 );
7079 WriteBig16( hdr
->answerCount
, 0 );
7080 WriteBig16( hdr
->authorityCount
, 0 );
7081 WriteBig16( hdr
->additionalCount
, 0 );
7083 ptr
= (uint8_t *)( hdr
+ 1 );
7085 err
= DomainNameAppendString( ptr
, inQName
, &ptr
);
7086 require_noerr_quiet( err
, exit
);
7088 WriteBig16( ptr
, inQType
);
7090 WriteBig16( ptr
, inQClass
);
7092 msgLen
= (size_t)( ptr
- inMsg
);
7093 check( msgLen
<= kDNSQueryMessageMaxLen
);
7095 if( outMsgLen
) *outMsgLen
= msgLen
;
7101 //===========================================================================================================================
7102 // DispatchSignalSourceCreate
7103 //===========================================================================================================================
7106 DispatchSignalSourceCreate(
7108 DispatchHandler inEventHandler
,
7110 dispatch_source_t
* outSource
)
7113 dispatch_source_t source
;
7115 source
= dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL
, (uintptr_t) inSignal
, 0, dispatch_get_main_queue() );
7116 require_action( source
, exit
, err
= kUnknownErr
);
7118 dispatch_set_context( source
, inContext
);
7119 dispatch_source_set_event_handler_f( source
, inEventHandler
);
7121 *outSource
= source
;
7128 //===========================================================================================================================
7129 // DispatchReadSourceCreate
7130 //===========================================================================================================================
7133 DispatchReadSourceCreate(
7135 DispatchHandler inEventHandler
,
7136 DispatchHandler inCancelHandler
,
7138 dispatch_source_t
* outSource
)
7141 dispatch_source_t source
;
7143 source
= dispatch_source_create( DISPATCH_SOURCE_TYPE_READ
, (uintptr_t) inSock
, 0, dispatch_get_main_queue() );
7144 require_action( source
, exit
, err
= kUnknownErr
);
7146 dispatch_set_context( source
, inContext
);
7147 dispatch_source_set_event_handler_f( source
, inEventHandler
);
7148 dispatch_source_set_cancel_handler_f( source
, inCancelHandler
);
7150 *outSource
= source
;
7157 //===========================================================================================================================
7158 // DispatchTimerCreate
7159 //===========================================================================================================================
7162 DispatchTimerCreate(
7163 dispatch_time_t inStart
,
7164 uint64_t inIntervalNs
,
7165 uint64_t inLeewayNs
,
7166 DispatchHandler inEventHandler
,
7167 DispatchHandler inCancelHandler
,
7169 dispatch_source_t
* outTimer
)
7172 dispatch_source_t timer
;
7174 timer
= dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, dispatch_get_main_queue() );
7175 require_action( timer
, exit
, err
= kUnknownErr
);
7177 dispatch_source_set_timer( timer
, inStart
, inIntervalNs
, inLeewayNs
);
7178 dispatch_set_context( timer
, inContext
);
7179 dispatch_source_set_event_handler_f( timer
, inEventHandler
);
7180 dispatch_source_set_cancel_handler_f( timer
, inCancelHandler
);
7189 //===========================================================================================================================
7190 // ServiceTypeDescription
7191 //===========================================================================================================================
7195 const char * name
; // Name of the service type in two-label "_service._proto" format.
7196 const char * description
; // Description of the service type.
7200 // A Non-comprehensive table of DNS-SD service types
7202 static const ServiceType kServiceTypes
[] =
7204 { "_acp-sync._tcp", "AirPort Base Station Sync" },
7205 { "_adisk._tcp", "Automatic Disk Discovery" },
7206 { "_afpovertcp._tcp", "Apple File Sharing" },
7207 { "_airdrop._tcp", "AirDrop" },
7208 { "_airplay._tcp", "AirPlay" },
7209 { "_airport._tcp", "AirPort Base Station" },
7210 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
7211 { "_eppc._tcp", "Remote AppleEvents" },
7212 { "_ftp._tcp", "File Transfer Protocol" },
7213 { "_home-sharing._tcp", "Home Sharing" },
7214 { "_homekit._tcp", "HomeKit" },
7215 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
7216 { "_https._tcp", "HTTP over SSL/TLS" },
7217 { "_ipp._tcp", "Internet Printing Protocol" },
7218 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
7219 { "_mediaremotetv._tcp", "Media Remote" },
7220 { "_net-assistant._tcp", "Apple Remote Desktop" },
7221 { "_od-master._tcp", "OpenDirectory Master" },
7222 { "_nfs._tcp", "Network File System" },
7223 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
7224 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
7225 { "_raop._tcp", "Remote Audio Output Protocol" },
7226 { "_rfb._tcp", "Remote Frame Buffer" },
7227 { "_scanner._tcp", "Bonjour Scanning" },
7228 { "_smb._tcp", "Server Message Block over TCP/IP" },
7229 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
7230 { "_sleep-proxy._udp", "Sleep Proxy Server" },
7231 { "_ssh._tcp", "SSH Remote Login Protocol" },
7232 { "_teleport._tcp", "teleport" },
7233 { "_tftp._tcp", "Trivial File Transfer Protocol" },
7234 { "_workstation._tcp", "Workgroup Manager" },
7235 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
7236 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
7239 static const char * ServiceTypeDescription( const char *inName
)
7241 const ServiceType
* serviceType
;
7242 const ServiceType
* const end
= kServiceTypes
+ countof( kServiceTypes
);
7244 for( serviceType
= kServiceTypes
; serviceType
< end
; ++serviceType
)
7246 if( strcasecmp( inName
, serviceType
->name
) == 0 ) return( serviceType
->description
);
7251 //===========================================================================================================================
7252 // SocketContextCancelHandler
7253 //===========================================================================================================================
7255 static void SocketContextCancelHandler( void *inContext
)
7257 SocketContext
* const context
= (SocketContext
*) inContext
;
7259 ForgetSocket( &context
->sock
);
7263 //===========================================================================================================================
7265 //===========================================================================================================================
7267 static OSStatus
StringToInt32( const char *inString
, int32_t *outValue
)
7273 value
= strtol( inString
, &endPtr
, 0 );
7274 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
7275 require_action_quiet( ( value
>= INT32_MIN
) && ( value
<= INT32_MAX
), exit
, err
= kRangeErr
);
7277 *outValue
= (int32_t) value
;
7284 //===========================================================================================================================
7286 //===========================================================================================================================
7288 static OSStatus
StringToUInt32( const char *inString
, uint32_t *outValue
)
7294 value
= (uint32_t) strtol( inString
, &endPtr
, 0 );
7295 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
7304 #if( TARGET_OS_DARWIN )
7305 //===========================================================================================================================
7306 // GetDefaultDNSServer
7307 //===========================================================================================================================
7309 static OSStatus
GetDefaultDNSServer( sockaddr_ip
*outAddr
)
7312 dns_config_t
* config
;
7313 struct sockaddr
* addr
;
7316 config
= dns_configuration_copy();
7317 require_action( config
, exit
, err
= kUnknownErr
);
7320 for( i
= 0; i
< config
->n_resolver
; ++i
)
7322 const dns_resolver_t
* const resolver
= config
->resolver
[ i
];
7324 if( !resolver
->domain
&& ( resolver
->n_nameserver
> 0 ) )
7326 addr
= resolver
->nameserver
[ 0 ];
7330 require_action_quiet( addr
, exit
, err
= kNotFoundErr
);
7332 SockAddrCopy( addr
, outAddr
);
7336 if( config
) dns_configuration_free( config
);
7341 //===========================================================================================================================
7344 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
7345 //===========================================================================================================================
7347 OSStatus
SocketWriteAll( SocketRef inSock
, const void *inData
, size_t inSize
, int32_t inTimeoutSecs
)
7350 const uint8_t * src
;
7351 const uint8_t * end
;
7353 struct timeval timeout
;
7356 FD_ZERO( &writeSet
);
7357 src
= (const uint8_t *) inData
;
7361 FD_SET( inSock
, &writeSet
);
7362 timeout
.tv_sec
= inTimeoutSecs
;
7363 timeout
.tv_usec
= 0;
7364 n
= select( (int)( inSock
+ 1 ), NULL
, &writeSet
, NULL
, &timeout
);
7365 if( n
== 0 ) { err
= kTimeoutErr
; goto exit
; }
7366 err
= map_socket_value_errno( inSock
, n
> 0, n
);
7367 require_noerr( err
, exit
);
7369 n
= send( inSock
, (char *) src
, (size_t)( end
- src
), 0 );
7370 err
= map_socket_value_errno( inSock
, n
>= 0, n
);
7371 if( err
== EINTR
) continue;
7372 require_noerr( err
, exit
);
7382 //===========================================================================================================================
7383 // ParseEscapedString
7385 // Note: This was copied from CoreUtils because the ParseEscapedString function is currently not exported in the framework.
7386 //===========================================================================================================================
7395 size_t * outCopiedLen
,
7396 size_t * outTotalLen
,
7397 const char ** outSrc
)
7406 lim
= dst
+ ( ( inMaxLen
> 0 ) ? ( inMaxLen
- 1 ) : 0 ); // Leave room for null terminator.
7408 while( ( inSrc
< inEnd
) && ( ( c
= *inSrc
++ ) != inDelimiter
) )
7412 require_action_quiet( inSrc
< inEnd
, exit
, err
= kUnderrunErr
);
7417 if( inBuf
) *dst
= c
;
7422 if( inBuf
&& ( inMaxLen
> 0 ) ) *dst
= '\0';
7426 if( outCopiedLen
) *outCopiedLen
= (size_t)( dst
- inBuf
);
7427 if( outTotalLen
) *outTotalLen
= len
;
7428 if( outSrc
) *outSrc
= inSrc
;
7432 //===========================================================================================================================
7435 // Warning: "inBuffer" may be modified even in error cases.
7437 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
7438 //===========================================================================================================================
7440 static OSStatus
ParseIPv4Address( const char *inStr
, uint8_t inBuffer
[ 4 ], const char **outStr
)
7456 for( ; ( c
= *inStr
) != '\0'; ++inStr
)
7458 if( isdigit_safe( c
) )
7460 v
= ( *dst
* 10 ) + ( c
- '0' );
7461 require_action_quiet( v
<= 255, exit
, err
= kRangeErr
);
7466 require_action_quiet( segments
<= 4, exit
, err
= kOverrunErr
);
7470 else if( ( c
== '.' ) && sawDigit
)
7472 require_action_quiet( segments
< 4, exit
, err
= kMalformedErr
);
7481 require_action_quiet( segments
== 4, exit
, err
= kUnderrunErr
);
7490 //===========================================================================================================================
7491 // StringToIPv4Address
7493 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
7494 //===========================================================================================================================
7497 StringToIPv4Address(
7499 StringToIPAddressFlags inFlags
,
7502 uint32_t * outSubnet
,
7503 uint32_t * outRouter
,
7504 const char ** outStr
)
7514 uint32_t subnetMask
;
7517 require_action( inStr
, exit
, err
= kParamErr
);
7519 // Parse the address-only part of the address (e.g. "1.2.3.4").
7521 err
= ParseIPv4Address( inStr
, buf
, &inStr
);
7522 require_noerr_quiet( err
, exit
);
7523 ip
= (uint32_t)( ( buf
[ 0 ] << 24 ) | ( buf
[ 1 ] << 16 ) | ( buf
[ 2 ] << 8 ) | buf
[ 3 ] );
7526 // Parse the port (if any).
7532 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPort
), exit
, err
= kUnexpectedErr
);
7533 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) port
= ( port
* 10 ) + ( c
- '0' );
7534 require_action_quiet( port
<= 65535, exit
, err
= kRangeErr
);
7538 // Parse the prefix length (if any).
7546 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPrefix
), exit
, err
= kUnexpectedErr
);
7547 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) prefix
= ( prefix
* 10 ) + ( c
- '0' );
7548 require_action_quiet( ( prefix
>= 0 ) && ( prefix
<= 32 ), exit
, err
= kRangeErr
);
7551 subnetMask
= ( prefix
> 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix
) ) : 0;
7552 router
= ( ip
& subnetMask
) | 1;
7555 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
7557 if( outIP
) *outIP
= ip
;
7558 if( outPort
&& hasPort
) *outPort
= port
;
7559 if( outSubnet
&& hasPrefix
) *outSubnet
= subnetMask
;
7560 if( outRouter
&& hasPrefix
) *outRouter
= router
;
7561 if( outStr
) *outStr
= inStr
;
7568 //===========================================================================================================================
7571 // Note: Parsed according to the rules specified in RFC 3513.
7572 // Warning: "inBuffer" may be modified even in error cases.
7574 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
7575 //===========================================================================================================================
7577 static OSStatus
ParseIPv6Address( const char *inStr
, int inAllowV4Mapped
, uint8_t inBuffer
[ 16 ], const char **outStr
)
7579 // Table to map uppercase hex characters - '0' to their numeric values.
7580 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
7581 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 };
7593 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
7595 for( i
= 0; i
< 16; ++i
) inBuffer
[ i
] = 0;
7597 // Special case leading :: (e.g. "::1") to simplify processing later.
7602 require_action_quiet( *inStr
== ':', exit
, err
= kMalformedErr
);
7605 // Parse the address.
7613 while( ( ( c
= *inStr
++ ) != '\0' ) && ( c
!= '%' ) && ( c
!= '/' ) && ( c
!= ']' ) )
7615 if( ( c
>= 'a' ) && ( c
<= 'f' ) ) c
-= ( 'a' - 'A' );
7616 if( ( ( c
>= '0' ) && ( c
<= '9' ) ) || ( ( c
>= 'A' ) && ( c
<= 'F' ) ) )
7619 check( c
< (int) countof( kASCIItoHexTable
) );
7620 v
= ( v
<< 4 ) | kASCIItoHexTable
[ c
];
7621 require_action_quiet( v
<= 0xFFFF, exit
, err
= kRangeErr
);
7630 require_action_quiet( !colonPtr
, exit
, err
= kMalformedErr
);
7634 require_action_quiet( *inStr
!= '\0', exit
, err
= kUnderrunErr
);
7635 require_action_quiet( ( dst
+ 2 ) <= lim
, exit
, err
= kOverrunErr
);
7636 *dst
++ = (uint8_t)( ( v
>> 8 ) & 0xFF );
7637 *dst
++ = (uint8_t)( v
& 0xFF );
7643 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
7645 if( inAllowV4Mapped
&& ( c
== '.' ) && ( ( dst
+ 4 ) <= lim
) )
7647 err
= ParseIPv4Address( ptr
, dst
, &inStr
);
7648 require_noerr_quiet( err
, exit
);
7651 ++inStr
; // Increment because the code below expects the end to be at "inStr - 1".
7657 require_action_quiet( ( dst
+ 2 ) <= lim
, exit
, err
= kOverrunErr
);
7658 *dst
++ = (uint8_t)( ( v
>> 8 ) & 0xFF );
7659 *dst
++ = (uint8_t)( v
& 0xFF );
7661 check( dst
<= lim
);
7664 require_action_quiet( dst
< lim
, exit
, err
= kOverrunErr
);
7665 n
= (int)( dst
- colonPtr
);
7666 for( i
= 1; i
<= n
; ++i
)
7668 lim
[ -i
] = colonPtr
[ n
- i
];
7669 colonPtr
[ n
- i
] = 0;
7673 require_action_quiet( dst
== lim
, exit
, err
= kUnderrunErr
);
7675 *outStr
= inStr
- 1;
7682 //===========================================================================================================================
7685 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
7686 //===========================================================================================================================
7688 static OSStatus
ParseIPv6Scope( const char *inStr
, uint32_t *outScope
, const char **outStr
)
7690 #if( TARGET_OS_POSIX )
7692 char scopeStr
[ 64 ];
7699 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
7702 lim
= dst
+ ( countof( scopeStr
) - 1 );
7703 while( ( ( c
= *inStr
) != '\0' ) && ( c
!= ':' ) && ( c
!= '/' ) && ( c
!= ']' ) && ( dst
< lim
) )
7708 check( dst
<= lim
);
7710 // First try to map as a name and if that fails, treat it as a numeric scope.
7712 scope
= if_nametoindex( scopeStr
);
7715 for( ptr
= scopeStr
; ( ( c
= *ptr
) >= '0' ) && ( c
<= '9' ); ++ptr
)
7717 scope
= ( scope
* 10 ) + ( ( (uint8_t) c
) - '0' );
7719 require_action_quiet( c
== '\0', exit
, err
= kMalformedErr
);
7720 require_action_quiet( ( ptr
!= scopeStr
) && ( ( (int)( ptr
- scopeStr
) ) <= 10 ), exit
, err
= kMalformedErr
);
7736 for( start
= inStr
; ( ( c
= *inStr
) >= '0' ) && ( c
<= '9' ); ++inStr
)
7738 scope
= ( scope
* 10 ) + ( c
- '0' );
7740 require_action_quiet( ( inStr
!= start
) && ( ( (int)( inStr
- start
) ) <= 10 ), exit
, err
= kMalformedErr
);
7751 //===========================================================================================================================
7752 // StringToIPv6Address
7754 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
7755 //===========================================================================================================================
7758 StringToIPv6Address(
7760 StringToIPAddressFlags inFlags
,
7761 uint8_t outIPv6
[ 16 ],
7762 uint32_t * outScope
,
7765 const char ** outStr
)
7779 require_action( inStr
, exit
, err
= kParamErr
);
7781 if( *inStr
== '[' ) ++inStr
; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
7783 // Parse the address-only part of the address (e.g. "1::1").
7785 err
= ParseIPv6Address( inStr
, !( inFlags
& kStringToIPAddressFlagsNoIPv4Mapped
), ipv6
, &inStr
);
7786 require_noerr_quiet( err
, exit
);
7789 // Parse the scope, port, or prefix length.
7800 if( c
== '%' ) // Scope (e.g. "%en0" or "%5")
7802 require_action_quiet( !hasScope
, exit
, err
= kMalformedErr
);
7803 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoScope
), exit
, err
= kUnexpectedErr
);
7805 err
= ParseIPv6Scope( inStr
, &scope
, &inStr
);
7806 require_noerr_quiet( err
, exit
);
7810 else if( c
== ':' ) // Port (e.g. ":80")
7812 require_action_quiet( !hasPort
, exit
, err
= kMalformedErr
);
7813 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPort
), exit
, err
= kUnexpectedErr
);
7814 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) port
= ( port
* 10 ) + ( c
- '0' );
7815 require_action_quiet( port
<= 65535, exit
, err
= kRangeErr
);
7818 else if( c
== '/' ) // Prefix Length (e.g. "/64")
7820 require_action_quiet( !hasPrefix
, exit
, err
= kMalformedErr
);
7821 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPrefix
), exit
, err
= kUnexpectedErr
);
7822 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) prefix
= ( prefix
* 10 ) + ( c
- '0' );
7823 require_action_quiet( ( prefix
>= 0 ) && ( prefix
<= 128 ), exit
, err
= kRangeErr
);
7828 require_action_quiet( !hasBracket
, exit
, err
= kMalformedErr
);
7838 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
7840 if( outIPv6
) for( i
= 0; i
< 16; ++i
) outIPv6
[ i
] = ipv6
[ i
];
7841 if( outScope
&& hasScope
) *outScope
= scope
;
7842 if( outPort
&& hasPort
) *outPort
= port
;
7843 if( outPrefix
&& hasPrefix
) *outPrefix
= prefix
;
7844 if( outStr
) *outStr
= inStr
;
7851 //===========================================================================================================================
7852 // StringArray_Append
7854 // Note: This was copied from CoreUtils because the StringArray_Append function is currently not exported in the framework.
7855 //===========================================================================================================================
7857 OSStatus
StringArray_Append( char ***ioArray
, size_t *ioCount
, const char *inStr
)
7866 newStr
= strdup( inStr
);
7867 require_action( newStr
, exit
, err
= kNoMemoryErr
);
7869 oldCount
= *ioCount
;
7870 newCount
= oldCount
+ 1;
7871 newArray
= (char **) malloc( newCount
* sizeof( *newArray
) );
7872 require_action( newArray
, exit
, err
= kNoMemoryErr
);
7876 oldArray
= *ioArray
;
7877 memcpy( newArray
, oldArray
, oldCount
* sizeof( *oldArray
) );
7880 newArray
[ oldCount
] = newStr
;
7883 *ioArray
= newArray
;
7884 *ioCount
= newCount
;
7888 if( newStr
) free( newStr
);
7892 //===========================================================================================================================
7895 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
7896 //===========================================================================================================================
7898 void StringArray_Free( char **inArray
, size_t inCount
)
7902 for( i
= 0; i
< inCount
; ++i
)
7904 free( inArray
[ i
] );
7906 if( inCount
> 0 ) free( inArray
);
7909 //===========================================================================================================================
7910 // ParseQuotedEscapedString
7912 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
7913 //===========================================================================================================================
7916 ParseQuotedEscapedString(
7919 const char * inDelimiters
,
7922 size_t * outCopiedLen
,
7923 size_t * outTotalLen
,
7924 const char ** outSrc
)
7926 const unsigned char * src
;
7927 const unsigned char * end
;
7928 unsigned char * dst
;
7929 unsigned char * lim
;
7933 Boolean singleQuote
;
7934 Boolean doubleQuote
;
7936 if( inEnd
== NULL
) inEnd
= inSrc
+ strlen( inSrc
);
7937 src
= (const unsigned char *) inSrc
;
7938 end
= (const unsigned char *) inEnd
;
7939 dst
= (unsigned char *) inBuf
;
7940 lim
= dst
+ inMaxLen
;
7941 while( ( src
< end
) && isspace_safe( *src
) ) ++src
; // Skip leading spaces.
7942 if( src
>= end
) return( false );
7944 // Parse each argument from the string.
7946 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
7949 singleQuote
= false;
7950 doubleQuote
= false;
7956 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
7960 singleQuote
= false;
7964 else if( doubleQuote
)
7966 // Double quotes protect everything except double quotes and backslashes. A backslash can be
7967 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
7968 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
7969 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
7970 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
7974 doubleQuote
= false;
7977 else if( c
== '\\' )
7982 if( ( c2
== '"' ) || ( c2
== '\\' ) )
7987 else if( c2
== '\n' )
7992 else if( ( c2
== 'x' ) || ( c2
== 'X' ) )
7996 if( ( ( end
- src
) >= 2 ) && IsHexPair( src
) )
7998 c
= HexPairToByte( src
);
8002 else if( isoctal_safe( c2
) )
8004 if( ( ( end
- src
) >= 3 ) && IsOctalTriple( src
) )
8006 c
= OctalTripleToByte( src
);
8013 else if( strchr( inDelimiters
, c
) )
8017 else if( c
== '\\' )
8019 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
8020 // A backslash followed by a newline disappears completely.
8021 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
8022 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
8032 else if( ( c
== 'x' ) || ( c
== 'X' ) )
8035 if( ( ( end
- src
) >= 2 ) && IsHexPair( src
) )
8037 c
= HexPairToByte( src
);
8041 else if( isoctal_safe( c
) )
8043 if( ( ( end
- src
) >= 3 ) && IsOctalTriple( src
) )
8045 c
= OctalTripleToByte( src
);
8059 else if( c
== '\'' )
8072 if( inBuf
) *dst
= c
;
8078 if( outCopiedLen
) *outCopiedLen
= (size_t)( dst
- ( (unsigned char *) inBuf
) );
8079 if( outTotalLen
) *outTotalLen
= totalLen
;
8080 if( outSrc
) *outSrc
= (const char *) src
;