2 Copyright (c) 2016-2018 Apple Inc. All rights reserved.
4 dnssdutil is a command-line utility for testing the DNS-SD API.
7 #include <CoreUtils/CommonServices.h> // Include early.
8 #include <CoreUtils/AsyncConnection.h>
9 #include <CoreUtils/CommandLineUtils.h>
10 #include <CoreUtils/DataBufferUtils.h>
11 #include <CoreUtils/DebugServices.h>
12 #include <CoreUtils/HTTPUtils.h>
13 #include <CoreUtils/MiscUtils.h>
14 #include <CoreUtils/NetUtils.h>
15 #include <CoreUtils/PrintFUtils.h>
16 #include <CoreUtils/RandomNumberUtils.h>
17 #include <CoreUtils/SoftLinking.h>
18 #include <CoreUtils/StringUtils.h>
19 #include <CoreUtils/TickUtils.h>
21 #include <dns_sd_private.h>
23 #if( TARGET_OS_DARWIN )
27 #include <sys/proc_info.h>
30 #if( TARGET_OS_POSIX )
31 #include <sys/resource.h>
34 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
35 #include "tweetnacl.h" // TweetNaCl from <https://tweetnacl.cr.yp.to/software.html>.
38 //===========================================================================================================================
40 //===========================================================================================================================
44 #define kDNSSDUtilNumVersion NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 )
46 #if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) )
47 #define DNSSDUTIL_SOURCE_VERSION "0.0.0"
50 // DNS-SD API flag descriptors
52 #define kDNSServiceFlagsDescriptors \
53 "\x00" "AutoTrigger\0" \
56 "\x03" "NoAutoRename\0" \
59 "\x06" "BrowseDomains\0" \
60 "\x07" "RegistrationDomains\0" \
61 "\x08" "LongLivedQuery\0" \
62 "\x09" "AllowRemoteQuery\0" \
63 "\x0A" "ForceMulticast\0" \
64 "\x0B" "KnownUnique\0" \
65 "\x0C" "ReturnIntermediates\0" \
66 "\x0D" "NonBrowsable\0" \
67 "\x0E" "ShareConnection\0" \
68 "\x0F" "SuppressUnusable\0" \
70 "\x11" "IncludeP2P\0" \
71 "\x12" "WakeOnResolve\0" \
72 "\x13" "BackgroundTrafficClass\0" \
73 "\x14" "IncludeAWDL\0" \
75 "\x16" "UnicastResponse\0" \
76 "\x17" "ValidateOptional\0" \
77 "\x18" "WakeOnlyService\0" \
78 "\x19" "ThresholdOne\0" \
79 "\x1A" "ThresholdFinder\0" \
80 "\x1B" "DenyCellular\0" \
81 "\x1C" "ServiceIndex\0" \
82 "\x1D" "DenyExpensive\0" \
83 "\x1E" "PathEvaluationDone\0" \
86 #define kDNSServiceProtocolDescriptors \
95 #define kDNSHeaderFlag_Response ( 1 << 15 )
96 #define kDNSHeaderFlag_AuthAnswer ( 1 << 10 )
97 #define kDNSHeaderFlag_Truncation ( 1 << 9 )
98 #define kDNSHeaderFlag_RecursionDesired ( 1 << 8 )
99 #define kDNSHeaderFlag_RecursionAvailable ( 1 << 7 )
101 #define kDNSOpCode_Query 0
102 #define kDNSOpCode_InverseQuery 1
103 #define kDNSOpCode_Status 2
104 #define kDNSOpCode_Notify 4
105 #define kDNSOpCode_Update 5
107 #define kDNSRCode_NoError 0
108 #define kDNSRCode_FormatError 1
109 #define kDNSRCode_ServerFailure 2
110 #define kDNSRCode_NXDomain 3
111 #define kDNSRCode_NotImplemented 4
112 #define kDNSRCode_Refused 5
114 #define kQClassUnicastResponseBit ( 1U << 15 )
115 #define kRRClassCacheFlushBit ( 1U << 15 )
117 #define kDomainLabelLengthMax 63
118 #define kDomainNameLengthMax 256
120 //===========================================================================================================================
121 // Gerneral Command Options
122 //===========================================================================================================================
124 // Command option macros
126 #define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON ) \
127 CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None, \
130 #define kRequiredOptionSuffix " [REQUIRED]"
132 #define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
133 CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, \
134 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
135 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
137 #define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
138 MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
140 #define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
141 CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
142 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
143 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
145 #define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \
146 CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL )
148 #define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
149 CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
150 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
151 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
153 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
154 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
156 // DNS-SD API flag options
158 static int gDNSSDFlags
= 0;
159 static int gDNSSDFlag_BrowseDomains
= false;
160 static int gDNSSDFlag_DenyCellular
= false;
161 static int gDNSSDFlag_DenyExpensive
= false;
162 static int gDNSSDFlag_ForceMulticast
= false;
163 static int gDNSSDFlag_IncludeAWDL
= false;
164 static int gDNSSDFlag_NoAutoRename
= false;
165 static int gDNSSDFlag_PathEvaluationDone
= false;
166 static int gDNSSDFlag_RegistrationDomains
= false;
167 static int gDNSSDFlag_ReturnIntermediates
= false;
168 static int gDNSSDFlag_Shared
= false;
169 static int gDNSSDFlag_SuppressUnusable
= false;
170 static int gDNSSDFlag_Timeout
= false;
171 static int gDNSSDFlag_UnicastResponse
= false;
172 static int gDNSSDFlag_Unique
= false;
173 static int gDNSSDFlag_WakeOnResolve
= false;
175 #define DNSSDFlagsOption() \
176 IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \
177 "DNSServiceFlags as an integer. This value is bitwise ORed with other single flag options.", false )
179 #define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \
180 BooleanOption( SHORT_CHAR, # FLAG_NAME, &gDNSSDFlag_ ## FLAG_NAME, "Use kDNSServiceFlags" # FLAG_NAME "." )
182 #define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular )
183 #define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive )
184 #define DNSSDFlagsOption_ForceMulticast() DNSSDFlagOption( 'M', ForceMulticast )
185 #define DNSSDFlagsOption_IncludeAWDL() DNSSDFlagOption( 'A', IncludeAWDL )
186 #define DNSSDFlagsOption_NoAutoRename() DNSSDFlagOption( 'N', NoAutoRename )
187 #define DNSSDFlagsOption_PathEvalDone() DNSSDFlagOption( 'P', PathEvaluationDone )
188 #define DNSSDFlagsOption_ReturnIntermediates() DNSSDFlagOption( 'I', ReturnIntermediates )
189 #define DNSSDFlagsOption_Shared() DNSSDFlagOption( 'S', Shared )
190 #define DNSSDFlagsOption_SuppressUnusable() DNSSDFlagOption( 'S', SuppressUnusable )
191 #define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout )
192 #define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse )
193 #define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique )
194 #define DNSSDFlagsOption_WakeOnResolve() DNSSDFlagOption( 'W', WakeOnResolve )
198 static const char * gInterface
= NULL
;
200 #define InterfaceOption() \
201 StringOption( 'i', "interface", &gInterface, "interface", \
202 "Network interface by name or index. Use index -1 for local-only.", false )
204 // Connection options
206 #define kConnectionArg_Normal ""
207 #define kConnectionArgPrefix_PID "pid:"
208 #define kConnectionArgPrefix_UUID "uuid:"
210 static const char * gConnectionOpt
= kConnectionArg_Normal
;
212 #define ConnectionOptions() \
213 { kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type", \
214 kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL, \
215 "Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL }
217 #define kConnectionSection_Name "Connection Option"
218 #define kConnectionSection_Text \
219 "The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n" \
220 "the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n" \
221 "specifying the connection option without an argument, i.e.,\n" \
225 "To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n" \
227 " --connection=pid:<PID>\n" \
229 "to specify the delegator by PID, or use\n" \
231 " --connection=uuid:<UUID>\n" \
233 "to specify the delegator by UUID.\n" \
235 "To not use a main connection at all, but instead perform operations on their own implicit connections, use\n" \
239 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
241 // Help text for record data options
243 #define kRDataArgPrefix_Domain "domain:"
244 #define kRDataArgPrefix_File "file:"
245 #define kRDataArgPrefix_HexString "hex:"
246 #define kRDataArgPrefix_IPv4 "ipv4:"
247 #define kRDataArgPrefix_IPv6 "ipv6:"
248 #define kRDataArgPrefix_SRV "srv:"
249 #define kRDataArgPrefix_String "string:"
250 #define kRDataArgPrefix_TXT "txt:"
252 #define kRecordDataSection_Name "Record Data Arguments"
253 #define kRecordDataSection_Text \
254 "A record data argument is specified in one of the following formats:\n" \
256 "Format Syntax Example\n" \
257 "Domain name domain:<domain name> domain:demo._test._tcp.local\n" \
258 "File containing record data file:<file path> file:/path/to/rdata.bin\n" \
259 "Hexadecimal string hex:<hex string> hex:c0000201 or hex:'C0 00 02 01'\n" \
260 "IPv4 address ipv4:<IPv4 address> ipv4:192.0.2.1\n" \
261 "IPv6 address ipv6:<IPv6 address> ipv6:2001:db8::1\n" \
262 "SRV record srv:<priority>,<weight>,<port>,<target> srv:0,0,64206,example.local\n" \
263 "String (w/escaped hex bytes) string:<string> string:'\\x09color=red'\n" \
264 "TXT record keys and values txt:<comma-delimited keys and values> txt:'vers=1.0,lang=en\\,es\\,fr,passreq'\n"
266 #define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text )
268 //===========================================================================================================================
269 // Browse Command Options
270 //===========================================================================================================================
272 static char ** gBrowse_ServiceTypes
= NULL
;
273 static size_t gBrowse_ServiceTypesCount
= 0;
274 static const char * gBrowse_Domain
= NULL
;
275 static int gBrowse_DoResolve
= false;
276 static int gBrowse_QueryTXT
= false;
277 static int gBrowse_TimeLimitSecs
= 0;
279 static CLIOption kBrowseOpts
[] =
282 MultiStringOption( 't', "type", &gBrowse_ServiceTypes
, &gBrowse_ServiceTypesCount
, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ),
283 StringOption( 'd', "domain", &gBrowse_Domain
, "domain", "Domain in which to browse for the service type(s).", false ),
285 CLI_OPTION_GROUP( "Flags" ),
287 DNSSDFlagsOption_IncludeAWDL(),
289 CLI_OPTION_GROUP( "Operation" ),
291 BooleanOption( 0 , "resolve", &gBrowse_DoResolve
, "Resolve service instances." ),
292 BooleanOption( 0 , "queryTXT", &gBrowse_QueryTXT
, "Query TXT records of service instances." ),
293 IntegerOption( 'l', "timeLimit", &gBrowse_TimeLimitSecs
, "seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ),
299 //===========================================================================================================================
300 // GetAddrInfo Command Options
301 //===========================================================================================================================
303 static const char * gGetAddrInfo_Name
= NULL
;
304 static int gGetAddrInfo_ProtocolIPv4
= false;
305 static int gGetAddrInfo_ProtocolIPv6
= false;
306 static int gGetAddrInfo_OneShot
= false;
307 static int gGetAddrInfo_TimeLimitSecs
= 0;
309 static CLIOption kGetAddrInfoOpts
[] =
312 StringOption( 'n', "name", &gGetAddrInfo_Name
, "domain name", "Domain name to resolve.", true ),
313 BooleanOption( 0 , "ipv4", &gGetAddrInfo_ProtocolIPv4
, "Use kDNSServiceProtocol_IPv4." ),
314 BooleanOption( 0 , "ipv6", &gGetAddrInfo_ProtocolIPv6
, "Use kDNSServiceProtocol_IPv6." ),
316 CLI_OPTION_GROUP( "Flags" ),
318 DNSSDFlagsOption_DenyCellular(),
319 DNSSDFlagsOption_DenyExpensive(),
320 DNSSDFlagsOption_PathEvalDone(),
321 DNSSDFlagsOption_ReturnIntermediates(),
322 DNSSDFlagsOption_SuppressUnusable(),
323 DNSSDFlagsOption_Timeout(),
325 CLI_OPTION_GROUP( "Operation" ),
327 BooleanOption( 'o', "oneshot", &gGetAddrInfo_OneShot
, "Finish after first set of results." ),
328 IntegerOption( 'l', "timeLimit", &gGetAddrInfo_TimeLimitSecs
, "seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ),
334 //===========================================================================================================================
335 // QueryRecord Command Options
336 //===========================================================================================================================
338 static const char * gQueryRecord_Name
= NULL
;
339 static const char * gQueryRecord_Type
= NULL
;
340 static int gQueryRecord_OneShot
= false;
341 static int gQueryRecord_TimeLimitSecs
= 0;
342 static int gQueryRecord_RawRData
= false;
344 static CLIOption kQueryRecordOpts
[] =
347 StringOption( 'n', "name", &gQueryRecord_Name
, "domain name", "Full domain name of record to query.", true ),
348 StringOption( 't', "type", &gQueryRecord_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
350 CLI_OPTION_GROUP( "Flags" ),
352 DNSSDFlagsOption_IncludeAWDL(),
353 DNSSDFlagsOption_ForceMulticast(),
354 DNSSDFlagsOption_Timeout(),
355 DNSSDFlagsOption_ReturnIntermediates(),
356 DNSSDFlagsOption_SuppressUnusable(),
357 DNSSDFlagsOption_UnicastResponse(),
358 DNSSDFlagsOption_DenyCellular(),
359 DNSSDFlagsOption_DenyExpensive(),
360 DNSSDFlagsOption_PathEvalDone(),
362 CLI_OPTION_GROUP( "Operation" ),
364 BooleanOption( 'o', "oneshot", &gQueryRecord_OneShot
, "Finish after first set of results." ),
365 IntegerOption( 'l', "timeLimit", &gQueryRecord_TimeLimitSecs
, "seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ),
366 BooleanOption( 0 , "raw", &gQueryRecord_RawRData
, "Show record data as a hexdump." ),
372 //===========================================================================================================================
373 // Register Command Options
374 //===========================================================================================================================
376 static const char * gRegister_Name
= NULL
;
377 static const char * gRegister_Type
= NULL
;
378 static const char * gRegister_Domain
= NULL
;
379 static int gRegister_Port
= 0;
380 static const char * gRegister_TXT
= NULL
;
381 static int gRegister_LifetimeMs
= -1;
382 static const char ** gAddRecord_Types
= NULL
;
383 static size_t gAddRecord_TypesCount
= 0;
384 static const char ** gAddRecord_Data
= NULL
;
385 static size_t gAddRecord_DataCount
= 0;
386 static const char ** gAddRecord_TTLs
= NULL
;
387 static size_t gAddRecord_TTLsCount
= 0;
388 static const char * gUpdateRecord_Data
= NULL
;
389 static int gUpdateRecord_DelayMs
= 0;
390 static int gUpdateRecord_TTL
= 0;
392 static CLIOption kRegisterOpts
[] =
395 StringOption( 'n', "name", &gRegister_Name
, "service name", "Name of service.", false ),
396 StringOption( 't', "type", &gRegister_Type
, "service type", "Service type, e.g., \"_ssh._tcp\".", true ),
397 StringOption( 'd', "domain", &gRegister_Domain
, "domain", "Domain in which to advertise the service.", false ),
398 IntegerOption( 'p', "port", &gRegister_Port
, "port number", "Service's port number.", true ),
399 StringOption( 0 , "txt", &gRegister_TXT
, "record data", "The TXT record data. See " kRecordDataSection_Name
" below.", false ),
401 CLI_OPTION_GROUP( "Flags" ),
403 DNSSDFlagsOption_IncludeAWDL(),
404 DNSSDFlagsOption_NoAutoRename(),
406 CLI_OPTION_GROUP( "Operation" ),
407 IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs
, "ms", "Lifetime of the service registration in milliseconds.", false ),
409 CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ),
410 StringOption( 0 , "updateData", &gUpdateRecord_Data
, "record data", "Record data for the record update. See " kRecordDataSection_Name
" below.", false ),
411 IntegerOption( 0 , "updateDelay", &gUpdateRecord_DelayMs
, "ms", "Number of milliseconds after registration to wait before record update.", false ),
412 IntegerOption( 0 , "updateTTL", &gUpdateRecord_TTL
, "seconds", "Time-to-live of the updated record.", false ),
414 CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ),
415 MultiStringOption( 0 , "addType", &gAddRecord_Types
, &gAddRecord_TypesCount
, "record type", "Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ),
416 MultiStringOptionEx( 0 , "addData", &gAddRecord_Data
, &gAddRecord_DataCount
, "record data", "Additional record's data. See " kRecordDataSection_Name
" below.", false, NULL
),
417 MultiStringOption( 0 , "addTTL", &gAddRecord_TTLs
, &gAddRecord_TTLsCount
, "seconds", "Time-to-live of additional record in seconds. Use '0' for default.", false ),
423 //===========================================================================================================================
424 // RegisterRecord Command Options
425 //===========================================================================================================================
427 static const char * gRegisterRecord_Name
= NULL
;
428 static const char * gRegisterRecord_Type
= NULL
;
429 static const char * gRegisterRecord_Data
= NULL
;
430 static int gRegisterRecord_TTL
= 0;
431 static int gRegisterRecord_LifetimeMs
= -1;
432 static const char * gRegisterRecord_UpdateData
= NULL
;
433 static int gRegisterRecord_UpdateDelayMs
= 0;
434 static int gRegisterRecord_UpdateTTL
= 0;
436 static CLIOption kRegisterRecordOpts
[] =
439 StringOption( 'n', "name", &gRegisterRecord_Name
, "record name", "Fully qualified domain name of record.", true ),
440 StringOption( 't', "type", &gRegisterRecord_Type
, "record type", "Record type by name (e.g., TXT, PTR, A) or number.", true ),
441 StringOption( 'd', "data", &gRegisterRecord_Data
, "record data", "The record data. See " kRecordDataSection_Name
" below.", false ),
442 IntegerOption( 0 , "ttl", &gRegisterRecord_TTL
, "seconds", "Time-to-live in seconds. Use '0' for default.", false ),
444 CLI_OPTION_GROUP( "Flags" ),
446 DNSSDFlagsOption_IncludeAWDL(),
447 DNSSDFlagsOption_Shared(),
448 DNSSDFlagsOption_Unique(),
450 CLI_OPTION_GROUP( "Operation" ),
451 IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs
, "ms", "Lifetime of the service registration in milliseconds.", false ),
453 CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ),
454 StringOption( 0 , "updateData", &gRegisterRecord_UpdateData
, "record data", "Record data for the record update.", false ),
455 IntegerOption( 0 , "updateDelay", &gRegisterRecord_UpdateDelayMs
, "ms", "Number of milliseconds after registration to wait before record update.", false ),
456 IntegerOption( 0 , "updateTTL", &gRegisterRecord_UpdateTTL
, "seconds", "Time-to-live of the updated record.", false ),
462 //===========================================================================================================================
463 // Resolve Command Options
464 //===========================================================================================================================
466 static char * gResolve_Name
= NULL
;
467 static char * gResolve_Type
= NULL
;
468 static char * gResolve_Domain
= NULL
;
469 static int gResolve_TimeLimitSecs
= 0;
471 static CLIOption kResolveOpts
[] =
474 StringOption( 'n', "name", &gResolve_Name
, "service name", "Name of the service instance to resolve.", true ),
475 StringOption( 't', "type", &gResolve_Type
, "service type", "Type of the service instance to resolve.", true ),
476 StringOption( 'd', "domain", &gResolve_Domain
, "domain", "Domain of the service instance to resolve.", true ),
478 CLI_OPTION_GROUP( "Flags" ),
480 DNSSDFlagsOption_ForceMulticast(),
481 DNSSDFlagsOption_IncludeAWDL(),
482 DNSSDFlagsOption_ReturnIntermediates(),
483 DNSSDFlagsOption_WakeOnResolve(),
485 CLI_OPTION_GROUP( "Operation" ),
487 IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs
, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ),
493 //===========================================================================================================================
494 // Reconfirm Command Options
495 //===========================================================================================================================
497 static const char * gReconfirmRecord_Name
= NULL
;
498 static const char * gReconfirmRecord_Type
= NULL
;
499 static const char * gReconfirmRecord_Class
= NULL
;
500 static const char * gReconfirmRecord_Data
= NULL
;
502 static CLIOption kReconfirmOpts
[] =
505 StringOption( 'n', "name", &gReconfirmRecord_Name
, "record name", "Full name of the record to reconfirm.", true ),
506 StringOption( 't', "type", &gReconfirmRecord_Type
, "record type", "Type of the record to reconfirm.", true ),
507 StringOption( 'c', "class", &gReconfirmRecord_Class
, "record class", "Class of the record to reconfirm. Default class is IN.", false ),
508 StringOption( 'd', "data", &gReconfirmRecord_Data
, "record data", "The record data. See " kRecordDataSection_Name
" below.", false ),
510 CLI_OPTION_GROUP( "Flags" ),
517 //===========================================================================================================================
518 // getaddrinfo-POSIX Command Options
519 //===========================================================================================================================
521 static const char * gGAIPOSIX_HostName
= NULL
;
522 static const char * gGAIPOSIX_ServName
= NULL
;
523 static const char * gGAIPOSIX_Family
= NULL
;
524 static int gGAIPOSIXFlag_AddrConfig
= false;
525 static int gGAIPOSIXFlag_All
= false;
526 static int gGAIPOSIXFlag_CanonName
= false;
527 static int gGAIPOSIXFlag_NumericHost
= false;
528 static int gGAIPOSIXFlag_NumericServ
= false;
529 static int gGAIPOSIXFlag_Passive
= false;
530 static int gGAIPOSIXFlag_V4Mapped
= false;
531 #if( defined( AI_V4MAPPED_CFG ) )
532 static int gGAIPOSIXFlag_V4MappedCFG
= false;
534 #if( defined( AI_DEFAULT ) )
535 static int gGAIPOSIXFlag_Default
= false;
537 #if( defined( AI_UNUSABLE ) )
538 static int gGAIPOSIXFlag_Unusable
= false;
541 static CLIOption kGetAddrInfoPOSIXOpts
[] =
543 StringOption( 'n', "hostname", &gGAIPOSIX_HostName
, "hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ),
544 StringOption( 's', "servname", &gGAIPOSIX_ServName
, "servname", "Port number in decimal or service name from services(5).", false ),
546 CLI_OPTION_GROUP( "Hints " ),
547 StringOptionEx( 'f', "family", &gGAIPOSIX_Family
, "address family", "Address family to use for hints ai_family field.", false,
549 "Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n"
550 "address family is specified, then AF_UNSPEC is used.\n"
552 BooleanOption( 0 , "flag-addrconfig", &gGAIPOSIXFlag_AddrConfig
, "In hints ai_flags field, set AI_ADDRCONFIG." ),
553 BooleanOption( 0 , "flag-all", &gGAIPOSIXFlag_All
, "In hints ai_flags field, set AI_ALL." ),
554 BooleanOption( 0 , "flag-canonname", &gGAIPOSIXFlag_CanonName
, "In hints ai_flags field, set AI_CANONNAME." ),
555 BooleanOption( 0 , "flag-numerichost", &gGAIPOSIXFlag_NumericHost
, "In hints ai_flags field, set AI_NUMERICHOST." ),
556 BooleanOption( 0 , "flag-numericserv", &gGAIPOSIXFlag_NumericServ
, "In hints ai_flags field, set AI_NUMERICSERV." ),
557 BooleanOption( 0 , "flag-passive", &gGAIPOSIXFlag_Passive
, "In hints ai_flags field, set AI_PASSIVE." ),
558 BooleanOption( 0 , "flag-v4mapped", &gGAIPOSIXFlag_V4Mapped
, "In hints ai_flags field, set AI_V4MAPPED." ),
559 #if( defined( AI_V4MAPPED_CFG ) )
560 BooleanOption( 0 , "flag-v4mappedcfg", &gGAIPOSIXFlag_V4MappedCFG
, "In hints ai_flags field, set AI_V4MAPPED_CFG." ),
562 #if( defined( AI_DEFAULT ) )
563 BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default
, "In hints ai_flags field, set AI_DEFAULT." ),
565 #if( defined( AI_UNUSABLE ) )
566 BooleanOption( 0 , "flag-unusable", &gGAIPOSIXFlag_Unusable
, "In hints ai_flags field, set AI_UNUSABLE." ),
569 CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ),
573 //===========================================================================================================================
574 // ReverseLookup Command Options
575 //===========================================================================================================================
577 static const char * gReverseLookup_IPAddr
= NULL
;
578 static int gReverseLookup_OneShot
= false;
579 static int gReverseLookup_TimeLimitSecs
= 0;
581 static CLIOption kReverseLookupOpts
[] =
584 StringOption( 'a', "address", &gReverseLookup_IPAddr
, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ),
586 CLI_OPTION_GROUP( "Flags" ),
588 DNSSDFlagsOption_ForceMulticast(),
589 DNSSDFlagsOption_ReturnIntermediates(),
590 DNSSDFlagsOption_SuppressUnusable(),
592 CLI_OPTION_GROUP( "Operation" ),
594 BooleanOption( 'o', "oneshot", &gReverseLookup_OneShot
, "Finish after first set of results." ),
595 IntegerOption( 'l', "timeLimit", &gReverseLookup_TimeLimitSecs
, "seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ),
601 //===========================================================================================================================
602 // PortMapping Command Options
603 //===========================================================================================================================
605 static int gPortMapping_ProtocolTCP
= false;
606 static int gPortMapping_ProtocolUDP
= false;
607 static int gPortMapping_InternalPort
= 0;
608 static int gPortMapping_ExternalPort
= 0;
609 static int gPortMapping_TTL
= 0;
611 static CLIOption kPortMappingOpts
[] =
614 BooleanOption( 0, "tcp", &gPortMapping_ProtocolTCP
, "Use kDNSServiceProtocol_TCP." ),
615 BooleanOption( 0, "udp", &gPortMapping_ProtocolUDP
, "Use kDNSServiceProtocol_UDP." ),
616 IntegerOption( 0, "internalPort", &gPortMapping_InternalPort
, "port number", "Internal port.", false ),
617 IntegerOption( 0, "externalPort", &gPortMapping_ExternalPort
, "port number", "Requested external port. Use '0' for any external port.", false ),
618 IntegerOption( 0, "ttl", &gPortMapping_TTL
, "seconds", "Requested TTL (renewal period) in seconds. Use '0' for a default value.", false ),
620 CLI_OPTION_GROUP( "Flags" ),
623 CLI_OPTION_GROUP( "Operation" ),
630 //===========================================================================================================================
631 // BrowseAll Command Options
632 //===========================================================================================================================
634 static const char * gBrowseAll_Domain
= NULL
;
635 static char ** gBrowseAll_ServiceTypes
= NULL
;
636 static size_t gBrowseAll_ServiceTypesCount
= 0;
637 static int gBrowseAll_IncludeAWDL
= false;
638 static int gBrowseAll_BrowseTimeSecs
= 5;
639 static int gBrowseAll_MaxConnectTimeSecs
= 0;
641 static CLIOption kBrowseAllOpts
[] =
644 StringOption( 'd', "domain", &gBrowseAll_Domain
, "domain", "Domain in which to browse for the service.", false ),
645 MultiStringOption( 't', "type", &gBrowseAll_ServiceTypes
, &gBrowseAll_ServiceTypesCount
, "service type", "Service type(s), e.g., \"_ssh._tcp\". All services are browsed for if none is specified.", false ),
647 CLI_OPTION_GROUP( "Flags" ),
648 DNSSDFlagsOption_IncludeAWDL(),
650 CLI_OPTION_GROUP( "Operation" ),
651 IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs
, "seconds", "Amount of time to spend browsing. (Default: 5 seconds)", false ),
652 IntegerOption( 'c', "maxConnectTime", &gBrowseAll_MaxConnectTimeSecs
, "seconds", "Max duration of connection attempts. If <= 0, then no connections are attempted. (Default: 0 seconds)", false ),
656 //===========================================================================================================================
657 // GetAddrInfoStress Command Options
658 //===========================================================================================================================
660 static int gGAIStress_TestDurationSecs
= 0;
661 static int gGAIStress_ConnectionCount
= 0;
662 static int gGAIStress_DurationMinMs
= 0;
663 static int gGAIStress_DurationMaxMs
= 0;
664 static int gGAIStress_RequestCountMax
= 0;
666 static CLIOption kGetAddrInfoStressOpts
[] =
670 CLI_OPTION_GROUP( "Flags" ),
671 DNSSDFlagsOption_ReturnIntermediates(),
672 DNSSDFlagsOption_SuppressUnusable(),
674 CLI_OPTION_GROUP( "Operation" ),
675 IntegerOption( 0, "testDuration", &gGAIStress_TestDurationSecs
, "seconds", "Stress test duration in seconds. Use '0' for forever.", false ),
676 IntegerOption( 0, "connectionCount", &gGAIStress_ConnectionCount
, "integer", "Number of simultaneous DNS-SD connections.", true ),
677 IntegerOption( 0, "requestDurationMin", &gGAIStress_DurationMinMs
, "ms", "Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
678 IntegerOption( 0, "requestDurationMax", &gGAIStress_DurationMaxMs
, "ms", "Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
679 IntegerOption( 0, "consecutiveRequestMax", &gGAIStress_RequestCountMax
, "integer", "Maximum number of requests on a connection before restarting it.", true ),
683 //===========================================================================================================================
684 // DNSQuery Command Options
685 //===========================================================================================================================
687 static char * gDNSQuery_Name
= NULL
;
688 static char * gDNSQuery_Type
= "A";
689 static char * gDNSQuery_Server
= NULL
;
690 static int gDNSQuery_TimeLimitSecs
= 5;
691 static int gDNSQuery_UseTCP
= false;
692 static int gDNSQuery_Flags
= kDNSHeaderFlag_RecursionDesired
;
693 static int gDNSQuery_RawRData
= false;
694 static int gDNSQuery_Verbose
= false;
696 #if( TARGET_OS_DARWIN )
697 #define kDNSQueryServerOptionIsRequired false
699 #define kDNSQueryServerOptionIsRequired true
702 static CLIOption kDNSQueryOpts
[] =
704 StringOption( 'n', "name", &gDNSQuery_Name
, "name", "Question name (QNAME) to put in DNS query message.", true ),
705 StringOption( 't', "type", &gDNSQuery_Type
, "type", "Question type (QTYPE) to put in DNS query message. Default value is 'A'.", false ),
706 StringOption( 's', "server", &gDNSQuery_Server
, "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired
),
707 IntegerOption( 'l', "timeLimit", &gDNSQuery_TimeLimitSecs
, "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ),
708 BooleanOption( 0 , "tcp", &gDNSQuery_UseTCP
, "Send the DNS query via TCP instead of UDP." ),
709 IntegerOption( 'f', "flags", &gDNSQuery_Flags
, "flags", "16-bit value for DNS header flags/codes field. Default value is 0x0100 (Recursion Desired).", false ),
710 BooleanOption( 0 , "raw", &gDNSQuery_RawRData
, "Present record data as a hexdump." ),
711 BooleanOption( 'v', "verbose", &gDNSQuery_Verbose
, "Prints the DNS message to be sent to the server." ),
715 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
716 //===========================================================================================================================
717 // DNSCrypt Command Options
718 //===========================================================================================================================
720 static char * gDNSCrypt_ProviderName
= NULL
;
721 static char * gDNSCrypt_ProviderKey
= NULL
;
722 static char * gDNSCrypt_Name
= NULL
;
723 static char * gDNSCrypt_Type
= NULL
;
724 static char * gDNSCrypt_Server
= NULL
;
725 static int gDNSCrypt_TimeLimitSecs
= 5;
726 static int gDNSCrypt_RawRData
= false;
727 static int gDNSCrypt_Verbose
= false;
729 static CLIOption kDNSCryptOpts
[] =
731 StringOption( 'p', "providerName", &gDNSCrypt_ProviderName
, "name", "The DNSCrypt provider name.", true ),
732 StringOption( 'k', "providerKey", &gDNSCrypt_ProviderKey
, "hex string", "The DNSCrypt provider's public signing key.", true ),
733 StringOption( 'n', "name", &gDNSCrypt_Name
, "name", "Question name (QNAME) to put in DNS query message.", true ),
734 StringOption( 't', "type", &gDNSCrypt_Type
, "type", "Question type (QTYPE) to put in DNS query message.", true ),
735 StringOption( 's', "server", &gDNSCrypt_Server
, "IP address", "DNS server's IPv4 or IPv6 address.", true ),
736 IntegerOption( 'l', "timeLimit", &gDNSCrypt_TimeLimitSecs
, "seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ),
737 BooleanOption( 0 , "raw", &gDNSCrypt_RawRData
, "Present record data as a hexdump." ),
738 BooleanOption( 'v', "verbose", &gDNSCrypt_Verbose
, "Prints the DNS message to be sent to the server." ),
743 //===========================================================================================================================
744 // MDNSQuery Command Options
745 //===========================================================================================================================
747 static char * gMDNSQuery_Name
= NULL
;
748 static char * gMDNSQuery_Type
= NULL
;
749 static int gMDNSQuery_SourcePort
= 0;
750 static int gMDNSQuery_IsQU
= false;
751 static int gMDNSQuery_RawRData
= false;
752 static int gMDNSQuery_UseIPv4
= false;
753 static int gMDNSQuery_UseIPv6
= false;
754 static int gMDNSQuery_AllResponses
= false;
755 static int gMDNSQuery_ReceiveSecs
= 1;
757 static CLIOption kMDNSQueryOpts
[] =
759 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
760 StringOption( 'n', "name", &gMDNSQuery_Name
, "name", "Question name (QNAME) to put in mDNS message.", true ),
761 StringOption( 't', "type", &gMDNSQuery_Type
, "type", "Question type (QTYPE) to put in mDNS message.", true ),
762 IntegerOption( 'p', "sourcePort", &gMDNSQuery_SourcePort
, "port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ),
763 BooleanOption( 'u', "QU", &gMDNSQuery_IsQU
, "Set the unicast-response bit, i.e., send a QU question." ),
764 BooleanOption( 0 , "raw", &gMDNSQuery_RawRData
, "Present record data as a hexdump." ),
765 BooleanOption( 0 , "ipv4", &gMDNSQuery_UseIPv4
, "Use IPv4." ),
766 BooleanOption( 0 , "ipv6", &gMDNSQuery_UseIPv6
, "Use IPv6." ),
767 BooleanOption( 'a', "allResponses", &gMDNSQuery_AllResponses
, "Print all received mDNS messages, not just those containing answers." ),
768 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 ),
772 //===========================================================================================================================
773 // PIDToUUID Command Options
774 //===========================================================================================================================
776 static int gPIDToUUID_PID
= 0;
778 static CLIOption kPIDToUUIDOpts
[] =
780 IntegerOption( 'p', "pid", &gPIDToUUID_PID
, "PID", "Process ID.", true ),
784 //===========================================================================================================================
785 // SSDP Command Options
786 //===========================================================================================================================
788 static int gSSDPDiscover_MX
= 1;
789 static const char * gSSDPDiscover_ST
= "ssdp:all";
790 static int gSSDPDiscover_ReceiveSecs
= 1;
791 static int gSSDPDiscover_UseIPv4
= false;
792 static int gSSDPDiscover_UseIPv6
= false;
793 static int gSSDPDiscover_Verbose
= false;
795 static CLIOption kSSDPDiscoverOpts
[] =
797 StringOption( 'i', "interface", &gInterface
, "name or index", "Network interface by name or index.", true ),
798 IntegerOption( 'm', "mx", &gSSDPDiscover_MX
, "seconds", "MX value in search request, i.e., max response delay in seconds. (Default: 1 second)", false ),
799 StringOption( 's', "st", &gSSDPDiscover_ST
, "string", "ST value in search request, i.e., the search target. (Default: \"ssdp:all\")", false ),
800 IntegerOption( 'r', "receiveTime", &gSSDPDiscover_ReceiveSecs
, "seconds", "Amount of time to spend receiving responses. -1 means unlimited. (Default: 1 second)", false ),
801 BooleanOption( 0 , "ipv4", &gSSDPDiscover_UseIPv4
, "Use IPv4, i.e., multicast to 239.255.255.250:1900." ),
802 BooleanOption( 0 , "ipv6", &gSSDPDiscover_UseIPv6
, "Use IPv6, i.e., multicast to [ff02::c]:1900" ),
803 BooleanOption( 'v', "verbose", &gSSDPDiscover_Verbose
, "Prints the search request(s) that were sent." ),
807 static void SSDPDiscoverCmd( void );
809 static CLIOption kSSDPOpts
[] =
811 Command( "discover", SSDPDiscoverCmd
, kSSDPDiscoverOpts
, "Crafts and multicasts an SSDP search message.", false ),
815 //===========================================================================================================================
816 // res_query Command Options
817 //===========================================================================================================================
819 static const char * gResQuery_Name
= NULL
;
820 static const char * gResQuery_Type
= NULL
;
821 static const char * gResQuery_Class
= NULL
;
822 static int gResQuery_UseLibInfo
= false;
824 static CLIOption kResQueryOpts
[] =
826 StringOption( 'n', "name", &gResQuery_Name
, "domain name", "Full domain name of record to query.", true ),
827 StringOption( 't', "type", &gResQuery_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
828 StringOption( 'c', "class", &gResQuery_Class
, "record class", "Record class by name or number. Default class is IN.", false ),
829 BooleanOption( 0 , "libinfo", &gResQuery_UseLibInfo
, "Use res_query from libinfo instead of libresolv." ),
833 //===========================================================================================================================
834 // dns_query Command Options
835 //===========================================================================================================================
837 static const char * gResolvDNSQuery_Name
= NULL
;
838 static const char * gResolvDNSQuery_Type
= NULL
;
839 static const char * gResolvDNSQuery_Class
= NULL
;
840 static const char * gResolvDNSQuery_Path
= NULL
;
842 static CLIOption kResolvDNSQueryOpts
[] =
844 StringOption( 'n', "name", &gResolvDNSQuery_Name
, "domain name", "Full domain name of record to query.", true ),
845 StringOption( 't', "type", &gResolvDNSQuery_Type
, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
846 StringOption( 'c', "class", &gResolvDNSQuery_Class
, "record class", "Record class by name or number. Default class is IN.", false ),
847 StringOption( 'p', "path", &gResolvDNSQuery_Path
, "file path", "The path argument to pass to dns_open() before calling dns_query(). Default value is NULL.", false ),
851 //===========================================================================================================================
853 //===========================================================================================================================
855 static OSStatus
VersionOptionCallback( CLIOption
*inOption
, const char *inArg
, int inUnset
);
857 static void BrowseCmd( void );
858 static void GetAddrInfoCmd( void );
859 static void QueryRecordCmd( void );
860 static void RegisterCmd( void );
861 static void RegisterRecordCmd( void );
862 static void ResolveCmd( void );
863 static void ReconfirmCmd( void );
864 static void GetAddrInfoPOSIXCmd( void );
865 static void ReverseLookupCmd( void );
866 static void PortMappingCmd( void );
867 static void BrowseAllCmd( void );
868 static void GetAddrInfoStressCmd( void );
869 static void DNSQueryCmd( void );
870 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
871 static void DNSCryptCmd( void );
873 static void MDNSQueryCmd( void );
874 static void PIDToUUIDCmd( void );
875 #if( TARGET_OS_DARWIN )
876 static void ResQueryCmd( void );
877 static void ResolvDNSQueryCmd( void );
879 static void DaemonVersionCmd( void );
881 static CLIOption kGlobalOpts
[] =
883 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback
, NULL
, NULL
,
884 kCLIOptionFlags_NoArgument
| kCLIOptionFlags_GlobalOnly
, "Displays the version of this tool.", NULL
),
889 Command( "browse", BrowseCmd
, kBrowseOpts
, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
890 Command( "getAddrInfo", GetAddrInfoCmd
, kGetAddrInfoOpts
, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
891 Command( "queryRecord", QueryRecordCmd
, kQueryRecordOpts
, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
892 Command( "register", RegisterCmd
, kRegisterOpts
, "Uses DNSServiceRegister() to register a service.", false ),
893 Command( "registerRecord", RegisterRecordCmd
, kRegisterRecordOpts
, "Uses DNSServiceRegisterRecord() to register a record.", false ),
894 Command( "resolve", ResolveCmd
, kResolveOpts
, "Uses DNSServiceResolve() to resolve a service.", false ),
895 Command( "reconfirm", ReconfirmCmd
, kReconfirmOpts
, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
896 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd
, kGetAddrInfoPOSIXOpts
, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
897 Command( "reverseLookup", ReverseLookupCmd
, kReverseLookupOpts
, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
898 Command( "portMapping", PortMappingCmd
, kPortMappingOpts
, "Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ),
899 Command( "browseAll", BrowseAllCmd
, kBrowseAllOpts
, "Browse and resolve all (or specific) services and, optionally, attempt connections.", false ),
901 // Uncommon commands.
903 Command( "getAddrInfoStress", GetAddrInfoStressCmd
, kGetAddrInfoStressOpts
, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
904 Command( "DNSQuery", DNSQueryCmd
, kDNSQueryOpts
, "Crafts and sends a DNS query.", true ),
905 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
906 Command( "DNSCrypt", DNSCryptCmd
, kDNSCryptOpts
, "Crafts and sends a DNSCrypt query.", true ),
908 Command( "mDNSQuery", MDNSQueryCmd
, kMDNSQueryOpts
, "Crafts and sends an mDNS query over the specified interface.", true ),
909 Command( "pid2uuid", PIDToUUIDCmd
, kPIDToUUIDOpts
, "Prints the UUID of a process.", true ),
910 Command( "ssdp", NULL
, kSSDPOpts
, "Commands for testing with Simple Service Discovery Protocol (SSDP).", true ),
911 #if( TARGET_OS_DARWIN )
912 Command( "res_query", ResQueryCmd
, kResQueryOpts
, "Uses res_query() from either libresolv or libinfo to query for a record.", true ),
913 Command( "dns_query", ResolvDNSQueryCmd
, kResolvDNSQueryOpts
, "Uses dns_query() from libresolv to query for a record.", true ),
915 Command( "daemonVersion", DaemonVersionCmd
, NULL
, "Prints the version of the DNS-SD daemon.", true ),
921 //===========================================================================================================================
923 //===========================================================================================================================
925 #define kExitReason_OneShotDone "one-shot done"
926 #define kExitReason_ReceivedResponse "received response"
927 #define kExitReason_SIGINT "interrupt signal"
928 #define kExitReason_Timeout "timeout"
929 #define kExitReason_TimeLimit "time limit"
931 static void Exit( void *inContext
) ATTRIBUTE_NORETURN
;
933 #define kTimestampBufLen 27
935 static char * GetTimestampStr( char inBuffer
[ kTimestampBufLen
] );
936 static DNSServiceFlags
GetDNSSDFlagsFromOpts( void );
940 kConnectionType_None
= 0,
941 kConnectionType_Normal
= 1,
942 kConnectionType_DelegatePID
= 2,
943 kConnectionType_DelegateUUID
= 3
960 CreateConnectionFromArgString(
961 const char * inString
,
962 dispatch_queue_t inQueue
,
963 DNSServiceRef
* outSDRef
,
964 ConnectionDesc
* outDesc
);
965 static OSStatus
InterfaceIndexFromArgString( const char *inString
, uint32_t *outIndex
);
966 static OSStatus
RecordDataFromArgString( const char *inString
, uint8_t **outDataPtr
, size_t *outDataLen
);
967 static OSStatus
RecordTypeFromArgString( const char *inString
, uint16_t *outValue
);
968 static OSStatus
RecordClassFromArgString( const char *inString
, uint16_t *outValue
);
970 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
972 static char * InterfaceIndexToName( uint32_t inIfIndex
, char inNameBuf
[ kInterfaceNameBufLen
] );
973 static const char * RecordTypeToString( unsigned int inValue
);
975 // DNS message helpers
981 uint8_t questionCount
[ 2 ];
982 uint8_t answerCount
[ 2 ];
983 uint8_t authorityCount
[ 2 ];
984 uint8_t additionalCount
[ 2 ];
988 #define kDNSHeaderLength 12
989 check_compile_time( sizeof( DNSHeader
) == kDNSHeaderLength
);
991 #define DNSHeaderGetID( HDR ) ReadBig16( ( HDR )->id )
992 #define DNSHeaderGetFlags( HDR ) ReadBig16( ( HDR )->flags )
993 #define DNSHeaderGetQuestionCount( HDR ) ReadBig16( ( HDR )->questionCount )
994 #define DNSHeaderGetAnswerCount( HDR ) ReadBig16( ( HDR )->answerCount )
995 #define DNSHeaderGetAuthorityCount( HDR ) ReadBig16( ( HDR )->authorityCount )
996 #define DNSHeaderGetAdditionalCount( HDR ) ReadBig16( ( HDR )->additionalCount )
999 DNSMessageExtractDomainName(
1000 const uint8_t * inMsgPtr
,
1002 const uint8_t * inNamePtr
,
1003 uint8_t inBuf
[ kDomainNameLengthMax
],
1004 const uint8_t ** outNextPtr
);
1006 DNSMessageExtractDomainNameString(
1007 const void * inMsgPtr
,
1009 const void * inNamePtr
,
1010 char inBuf
[ kDNSServiceMaxDomainName
],
1011 const uint8_t ** outNextPtr
);
1013 DNSMessageExtractRecord(
1014 const uint8_t * inMsgPtr
,
1016 const uint8_t * inPtr
,
1017 uint8_t inNameBuf
[ kDomainNameLengthMax
],
1019 uint16_t * outClass
,
1021 const uint8_t ** outRDataPtr
,
1022 size_t * outRDataLen
,
1023 const uint8_t ** outPtr
);
1024 static OSStatus
DNSMessageGetAnswerSection( const uint8_t *inMsgPtr
, size_t inMsgLen
, const uint8_t **outPtr
);
1026 DNSRecordDataToString(
1027 const void * inRDataPtr
,
1029 unsigned int inRDataType
,
1030 const void * inMsgPtr
,
1032 char ** outString
);
1034 DomainNameAppendString(
1035 uint8_t inDomainName
[ kDomainNameLengthMax
],
1036 const char * inString
,
1037 uint8_t ** outEndPtr
);
1038 static Boolean
DomainNameEqual( const uint8_t *inName1
, const uint8_t *inName2
);
1040 DomainNameFromString(
1041 uint8_t inDomainName
[ kDomainNameLengthMax
],
1042 const char * inString
,
1043 uint8_t ** outEndPtr
);
1046 const uint8_t * inDomainName
,
1047 const uint8_t * inEnd
,
1048 char inBuf
[ kDNSServiceMaxDomainName
],
1049 const uint8_t ** outNextPtr
);
1051 static OSStatus
PrintDNSMessage( const uint8_t *inMsgPtr
, size_t inMsgLen
, Boolean inIsMDNS
, Boolean inPrintRaw
);
1053 #define PrintMDNSMessage( MSGPTR, MSGLEN, RAW ) PrintDNSMessage( MSGPTR, MSGLEN, true, RAW )
1054 #define PrintUDNSMessage( MSGPTR, MSGLEN, RAW ) PrintDNSMessage( MSGPTR, MSGLEN, false, RAW )
1056 #define kDNSQueryMessageMaxLen ( kDNSHeaderLength + kDomainNameLengthMax + 4 )
1059 WriteDNSQueryMessage(
1060 uint8_t inMsg
[ kDNSQueryMessageMaxLen
],
1063 const char * inQName
,
1066 size_t * outMsgLen
);
1070 typedef void ( *DispatchHandler
)( void *inContext
);
1073 DispatchSignalSourceCreate(
1075 DispatchHandler inEventHandler
,
1077 dispatch_source_t
* outSource
);
1079 DispatchReadSourceCreate(
1081 DispatchHandler inEventHandler
,
1082 DispatchHandler inCancelHandler
,
1084 dispatch_source_t
* outSource
);
1086 DispatchTimerCreate(
1087 dispatch_time_t inStart
,
1088 uint64_t inIntervalNs
,
1089 uint64_t inLeewayNs
,
1090 DispatchHandler inEventHandler
,
1091 DispatchHandler inCancelHandler
,
1093 dispatch_source_t
* outTimer
);
1095 static const char * ServiceTypeDescription( const char *inName
);
1104 static void SocketContextCancelHandler( void *inContext
);
1105 static OSStatus
StringToInt32( const char *inString
, int32_t *outValue
);
1106 static OSStatus
StringToUInt32( const char *inString
, uint32_t *outValue
);
1107 #if( TARGET_OS_DARWIN )
1108 static OSStatus
GetDefaultDNSServer( sockaddr_ip
*outAddr
);
1111 #define AddRmvString( X ) ( ( (X) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" )
1112 #define Unused( X ) (void)(X)
1114 //===========================================================================================================================
1116 //===========================================================================================================================
1118 int main( int argc
, const char **argv
)
1120 // Route DebugServices logging output to stderr.
1122 dlog_control( "DebugServices:output=file;stderr" );
1124 CLIInit( argc
, argv
);
1125 CLIParse( kGlobalOpts
, kCLIFlags_None
);
1127 return( gExitCode
);
1130 //===========================================================================================================================
1131 // VersionOptionCallback
1132 //===========================================================================================================================
1134 static OSStatus
VersionOptionCallback( CLIOption
*inOption
, const char *inArg
, int inUnset
)
1136 const char * srcVers
;
1137 #if( MDNSRESPONDER_PROJECT )
1145 #if( MDNSRESPONDER_PROJECT )
1146 srcVers
= SourceVersionToCString( _DNS_SD_H
, srcStr
);
1148 srcVers
= DNSSDUTIL_SOURCE_VERSION
;
1150 FPrintF( stdout
, "%s version %v (%s)\n", gProgramName
, kDNSSDUtilNumVersion
, srcVers
);
1152 return( kEndingErr
);
1155 //===========================================================================================================================
1157 //===========================================================================================================================
1159 typedef struct BrowseResolveOp BrowseResolveOp
;
1161 struct BrowseResolveOp
1163 BrowseResolveOp
* next
; // Next resolve operation in list.
1164 DNSServiceRef sdRef
; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
1165 char * fullName
; // Full name of the service to resolve.
1166 uint32_t interfaceIndex
; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
1171 DNSServiceRef mainRef
; // Main sdRef for shared connection.
1172 DNSServiceRef
* opRefs
; // Array of sdRefs for individual Browse operarions.
1173 size_t opRefsCount
; // Count of array of sdRefs for non-shared connections.
1174 const char * domain
; // Domain for DNSServiceBrowse operation(s).
1175 DNSServiceFlags flags
; // Flags for DNSServiceBrowse operation(s).
1176 char ** serviceTypes
; // Array of service types to browse for.
1177 size_t serviceTypesCount
; // Count of array of service types to browse for.
1178 int timeLimitSecs
; // Time limit of DNSServiceBrowse operation in seconds.
1179 BrowseResolveOp
* resolveList
; // List of resolve and/or TXT record query operations.
1180 uint32_t ifIndex
; // Interface index of DNSServiceBrowse operation(s).
1181 Boolean printedHeader
; // True if results header has been printed.
1182 Boolean doResolve
; // True if service instances are to be resolved.
1183 Boolean doResolveTXTOnly
; // True if TXT records of service instances are to be queried.
1187 static void BrowsePrintPrologue( const BrowseContext
*inContext
);
1188 static void BrowseContextFree( BrowseContext
*inContext
);
1189 static OSStatus
BrowseResolveOpCreate( const char *inFullName
, uint32_t inInterfaceIndex
, BrowseResolveOp
**outOp
);
1190 static void BrowseResolveOpFree( BrowseResolveOp
*inOp
);
1191 static void DNSSD_API
1193 DNSServiceRef inSDRef
,
1194 DNSServiceFlags inFlags
,
1195 uint32_t inInterfaceIndex
,
1196 DNSServiceErrorType inError
,
1197 const char * inName
,
1198 const char * inRegType
,
1199 const char * inDomain
,
1201 static void DNSSD_API
1202 BrowseResolveCallback(
1203 DNSServiceRef inSDRef
,
1204 DNSServiceFlags inFlags
,
1205 uint32_t inInterfaceIndex
,
1206 DNSServiceErrorType inError
,
1207 const char * inFullName
,
1208 const char * inHostname
,
1211 const unsigned char * inTXTPtr
,
1213 static void DNSSD_API
1214 BrowseQueryRecordCallback(
1215 DNSServiceRef inSDRef
,
1216 DNSServiceFlags inFlags
,
1217 uint32_t inInterfaceIndex
,
1218 DNSServiceErrorType inError
,
1219 const char * inFullName
,
1222 uint16_t inRDataLen
,
1223 const void * inRDataPtr
,
1227 static void BrowseCmd( void )
1231 BrowseContext
* context
= NULL
;
1232 dispatch_source_t signalSource
= NULL
;
1233 int useMainConnection
;
1235 // Set up SIGINT handler.
1237 signal( SIGINT
, SIG_IGN
);
1238 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
1239 require_noerr( err
, exit
);
1240 dispatch_resume( signalSource
);
1244 context
= (BrowseContext
*) calloc( 1, sizeof( *context
) );
1245 require_action( context
, exit
, err
= kNoMemoryErr
);
1247 context
->opRefs
= (DNSServiceRef
*) calloc( gBrowse_ServiceTypesCount
, sizeof( DNSServiceRef
) );
1248 require_action( context
->opRefs
, exit
, err
= kNoMemoryErr
);
1249 context
->opRefsCount
= gBrowse_ServiceTypesCount
;
1251 // Check command parameters.
1253 if( gBrowse_TimeLimitSecs
< 0 )
1255 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs
);
1260 // Create main connection.
1262 if( gConnectionOpt
)
1264 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
1265 require_noerr_quiet( err
, exit
);
1266 useMainConnection
= true;
1270 useMainConnection
= false;
1275 context
->flags
= GetDNSSDFlagsFromOpts();
1276 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
1280 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
1281 require_noerr_quiet( err
, exit
);
1283 // Set remaining parameters.
1285 context
->serviceTypes
= gBrowse_ServiceTypes
;
1286 context
->serviceTypesCount
= gBrowse_ServiceTypesCount
;
1287 context
->domain
= gBrowse_Domain
;
1288 context
->doResolve
= gBrowse_DoResolve
? true : false;
1289 context
->timeLimitSecs
= gBrowse_TimeLimitSecs
;
1290 context
->doResolveTXTOnly
= gBrowse_QueryTXT
? true : false;
1294 BrowsePrintPrologue( context
);
1296 // Start operation(s).
1298 for( i
= 0; i
< context
->serviceTypesCount
; ++i
)
1300 DNSServiceRef sdRef
;
1302 if( useMainConnection
) sdRef
= context
->mainRef
;
1303 err
= DNSServiceBrowse( &sdRef
, context
->flags
, context
->ifIndex
, context
->serviceTypes
[ i
], context
->domain
,
1304 BrowseCallback
, context
);
1305 require_noerr( err
, exit
);
1307 context
->opRefs
[ i
] = sdRef
;
1308 if( !useMainConnection
)
1310 err
= DNSServiceSetDispatchQueue( context
->opRefs
[ i
], dispatch_get_main_queue() );
1311 require_noerr( err
, exit
);
1317 if( context
->timeLimitSecs
> 0 )
1319 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
1320 kExitReason_TimeLimit
, Exit
);
1325 dispatch_source_forget( &signalSource
);
1326 if( context
) BrowseContextFree( context
);
1327 if( err
) exit( 1 );
1330 //===========================================================================================================================
1331 // BrowsePrintPrologue
1332 //===========================================================================================================================
1334 static void BrowsePrintPrologue( const BrowseContext
*inContext
)
1336 const int timeLimitSecs
= inContext
->timeLimitSecs
;
1337 const char * const * serviceType
= (const char **) inContext
->serviceTypes
;
1338 const char * const * const end
= (const char **) inContext
->serviceTypes
+ inContext
->serviceTypesCount
;
1339 char time
[ kTimestampBufLen
];
1340 char ifName
[ kInterfaceNameBufLen
];
1342 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
1344 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
1345 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
1346 FPrintF( stdout
, "Service types: %s", *serviceType
++ );
1347 while( serviceType
< end
) FPrintF( stdout
, ", %s", *serviceType
++ );
1348 FPrintF( stdout
, "\n" );
1349 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "<NULL> (default domains)" );
1350 FPrintF( stdout
, "Time limit: " );
1351 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
1352 else FPrintF( stdout
, "∞\n" );
1353 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
1354 FPrintF( stdout
, "---\n" );
1357 //===========================================================================================================================
1358 // BrowseContextFree
1359 //===========================================================================================================================
1361 static void BrowseContextFree( BrowseContext
*inContext
)
1365 for( i
= 0; i
< inContext
->opRefsCount
; ++i
)
1367 DNSServiceForget( &inContext
->opRefs
[ i
] );
1369 if( inContext
->serviceTypes
)
1371 StringArray_Free( inContext
->serviceTypes
, inContext
->serviceTypesCount
);
1372 inContext
->serviceTypes
= NULL
;
1373 inContext
->serviceTypesCount
= 0;
1375 DNSServiceForget( &inContext
->mainRef
);
1379 //===========================================================================================================================
1380 // BrowseResolveOpCreate
1381 //===========================================================================================================================
1383 static OSStatus
BrowseResolveOpCreate( const char *inFullName
, uint32_t inInterfaceIndex
, BrowseResolveOp
**outOp
)
1386 BrowseResolveOp
* resolveOp
;
1388 resolveOp
= (BrowseResolveOp
*) calloc( 1, sizeof( *resolveOp
) );
1389 require_action( resolveOp
, exit
, err
= kNoMemoryErr
);
1391 resolveOp
->fullName
= strdup( inFullName
);
1392 require_action( resolveOp
->fullName
, exit
, err
= kNoMemoryErr
);
1394 resolveOp
->interfaceIndex
= inInterfaceIndex
;
1401 if( resolveOp
) BrowseResolveOpFree( resolveOp
);
1405 //===========================================================================================================================
1406 // BrowseResolveOpFree
1407 //===========================================================================================================================
1409 static void BrowseResolveOpFree( BrowseResolveOp
*inOp
)
1411 DNSServiceForget( &inOp
->sdRef
);
1412 ForgetMem( &inOp
->fullName
);
1416 //===========================================================================================================================
1418 //===========================================================================================================================
1420 static void DNSSD_API
1422 DNSServiceRef inSDRef
,
1423 DNSServiceFlags inFlags
,
1424 uint32_t inInterfaceIndex
,
1425 DNSServiceErrorType inError
,
1426 const char * inName
,
1427 const char * inRegType
,
1428 const char * inDomain
,
1431 BrowseContext
* const context
= (BrowseContext
*) inContext
;
1433 BrowseResolveOp
* newOp
= NULL
;
1434 BrowseResolveOp
** p
;
1435 char fullName
[ kDNSServiceMaxDomainName
];
1436 char time
[ kTimestampBufLen
];
1440 GetTimestampStr( time
);
1443 require_noerr( err
, exit
);
1445 if( !context
->printedHeader
)
1447 FPrintF( stdout
, "%-26s A/R Flags IF %-20s %-20s Instance Name\n", "Timestamp", "Domain", "Service Type" );
1448 context
->printedHeader
= true;
1450 FPrintF( stdout
, "%-26s %-3s %5X %2d %-20s %-20s %s\n",
1451 time
, AddRmvString( inFlags
), inFlags
, (int32_t) inInterfaceIndex
, inDomain
, inRegType
, inName
);
1453 if( !context
->doResolve
&& !context
->doResolveTXTOnly
) goto exit
;
1455 err
= DNSServiceConstructFullName( fullName
, inName
, inRegType
, inDomain
);
1456 require_noerr( err
, exit
);
1458 if( inFlags
& kDNSServiceFlagsAdd
)
1460 DNSServiceRef sdRef
;
1461 DNSServiceFlags flags
;
1463 err
= BrowseResolveOpCreate( fullName
, inInterfaceIndex
, &newOp
);
1464 require_noerr( err
, exit
);
1466 if( context
->mainRef
)
1468 sdRef
= context
->mainRef
;
1469 flags
= kDNSServiceFlagsShareConnection
;
1475 if( context
->doResolve
)
1477 err
= DNSServiceResolve( &sdRef
, flags
, inInterfaceIndex
, inName
, inRegType
, inDomain
, BrowseResolveCallback
,
1479 require_noerr( err
, exit
);
1483 err
= DNSServiceQueryRecord( &sdRef
, flags
, inInterfaceIndex
, fullName
, kDNSServiceType_TXT
, kDNSServiceClass_IN
,
1484 BrowseQueryRecordCallback
, NULL
);
1485 require_noerr( err
, exit
);
1488 newOp
->sdRef
= sdRef
;
1489 if( !context
->mainRef
)
1491 err
= DNSServiceSetDispatchQueue( newOp
->sdRef
, dispatch_get_main_queue() );
1492 require_noerr( err
, exit
);
1494 for( p
= &context
->resolveList
; *p
; p
= &( *p
)->next
) {}
1500 BrowseResolveOp
* resolveOp
;
1502 for( p
= &context
->resolveList
; ( resolveOp
= *p
) != NULL
; p
= &resolveOp
->next
)
1504 if( ( resolveOp
->interfaceIndex
== inInterfaceIndex
) && ( strcasecmp( resolveOp
->fullName
, fullName
) == 0 ) )
1511 *p
= resolveOp
->next
;
1512 BrowseResolveOpFree( resolveOp
);
1517 if( newOp
) BrowseResolveOpFree( newOp
);
1518 if( err
) exit( 1 );
1521 //===========================================================================================================================
1522 // BrowseQueryRecordCallback
1523 //===========================================================================================================================
1525 static void DNSSD_API
1526 BrowseQueryRecordCallback(
1527 DNSServiceRef inSDRef
,
1528 DNSServiceFlags inFlags
,
1529 uint32_t inInterfaceIndex
,
1530 DNSServiceErrorType inError
,
1531 const char * inFullName
,
1534 uint16_t inRDataLen
,
1535 const void * inRDataPtr
,
1540 char time
[ kTimestampBufLen
];
1545 Unused( inContext
);
1547 GetTimestampStr( time
);
1550 require_noerr( err
, exit
);
1551 require_action( inType
== kDNSServiceType_TXT
, exit
, err
= kTypeErr
);
1553 FPrintF( stdout
, "%s %s %s TXT on interface %d\n TXT: %#{txt}\n",
1554 time
, AddRmvString( inFlags
), inFullName
, (int32_t) inInterfaceIndex
, inRDataPtr
, (size_t) inRDataLen
);
1557 if( err
) exit( 1 );
1560 //===========================================================================================================================
1561 // BrowseResolveCallback
1562 //===========================================================================================================================
1564 static void DNSSD_API
1565 BrowseResolveCallback(
1566 DNSServiceRef inSDRef
,
1567 DNSServiceFlags inFlags
,
1568 uint32_t inInterfaceIndex
,
1569 DNSServiceErrorType inError
,
1570 const char * inFullName
,
1571 const char * inHostname
,
1574 const unsigned char * inTXTPtr
,
1577 char time
[ kTimestampBufLen
];
1578 char errorStr
[ 64 ];
1582 Unused( inContext
);
1584 GetTimestampStr( time
);
1586 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " error %#m", inError
);
1588 FPrintF( stdout
, "%s %s can be reached at %s:%u (interface %d)%?s\n",
1589 time
, inFullName
, inHostname
, ntohs( inPort
), (int32_t) inInterfaceIndex
, inError
, errorStr
);
1592 FPrintF( stdout
, " TXT record: %#H\n", inTXTPtr
, (int) inTXTLen
, INT_MAX
);
1596 FPrintF( stdout
, " TXT record: %#{txt}\n", inTXTPtr
, (size_t) inTXTLen
);
1600 //===========================================================================================================================
1602 //===========================================================================================================================
1606 DNSServiceRef mainRef
; // Main sdRef for shared connection.
1607 DNSServiceRef opRef
; // sdRef for the DNSServiceGetAddrInfo operation.
1608 const char * name
; // Hostname to resolve.
1609 DNSServiceFlags flags
; // Flags argument for DNSServiceGetAddrInfo().
1610 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceGetAddrInfo().
1611 uint32_t ifIndex
; // Interface index argument for DNSServiceGetAddrInfo().
1612 int timeLimitSecs
; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
1613 Boolean printedHeader
; // True if the results header has been printed.
1614 Boolean oneShotMode
; // True if command is done after the first set of results (one-shot mode).
1615 Boolean needIPv4
; // True if in one-shot mode and an IPv4 result is needed.
1616 Boolean needIPv6
; // True if in one-shot mode and an IPv6 result is needed.
1618 } GetAddrInfoContext
;
1620 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext
*inContext
);
1621 static void GetAddrInfoContextFree( GetAddrInfoContext
*inContext
);
1622 static void DNSSD_API
1623 GetAddrInfoCallback(
1624 DNSServiceRef inSDRef
,
1625 DNSServiceFlags inFlags
,
1626 uint32_t inInterfaceIndex
,
1627 DNSServiceErrorType inError
,
1628 const char * inHostname
,
1629 const struct sockaddr
* inSockAddr
,
1633 static void GetAddrInfoCmd( void )
1636 DNSServiceRef sdRef
;
1637 GetAddrInfoContext
* context
= NULL
;
1638 dispatch_source_t signalSource
= NULL
;
1639 int useMainConnection
;
1641 // Set up SIGINT handler.
1643 signal( SIGINT
, SIG_IGN
);
1644 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
1645 require_noerr( err
, exit
);
1646 dispatch_resume( signalSource
);
1648 // Check command parameters.
1650 if( gGetAddrInfo_TimeLimitSecs
< 0 )
1652 FPrintF( stderr
, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs
);
1659 context
= (GetAddrInfoContext
*) calloc( 1, sizeof( *context
) );
1660 require_action( context
, exit
, err
= kNoMemoryErr
);
1662 // Create main connection.
1664 if( gConnectionOpt
)
1666 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
1667 require_noerr_quiet( err
, exit
);
1668 useMainConnection
= true;
1672 useMainConnection
= false;
1677 context
->flags
= GetDNSSDFlagsFromOpts();
1678 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
1682 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
1683 require_noerr_quiet( err
, exit
);
1685 // Set remaining parameters.
1687 context
->name
= gGetAddrInfo_Name
;
1688 context
->timeLimitSecs
= gGetAddrInfo_TimeLimitSecs
;
1689 if( gGetAddrInfo_ProtocolIPv4
) context
->protocols
|= kDNSServiceProtocol_IPv4
;
1690 if( gGetAddrInfo_ProtocolIPv6
) context
->protocols
|= kDNSServiceProtocol_IPv6
;
1691 if( gGetAddrInfo_OneShot
)
1693 context
->oneShotMode
= true;
1694 context
->needIPv4
= ( gGetAddrInfo_ProtocolIPv4
|| !gGetAddrInfo_ProtocolIPv6
) ? true : false;
1695 context
->needIPv6
= ( gGetAddrInfo_ProtocolIPv6
|| !gGetAddrInfo_ProtocolIPv4
) ? true : false;
1700 GetAddrInfoPrintPrologue( context
);
1704 if( useMainConnection
) sdRef
= context
->mainRef
;
1705 err
= DNSServiceGetAddrInfo( &sdRef
, context
->flags
, context
->ifIndex
, context
->protocols
, context
->name
,
1706 GetAddrInfoCallback
, context
);
1707 require_noerr( err
, exit
);
1709 context
->opRef
= sdRef
;
1710 if( !useMainConnection
)
1712 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
1713 require_noerr( err
, exit
);
1718 if( context
->timeLimitSecs
> 0 )
1720 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
1721 kExitReason_TimeLimit
, Exit
);
1726 dispatch_source_forget( &signalSource
);
1727 if( context
) GetAddrInfoContextFree( context
);
1728 if( err
) exit( 1 );
1731 //===========================================================================================================================
1732 // GetAddrInfoPrintPrologue
1733 //===========================================================================================================================
1735 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext
*inContext
)
1737 const int timeLimitSecs
= inContext
->timeLimitSecs
;
1738 char ifName
[ kInterfaceNameBufLen
];
1739 char time
[ kTimestampBufLen
];
1741 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
1743 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
1744 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
1745 FPrintF( stdout
, "Protocols: %#{flags}\n", inContext
->protocols
, kDNSServiceProtocolDescriptors
);
1746 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
1747 FPrintF( stdout
, "Mode: %s\n", inContext
->oneShotMode
? "one-shot" : "continuous" );
1748 FPrintF( stdout
, "Time limit: " );
1749 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
1750 else FPrintF( stdout
, "∞\n" );
1751 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
1752 FPrintF( stdout
, "---\n" );
1755 //===========================================================================================================================
1756 // GetAddrInfoContextFree
1757 //===========================================================================================================================
1759 static void GetAddrInfoContextFree( GetAddrInfoContext
*inContext
)
1761 DNSServiceForget( &inContext
->opRef
);
1762 DNSServiceForget( &inContext
->mainRef
);
1766 //===========================================================================================================================
1767 // GetAddrInfoCallback
1768 //===========================================================================================================================
1770 static void DNSSD_API
1771 GetAddrInfoCallback(
1772 DNSServiceRef inSDRef
,
1773 DNSServiceFlags inFlags
,
1774 uint32_t inInterfaceIndex
,
1775 DNSServiceErrorType inError
,
1776 const char * inHostname
,
1777 const struct sockaddr
* inSockAddr
,
1781 GetAddrInfoContext
* const context
= (GetAddrInfoContext
*) inContext
;
1783 const char * addrStr
;
1784 char addrStrBuf
[ kSockAddrStringMaxSize
];
1785 char time
[ kTimestampBufLen
];
1789 GetTimestampStr( time
);
1793 case kDNSServiceErr_NoError
:
1794 case kDNSServiceErr_NoSuchRecord
:
1798 case kDNSServiceErr_Timeout
:
1799 Exit( kExitReason_Timeout
);
1806 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
1808 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
1815 err
= SockAddrToString( inSockAddr
, kSockAddrStringFlagsNone
, addrStrBuf
);
1816 require_noerr( err
, exit
);
1817 addrStr
= addrStrBuf
;
1821 addrStr
= ( inSockAddr
->sa_family
== AF_INET
) ? "No Such Record (A)" : "No Such Record (AAAA)";
1824 if( !context
->printedHeader
)
1826 FPrintF( stdout
, "%-26s A/R Flags IF %-32s %-38s %6s\n", "Timestamp", "Hostname", "Address", "TTL" );
1827 context
->printedHeader
= true;
1829 FPrintF( stdout
, "%-26s %s %5X %2d %-32s %-38s %6u\n",
1830 time
, AddRmvString( inFlags
), inFlags
, (int32_t) inInterfaceIndex
, inHostname
, addrStr
, inTTL
);
1832 if( context
->oneShotMode
)
1834 if( inFlags
& kDNSServiceFlagsAdd
)
1836 if( inSockAddr
->sa_family
== AF_INET
) context
->needIPv4
= false;
1837 else context
->needIPv6
= false;
1839 if( !( inFlags
& kDNSServiceFlagsMoreComing
) && !context
->needIPv4
&& !context
->needIPv6
)
1841 Exit( kExitReason_OneShotDone
);
1846 if( err
) exit( 1 );
1849 //===========================================================================================================================
1851 //===========================================================================================================================
1855 DNSServiceRef mainRef
; // Main sdRef for shared connection.
1856 DNSServiceRef opRef
; // sdRef for the DNSServiceQueryRecord operation.
1857 const char * recordName
; // Resource record name argument for DNSServiceQueryRecord().
1858 DNSServiceFlags flags
; // Flags argument for DNSServiceQueryRecord().
1859 uint32_t ifIndex
; // Interface index argument for DNSServiceQueryRecord().
1860 int timeLimitSecs
; // Time limit for the DNSServiceQueryRecord() operation in seconds.
1861 uint16_t recordType
; // Resource record type argument for DNSServiceQueryRecord().
1862 Boolean printedHeader
; // True if the results header was printed.
1863 Boolean oneShotMode
; // True if command is done after the first set of results (one-shot mode).
1864 Boolean gotRecord
; // True if in one-shot mode and received at least one record of the desired type.
1865 Boolean printRawRData
; // True if RDATA results are not to be formatted when printed.
1867 } QueryRecordContext
;
1869 static void QueryRecordPrintPrologue( const QueryRecordContext
*inContext
);
1870 static void QueryRecordContextFree( QueryRecordContext
*inContext
);
1871 static void DNSSD_API
1872 QueryRecordCallback(
1873 DNSServiceRef inSDRef
,
1874 DNSServiceFlags inFlags
,
1875 uint32_t inInterfaceIndex
,
1876 DNSServiceErrorType inError
,
1877 const char * inFullName
,
1880 uint16_t inRDataLen
,
1881 const void * inRDataPtr
,
1885 static void QueryRecordCmd( void )
1888 DNSServiceRef sdRef
;
1889 QueryRecordContext
* context
= NULL
;
1890 dispatch_source_t signalSource
= NULL
;
1891 int useMainConnection
;
1893 // Set up SIGINT handler.
1895 signal( SIGINT
, SIG_IGN
);
1896 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
1897 require_noerr( err
, exit
);
1898 dispatch_resume( signalSource
);
1902 context
= (QueryRecordContext
*) calloc( 1, sizeof( *context
) );
1903 require_action( context
, exit
, err
= kNoMemoryErr
);
1905 // Check command parameters.
1907 if( gQueryRecord_TimeLimitSecs
< 0 )
1909 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs
);
1914 // Create main connection.
1916 if( gConnectionOpt
)
1918 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
1919 require_noerr_quiet( err
, exit
);
1920 useMainConnection
= true;
1924 useMainConnection
= false;
1929 context
->flags
= GetDNSSDFlagsFromOpts();
1930 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
1934 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
1935 require_noerr_quiet( err
, exit
);
1939 err
= RecordTypeFromArgString( gQueryRecord_Type
, &context
->recordType
);
1940 require_noerr( err
, exit
);
1942 // Set remaining parameters.
1944 context
->recordName
= gQueryRecord_Name
;
1945 context
->timeLimitSecs
= gQueryRecord_TimeLimitSecs
;
1946 context
->oneShotMode
= gQueryRecord_OneShot
? true : false;
1947 context
->printRawRData
= gQueryRecord_RawRData
? true : false;
1951 QueryRecordPrintPrologue( context
);
1955 if( useMainConnection
) sdRef
= context
->mainRef
;
1956 err
= DNSServiceQueryRecord( &sdRef
, context
->flags
, context
->ifIndex
, context
->recordName
, context
->recordType
,
1957 kDNSServiceClass_IN
, QueryRecordCallback
, context
);
1958 require_noerr( err
, exit
);
1960 context
->opRef
= sdRef
;
1961 if( !useMainConnection
)
1963 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
1964 require_noerr( err
, exit
);
1969 if( context
->timeLimitSecs
> 0 )
1971 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_TimeLimit
,
1977 dispatch_source_forget( &signalSource
);
1978 if( context
) QueryRecordContextFree( context
);
1979 if( err
) exit( 1 );
1982 //===========================================================================================================================
1983 // QueryRecordContextFree
1984 //===========================================================================================================================
1986 static void QueryRecordContextFree( QueryRecordContext
*inContext
)
1988 DNSServiceForget( &inContext
->opRef
);
1989 DNSServiceForget( &inContext
->mainRef
);
1993 //===========================================================================================================================
1994 // QueryRecordPrintPrologue
1995 //===========================================================================================================================
1997 static void QueryRecordPrintPrologue( const QueryRecordContext
*inContext
)
1999 const int timeLimitSecs
= inContext
->timeLimitSecs
;
2000 char ifName
[ kInterfaceNameBufLen
];
2001 char time
[ kTimestampBufLen
];
2003 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
2005 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
2006 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
2007 FPrintF( stdout
, "Name: %s\n", inContext
->recordName
);
2008 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->recordType
), inContext
->recordType
);
2009 FPrintF( stdout
, "Mode: %s\n", inContext
->oneShotMode
? "one-shot" : "continuous" );
2010 FPrintF( stdout
, "Time limit: " );
2011 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
2012 else FPrintF( stdout
, "∞\n" );
2013 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
2014 FPrintF( stdout
, "---\n" );
2018 //===========================================================================================================================
2019 // QueryRecordCallback
2020 //===========================================================================================================================
2022 static void DNSSD_API
2023 QueryRecordCallback(
2024 DNSServiceRef inSDRef
,
2025 DNSServiceFlags inFlags
,
2026 uint32_t inInterfaceIndex
,
2027 DNSServiceErrorType inError
,
2028 const char * inFullName
,
2031 uint16_t inRDataLen
,
2032 const void * inRDataPtr
,
2036 QueryRecordContext
* const context
= (QueryRecordContext
*) inContext
;
2038 char * rdataStr
= NULL
;
2039 char time
[ kTimestampBufLen
];
2043 GetTimestampStr( time
);
2047 case kDNSServiceErr_NoError
:
2048 case kDNSServiceErr_NoSuchRecord
:
2052 case kDNSServiceErr_Timeout
:
2053 Exit( kExitReason_Timeout
);
2060 if( inError
== kDNSServiceErr_NoSuchRecord
)
2062 ASPrintF( &rdataStr
, "No Such Record" );
2066 if( !context
->printRawRData
) DNSRecordDataToString( inRDataPtr
, inRDataLen
, inType
, NULL
, 0, &rdataStr
);
2069 ASPrintF( &rdataStr
, "%#H", inRDataPtr
, inRDataLen
, INT_MAX
);
2070 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
2074 if( !context
->printedHeader
)
2076 FPrintF( stdout
, "%-26s A/R Flags IF %-32s %-5s %-5s %6s RData\n", "Timestamp", "Name", "Type", "Class", "TTL" );
2077 context
->printedHeader
= true;
2079 FPrintF( stdout
, "%-26s %-3s %5X %2d %-32s %-5s %?-5s%?5u %6u %s\n",
2080 time
, AddRmvString( inFlags
), inFlags
, (int32_t) inInterfaceIndex
, inFullName
, RecordTypeToString( inType
),
2081 ( inClass
== kDNSServiceClass_IN
), "IN", ( inClass
!= kDNSServiceClass_IN
), inClass
, inTTL
, rdataStr
);
2083 if( context
->oneShotMode
)
2085 if( ( inFlags
& kDNSServiceFlagsAdd
) &&
2086 ( ( context
->recordType
== kDNSServiceType_ANY
) || ( context
->recordType
== inType
) ) )
2088 context
->gotRecord
= true;
2090 if( !( inFlags
& kDNSServiceFlagsMoreComing
) && context
->gotRecord
) Exit( kExitReason_OneShotDone
);
2094 FreeNullSafe( rdataStr
);
2095 if( err
) exit( 1 );
2098 //===========================================================================================================================
2100 //===========================================================================================================================
2104 DNSRecordRef recordRef
; // Reference returned by DNSServiceAddRecord().
2105 uint8_t * dataPtr
; // Record data.
2106 size_t dataLen
; // Record data length.
2107 uint32_t ttl
; // Record TTL value.
2108 uint16_t type
; // Record type.
2114 DNSServiceRef opRef
; // sdRef for DNSServiceRegister operation.
2115 const char * name
; // Service name argument for DNSServiceRegister().
2116 const char * type
; // Service type argument for DNSServiceRegister().
2117 const char * domain
; // Domain in which advertise the service.
2118 uint8_t * txtPtr
; // Service TXT record data. (malloc'd)
2119 size_t txtLen
; // Service TXT record data len.
2120 ExtraRecord
* extraRecords
; // Array of extra records to add to registered service.
2121 size_t extraRecordsCount
; // Number of extra records.
2122 uint8_t * updateTXTPtr
; // Pointer to record data for TXT record update. (malloc'd)
2123 size_t updateTXTLen
; // Length of record data for TXT record update.
2124 uint32_t updateTTL
; // TTL of updated TXT record.
2125 int updateDelayMs
; // Post-registration TXT record update delay in milliseconds.
2126 DNSServiceFlags flags
; // Flags argument for DNSServiceRegister().
2127 uint32_t ifIndex
; // Interface index argument for DNSServiceRegister().
2128 int lifetimeMs
; // Lifetime of the record registration in milliseconds.
2129 uint16_t port
; // Service instance's port number.
2130 Boolean printedHeader
; // True if results header was printed.
2131 Boolean didRegister
; // True if service was registered.
2135 static void RegisterPrintPrologue( const RegisterContext
*inContext
);
2136 static void RegisterContextFree( RegisterContext
*inContext
);
2137 static void DNSSD_API
2139 DNSServiceRef inSDRef
,
2140 DNSServiceFlags inFlags
,
2141 DNSServiceErrorType inError
,
2142 const char * inName
,
2143 const char * inType
,
2144 const char * inDomain
,
2146 static void RegisterUpdate( void *inContext
);
2148 static void RegisterCmd( void )
2151 RegisterContext
* context
= NULL
;
2152 dispatch_source_t signalSource
= NULL
;
2154 // Set up SIGINT handler.
2156 signal( SIGINT
, SIG_IGN
);
2157 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
2158 require_noerr( err
, exit
);
2159 dispatch_resume( signalSource
);
2163 context
= (RegisterContext
*) calloc( 1, sizeof( *context
) );
2164 require_action( context
, exit
, err
= kNoMemoryErr
);
2166 // Check command parameters.
2168 if( ( gRegister_Port
< 0 ) || ( gRegister_Port
> UINT16_MAX
) )
2170 FPrintF( stderr
, "Port number %d is out-of-range.\n", gRegister_Port
);
2175 if( ( gAddRecord_DataCount
!= gAddRecord_TypesCount
) || ( gAddRecord_TTLsCount
!= gAddRecord_TypesCount
) )
2177 FPrintF( stderr
, "There are missing additional record parameters.\n" );
2184 context
->flags
= GetDNSSDFlagsFromOpts();
2186 // Get interface index.
2188 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
2189 require_noerr_quiet( err
, exit
);
2191 // Get TXT record data.
2195 err
= RecordDataFromArgString( gRegister_TXT
, &context
->txtPtr
, &context
->txtLen
);
2196 require_noerr_quiet( err
, exit
);
2199 // Set remaining parameters.
2201 context
->name
= gRegister_Name
;
2202 context
->type
= gRegister_Type
;
2203 context
->domain
= gRegister_Domain
;
2204 context
->port
= (uint16_t) gRegister_Port
;
2205 context
->lifetimeMs
= gRegister_LifetimeMs
;
2207 if( gAddRecord_TypesCount
> 0 )
2211 context
->extraRecords
= (ExtraRecord
*) calloc( gAddRecord_TypesCount
, sizeof( ExtraRecord
) );
2212 require_action( context
, exit
, err
= kNoMemoryErr
);
2213 context
->extraRecordsCount
= gAddRecord_TypesCount
;
2215 for( i
= 0; i
< gAddRecord_TypesCount
; ++i
)
2217 ExtraRecord
* const extraRecord
= &context
->extraRecords
[ i
];
2219 err
= RecordTypeFromArgString( gAddRecord_Types
[ i
], &extraRecord
->type
);
2220 require_noerr( err
, exit
);
2222 err
= StringToUInt32( gAddRecord_TTLs
[ i
], &extraRecord
->ttl
);
2225 FPrintF( stderr
, "Invalid TTL value: %s\n", gAddRecord_TTLs
[ i
] );
2230 err
= RecordDataFromArgString( gAddRecord_Data
[ i
], &extraRecord
->dataPtr
, &extraRecord
->dataLen
);
2231 require_noerr_quiet( err
, exit
);
2235 if( gUpdateRecord_Data
)
2237 err
= RecordDataFromArgString( gUpdateRecord_Data
, &context
->updateTXTPtr
, &context
->updateTXTLen
);
2238 require_noerr_quiet( err
, exit
);
2240 context
->updateTTL
= (uint32_t) gUpdateRecord_TTL
;
2241 context
->updateDelayMs
= gUpdateRecord_DelayMs
;
2246 RegisterPrintPrologue( context
);
2250 err
= DNSServiceRegister( &context
->opRef
, context
->flags
, context
->ifIndex
, context
->name
, context
->type
,
2251 context
->domain
, NULL
, htons( context
->port
), (uint16_t) context
->txtLen
, context
->txtPtr
,
2252 RegisterCallback
, context
);
2253 ForgetMem( &context
->txtPtr
);
2254 require_noerr( err
, exit
);
2256 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
2257 require_noerr( err
, exit
);
2262 dispatch_source_forget( &signalSource
);
2263 if( context
) RegisterContextFree( context
);
2264 if( err
) exit( 1 );
2267 //===========================================================================================================================
2268 // RegisterPrintPrologue
2269 //===========================================================================================================================
2271 static void RegisterPrintPrologue( const RegisterContext
*inContext
)
2275 char ifName
[ kInterfaceNameBufLen
];
2276 char time
[ kTimestampBufLen
];
2278 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
2280 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
2281 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
2282 FPrintF( stdout
, "Name: %s\n", inContext
->name
? inContext
->name
: "<NULL>" );
2283 FPrintF( stdout
, "Type: %s\n", inContext
->type
);
2284 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "<NULL> (default domains)" );
2285 FPrintF( stdout
, "Port: %u\n", inContext
->port
);
2286 FPrintF( stdout
, "TXT data: %#{txt}\n", inContext
->txtPtr
, inContext
->txtLen
);
2287 infinite
= ( inContext
->lifetimeMs
< 0 ) ? true : false;
2288 FPrintF( stdout
, "Lifetime: %?s%?d ms\n", infinite
, "∞", !infinite
, inContext
->lifetimeMs
);
2289 if( inContext
->updateTXTPtr
)
2291 FPrintF( stdout
, "\nUpdate record:\n" );
2292 FPrintF( stdout
, " Delay: %d ms\n", ( inContext
->updateDelayMs
> 0 ) ? inContext
->updateDelayMs
: 0 );
2293 FPrintF( stdout
, " TTL: %u%?s\n",
2294 inContext
->updateTTL
, inContext
->updateTTL
== 0, " (system will use a default value.)" );
2295 FPrintF( stdout
, " TXT data: %#{txt}\n", inContext
->updateTXTPtr
, inContext
->updateTXTLen
);
2297 if( inContext
->extraRecordsCount
> 0 ) FPrintF( stdout
, "\n" );
2298 for( i
= 0; i
< inContext
->extraRecordsCount
; ++i
)
2300 const ExtraRecord
* record
= &inContext
->extraRecords
[ i
];
2302 FPrintF( stdout
, "Extra record %zu:\n", i
+ 1 );
2303 FPrintF( stdout
, " Type: %s (%u)\n", RecordTypeToString( record
->type
), record
->type
);
2304 FPrintF( stdout
, " TTL: %u%?s\n", record
->ttl
, record
->ttl
== 0, " (system will use a default value.)" );
2305 FPrintF( stdout
, " RData: %#H\n\n", record
->dataPtr
, (int) record
->dataLen
, INT_MAX
);
2307 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
2308 FPrintF( stdout
, "---\n" );
2311 //===========================================================================================================================
2312 // RegisterContextFree
2313 //===========================================================================================================================
2315 static void RegisterContextFree( RegisterContext
*inContext
)
2317 ExtraRecord
* record
;
2318 const ExtraRecord
* const end
= inContext
->extraRecords
+ inContext
->extraRecordsCount
;
2320 DNSServiceForget( &inContext
->opRef
);
2321 ForgetMem( &inContext
->txtPtr
);
2322 for( record
= inContext
->extraRecords
; record
< end
; ++record
)
2324 check( !record
->recordRef
);
2325 ForgetMem( &record
->dataPtr
);
2327 ForgetMem( &inContext
->extraRecords
);
2328 ForgetMem( &inContext
->updateTXTPtr
);
2332 //===========================================================================================================================
2334 //===========================================================================================================================
2336 static void DNSSD_API
2338 DNSServiceRef inSDRef
,
2339 DNSServiceFlags inFlags
,
2340 DNSServiceErrorType inError
,
2341 const char * inName
,
2342 const char * inType
,
2343 const char * inDomain
,
2346 RegisterContext
* const context
= (RegisterContext
*) inContext
;
2348 char time
[ kTimestampBufLen
];
2352 GetTimestampStr( time
);
2354 if( !context
->printedHeader
)
2356 FPrintF( stdout
, "%-26s A/R Flags Service\n", "Timestamp" );
2357 context
->printedHeader
= true;
2359 FPrintF( stdout
, "%-26s %-3s %5X %s.%s%s %?#m\n",
2360 time
, AddRmvString( inFlags
), inFlags
, inName
, inType
, inDomain
, inError
, inError
);
2362 require_noerr_action_quiet( inError
, exit
, err
= inError
);
2364 if( !context
->didRegister
&& ( inFlags
& kDNSServiceFlagsAdd
) )
2366 context
->didRegister
= true;
2367 if( context
->updateTXTPtr
)
2369 if( context
->updateDelayMs
> 0 )
2371 dispatch_after_f( dispatch_time_milliseconds( context
->updateDelayMs
), dispatch_get_main_queue(),
2372 context
, RegisterUpdate
);
2376 RegisterUpdate( context
);
2379 if( context
->extraRecordsCount
> 0 )
2381 ExtraRecord
* record
;
2382 const ExtraRecord
* const end
= context
->extraRecords
+ context
->extraRecordsCount
;
2384 for( record
= context
->extraRecords
; record
< end
; ++record
)
2386 err
= DNSServiceAddRecord( context
->opRef
, &record
->recordRef
, 0, record
->type
,
2387 (uint16_t) record
->dataLen
, record
->dataPtr
, record
->ttl
);
2388 require_noerr( err
, exit
);
2391 if( context
->lifetimeMs
== 0 )
2393 Exit( kExitReason_TimeLimit
);
2395 else if( context
->lifetimeMs
> 0 )
2397 dispatch_after_f( dispatch_time_milliseconds( context
->lifetimeMs
), dispatch_get_main_queue(),
2398 kExitReason_TimeLimit
, Exit
);
2404 if( err
) exit( 1 );
2407 //===========================================================================================================================
2409 //===========================================================================================================================
2411 static void RegisterUpdate( void *inContext
)
2414 RegisterContext
* const context
= (RegisterContext
*) inContext
;
2416 err
= DNSServiceUpdateRecord( context
->opRef
, NULL
, 0, (uint16_t) context
->updateTXTLen
, context
->updateTXTPtr
,
2417 context
->updateTTL
);
2418 require_noerr( err
, exit
);
2421 if( err
) exit( 1 );
2424 //===========================================================================================================================
2425 // RegisterRecordCmd
2426 //===========================================================================================================================
2430 DNSServiceRef conRef
; // sdRef to be initialized by DNSServiceCreateConnection().
2431 DNSRecordRef recordRef
; // Registered record reference.
2432 const char * recordName
; // Name of resource record.
2433 uint8_t * dataPtr
; // Pointer to resource record data.
2434 size_t dataLen
; // Length of resource record data.
2435 uint32_t ttl
; // TTL value of resource record in seconds.
2436 uint32_t ifIndex
; // Interface index argument for DNSServiceRegisterRecord().
2437 DNSServiceFlags flags
; // Flags argument for DNSServiceRegisterRecord().
2438 int lifetimeMs
; // Lifetime of the record registration in milliseconds.
2439 uint16_t recordType
; // Resource record type.
2440 uint8_t * updateDataPtr
; // Pointer to data for record update. (malloc'd)
2441 size_t updateDataLen
; // Length of data for record update.
2442 uint32_t updateTTL
; // TTL for updated record.
2443 int updateDelayMs
; // Post-registration record update delay in milliseconds.
2444 Boolean didRegister
; // True if the record was registered.
2446 } RegisterRecordContext
;
2448 static void RegisterRecordPrintPrologue( const RegisterRecordContext
*inContext
);
2449 static void RegisterRecordContextFree( RegisterRecordContext
*inContext
);
2450 static void DNSSD_API
2451 RegisterRecordCallback(
2452 DNSServiceRef inSDRef
,
2453 DNSRecordRef inRecordRef
,
2454 DNSServiceFlags inFlags
,
2455 DNSServiceErrorType inError
,
2457 static void RegisterRecordUpdate( void *inContext
);
2459 static void RegisterRecordCmd( void )
2462 RegisterRecordContext
* context
= NULL
;
2463 dispatch_source_t signalSource
= NULL
;
2465 // Set up SIGINT handler.
2467 signal( SIGINT
, SIG_IGN
);
2468 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
2469 require_noerr( err
, exit
);
2470 dispatch_resume( signalSource
);
2474 context
= (RegisterRecordContext
*) calloc( 1, sizeof( *context
) );
2475 require_action( context
, exit
, err
= kNoMemoryErr
);
2477 // Create connection.
2479 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->conRef
, NULL
);
2480 require_noerr_quiet( err
, exit
);
2484 context
->flags
= GetDNSSDFlagsFromOpts();
2488 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
2489 require_noerr_quiet( err
, exit
);
2493 err
= RecordTypeFromArgString( gRegisterRecord_Type
, &context
->recordType
);
2494 require_noerr( err
, exit
);
2498 if( gRegisterRecord_Data
)
2500 err
= RecordDataFromArgString( gRegisterRecord_Data
, &context
->dataPtr
, &context
->dataLen
);
2501 require_noerr_quiet( err
, exit
);
2504 // Set remaining parameters.
2506 context
->recordName
= gRegisterRecord_Name
;
2507 context
->ttl
= (uint32_t) gRegisterRecord_TTL
;
2508 context
->lifetimeMs
= gRegisterRecord_LifetimeMs
;
2512 if( gRegisterRecord_UpdateData
)
2514 err
= RecordDataFromArgString( gRegisterRecord_UpdateData
, &context
->updateDataPtr
, &context
->updateDataLen
);
2515 require_noerr_quiet( err
, exit
);
2517 context
->updateTTL
= (uint32_t) gRegisterRecord_UpdateTTL
;
2518 context
->updateDelayMs
= gRegisterRecord_UpdateDelayMs
;
2523 RegisterRecordPrintPrologue( context
);
2527 err
= DNSServiceRegisterRecord( context
->conRef
, &context
->recordRef
, context
->flags
, context
->ifIndex
,
2528 context
->recordName
, context
->recordType
, kDNSServiceClass_IN
, (uint16_t) context
->dataLen
, context
->dataPtr
,
2529 context
->ttl
, RegisterRecordCallback
, context
);
2532 FPrintF( stderr
, "DNSServiceRegisterRecord() returned %#m\n", err
);
2539 dispatch_source_forget( &signalSource
);
2540 if( context
) RegisterRecordContextFree( context
);
2541 if( err
) exit( 1 );
2544 //===========================================================================================================================
2545 // RegisterRecordPrintPrologue
2546 //===========================================================================================================================
2548 static void RegisterRecordPrintPrologue( const RegisterRecordContext
*inContext
)
2551 char time
[ kTimestampBufLen
];
2552 char ifName
[ kInterfaceNameBufLen
];
2554 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
2556 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
2557 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
2558 FPrintF( stdout
, "Name: %s\n", inContext
->recordName
);
2559 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->recordType
), inContext
->recordType
);
2560 FPrintF( stdout
, "TTL: %u\n", inContext
->ttl
);
2561 FPrintF( stdout
, "Data: %#H\n", inContext
->dataPtr
, (int) inContext
->dataLen
, INT_MAX
);
2562 infinite
= ( inContext
->lifetimeMs
< 0 ) ? true : false;
2563 FPrintF( stdout
, "Lifetime: %?s%?d ms\n", infinite
, "∞", !infinite
, inContext
->lifetimeMs
);
2564 if( inContext
->updateDataPtr
)
2566 FPrintF( stdout
, "\nUpdate record:\n" );
2567 FPrintF( stdout
, " Delay: %d ms\n", ( inContext
->updateDelayMs
>= 0 ) ? inContext
->updateDelayMs
: 0 );
2568 FPrintF( stdout
, " TTL: %u%?s\n",
2569 inContext
->updateTTL
, inContext
->updateTTL
== 0, " (system will use a default value.)" );
2570 FPrintF( stdout
, " RData: %#H\n", inContext
->updateDataPtr
, (int) inContext
->updateDataLen
, INT_MAX
);
2572 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
2573 FPrintF( stdout
, "---\n" );
2576 //===========================================================================================================================
2577 // RegisterRecordContextFree
2578 //===========================================================================================================================
2580 static void RegisterRecordContextFree( RegisterRecordContext
*inContext
)
2582 DNSServiceForget( &inContext
->conRef
);
2583 ForgetMem( &inContext
->dataPtr
);
2584 ForgetMem( &inContext
->updateDataPtr
);
2588 //===========================================================================================================================
2589 // RegisterRecordCallback
2590 //===========================================================================================================================
2593 RegisterRecordCallback(
2594 DNSServiceRef inSDRef
,
2595 DNSRecordRef inRecordRef
,
2596 DNSServiceFlags inFlags
,
2597 DNSServiceErrorType inError
,
2600 RegisterRecordContext
* context
= (RegisterRecordContext
*) inContext
;
2601 char time
[ kTimestampBufLen
];
2604 Unused( inRecordRef
);
2608 GetTimestampStr( time
);
2609 FPrintF( stdout
, "%s Record registration result (error %#m)\n", time
, inError
);
2611 if( !context
->didRegister
&& !inError
)
2613 context
->didRegister
= true;
2614 if( context
->updateDataPtr
)
2616 if( context
->updateDelayMs
> 0 )
2618 dispatch_after_f( dispatch_time_milliseconds( context
->updateDelayMs
), dispatch_get_main_queue(),
2619 context
, RegisterRecordUpdate
);
2623 RegisterRecordUpdate( context
);
2626 if( context
->lifetimeMs
== 0 )
2628 Exit( kExitReason_TimeLimit
);
2630 else if( context
->lifetimeMs
> 0 )
2632 dispatch_after_f( dispatch_time_milliseconds( context
->lifetimeMs
), dispatch_get_main_queue(),
2633 kExitReason_TimeLimit
, Exit
);
2638 //===========================================================================================================================
2639 // RegisterRecordUpdate
2640 //===========================================================================================================================
2642 static void RegisterRecordUpdate( void *inContext
)
2645 RegisterRecordContext
* const context
= (RegisterRecordContext
*) inContext
;
2647 err
= DNSServiceUpdateRecord( context
->conRef
, context
->recordRef
, 0, (uint16_t) context
->updateDataLen
,
2648 context
->updateDataPtr
, context
->updateTTL
);
2649 require_noerr( err
, exit
);
2652 if( err
) exit( 1 );
2655 //===========================================================================================================================
2657 //===========================================================================================================================
2661 DNSServiceRef mainRef
; // Main sdRef for shared connections.
2662 DNSServiceRef opRef
; // sdRef for the DNSServiceResolve operation.
2663 DNSServiceFlags flags
; // Flags argument for DNSServiceResolve().
2664 const char * name
; // Service name argument for DNSServiceResolve().
2665 const char * type
; // Service type argument for DNSServiceResolve().
2666 const char * domain
; // Domain argument for DNSServiceResolve().
2667 uint32_t ifIndex
; // Interface index argument for DNSServiceResolve().
2668 int timeLimitSecs
; // Time limit for the DNSServiceResolve operation in seconds.
2672 static void ResolvePrintPrologue( const ResolveContext
*inContext
);
2673 static void ResolveContextFree( ResolveContext
*inContext
);
2674 static void DNSSD_API
2676 DNSServiceRef inSDRef
,
2677 DNSServiceFlags inFlags
,
2678 uint32_t inInterfaceIndex
,
2679 DNSServiceErrorType inError
,
2680 const char * inFullName
,
2681 const char * inHostname
,
2684 const unsigned char * inTXTPtr
,
2687 static void ResolveCmd( void )
2690 DNSServiceRef sdRef
;
2691 ResolveContext
* context
= NULL
;
2692 dispatch_source_t signalSource
= NULL
;
2693 int useMainConnection
;
2695 // Set up SIGINT handler.
2697 signal( SIGINT
, SIG_IGN
);
2698 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
2699 require_noerr( err
, exit
);
2700 dispatch_resume( signalSource
);
2704 context
= (ResolveContext
*) calloc( 1, sizeof( *context
) );
2705 require_action( context
, exit
, err
= kNoMemoryErr
);
2707 // Check command parameters.
2709 if( gResolve_TimeLimitSecs
< 0 )
2711 FPrintF( stderr
, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs
);
2716 // Create main connection.
2718 if( gConnectionOpt
)
2720 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
2721 require_noerr_quiet( err
, exit
);
2722 useMainConnection
= true;
2726 useMainConnection
= false;
2731 context
->flags
= GetDNSSDFlagsFromOpts();
2732 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
2734 // Get interface index.
2736 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
2737 require_noerr_quiet( err
, exit
);
2739 // Set remaining parameters.
2741 context
->name
= gResolve_Name
;
2742 context
->type
= gResolve_Type
;
2743 context
->domain
= gResolve_Domain
;
2744 context
->timeLimitSecs
= gResolve_TimeLimitSecs
;
2748 ResolvePrintPrologue( context
);
2752 if( useMainConnection
) sdRef
= context
->mainRef
;
2753 err
= DNSServiceResolve( &sdRef
, context
->flags
, context
->ifIndex
, context
->name
, context
->type
, context
->domain
,
2754 ResolveCallback
, NULL
);
2755 require_noerr( err
, exit
);
2757 context
->opRef
= sdRef
;
2758 if( !useMainConnection
)
2760 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
2761 require_noerr( err
, exit
);
2766 if( context
->timeLimitSecs
> 0 )
2768 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
2769 kExitReason_TimeLimit
, Exit
);
2774 dispatch_source_forget( &signalSource
);
2775 if( context
) ResolveContextFree( context
);
2776 if( err
) exit( 1 );
2779 //===========================================================================================================================
2781 //===========================================================================================================================
2783 static void ReconfirmCmd( void )
2786 uint8_t * rdataPtr
= NULL
;
2787 size_t rdataLen
= 0;
2788 DNSServiceFlags flags
;
2790 uint16_t type
, class;
2791 char ifName
[ kInterfaceNameBufLen
];
2795 flags
= GetDNSSDFlagsFromOpts();
2797 // Get interface index.
2799 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
2800 require_noerr_quiet( err
, exit
);
2804 err
= RecordTypeFromArgString( gReconfirmRecord_Type
, &type
);
2805 require_noerr( err
, exit
);
2809 if( gReconfirmRecord_Data
)
2811 err
= RecordDataFromArgString( gReconfirmRecord_Data
, &rdataPtr
, &rdataLen
);
2812 require_noerr_quiet( err
, exit
);
2815 // Get record class.
2817 if( gReconfirmRecord_Class
)
2819 err
= RecordClassFromArgString( gReconfirmRecord_Class
, &class );
2820 require_noerr( err
, exit
);
2824 class = kDNSServiceClass_IN
;
2829 FPrintF( stdout
, "Flags: %#{flags}\n", flags
, kDNSServiceFlagsDescriptors
);
2830 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
2831 FPrintF( stdout
, "Name: %s\n", gReconfirmRecord_Name
);
2832 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
2833 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
2834 FPrintF( stdout
, "Data: %#H\n", rdataPtr
, (int) rdataLen
, INT_MAX
);
2835 FPrintF( stdout
, "---\n" );
2837 err
= DNSServiceReconfirmRecord( flags
, ifIndex
, gReconfirmRecord_Name
, type
, class, (uint16_t) rdataLen
, rdataPtr
);
2838 FPrintF( stdout
, "Error: %#m\n", err
);
2841 FreeNullSafe( rdataPtr
);
2842 if( err
) exit( 1 );
2845 //===========================================================================================================================
2846 // ResolvePrintPrologue
2847 //===========================================================================================================================
2849 static void ResolvePrintPrologue( const ResolveContext
*inContext
)
2851 const int timeLimitSecs
= inContext
->timeLimitSecs
;
2852 char ifName
[ kInterfaceNameBufLen
];
2853 char time
[ kTimestampBufLen
];
2855 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
2857 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
2858 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
2859 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
2860 FPrintF( stdout
, "Type: %s\n", inContext
->type
);
2861 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
);
2862 FPrintF( stdout
, "Time limit: " );
2863 if( timeLimitSecs
> 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
2864 else FPrintF( stdout
, "∞\n" );
2865 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
2866 FPrintF( stdout
, "---\n" );
2869 //===========================================================================================================================
2870 // ResolveContextFree
2871 //===========================================================================================================================
2873 static void ResolveContextFree( ResolveContext
*inContext
)
2875 DNSServiceForget( &inContext
->opRef
);
2876 DNSServiceForget( &inContext
->mainRef
);
2880 //===========================================================================================================================
2882 //===========================================================================================================================
2884 static void DNSSD_API
2886 DNSServiceRef inSDRef
,
2887 DNSServiceFlags inFlags
,
2888 uint32_t inInterfaceIndex
,
2889 DNSServiceErrorType inError
,
2890 const char * inFullName
,
2891 const char * inHostname
,
2894 const unsigned char * inTXTPtr
,
2897 char time
[ kTimestampBufLen
];
2898 char errorStr
[ 64 ];
2902 Unused( inContext
);
2904 GetTimestampStr( time
);
2906 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " error %#m", inError
);
2908 FPrintF( stdout
, "%s: %s can be reached at %s:%u (interface %d)%?s\n",
2909 time
, inFullName
, inHostname
, ntohs( inPort
), (int32_t) inInterfaceIndex
, inError
, errorStr
);
2912 FPrintF( stdout
, " TXT record: %#H\n", inTXTPtr
, (int) inTXTLen
, INT_MAX
);
2916 FPrintF( stdout
, " TXT record: %#{txt}\n", inTXTPtr
, (size_t) inTXTLen
);
2920 //===========================================================================================================================
2921 // GetAddrInfoPOSIXCmd
2922 //===========================================================================================================================
2924 #define AddressFamilyStr( X ) ( \
2925 ( (X) == AF_INET ) ? "inet" : \
2926 ( (X) == AF_INET6 ) ? "inet6" : \
2927 ( (X) == AF_UNSPEC ) ? "unspec" : \
2937 #define CaseFlagStringify( X ) { (X), # X }
2939 const FlagStringPair kGAIPOSIXFlagStringPairs
[] =
2941 #if( defined( AI_UNUSABLE ) )
2942 CaseFlagStringify( AI_UNUSABLE
),
2944 CaseFlagStringify( AI_NUMERICSERV
),
2945 CaseFlagStringify( AI_V4MAPPED
),
2946 CaseFlagStringify( AI_ADDRCONFIG
),
2947 #if( defined( AI_V4MAPPED_CFG ) )
2948 CaseFlagStringify( AI_V4MAPPED_CFG
),
2950 CaseFlagStringify( AI_ALL
),
2951 CaseFlagStringify( AI_NUMERICHOST
),
2952 CaseFlagStringify( AI_CANONNAME
),
2953 CaseFlagStringify( AI_PASSIVE
),
2957 static void GetAddrInfoPOSIXCmd( void )
2960 struct addrinfo hints
;
2961 const struct addrinfo
* addrInfo
;
2962 struct addrinfo
* addrInfoList
= NULL
;
2963 const FlagStringPair
* pair
;
2964 char time
[ kTimestampBufLen
];
2966 memset( &hints
, 0, sizeof( hints
) );
2967 hints
.ai_socktype
= SOCK_STREAM
;
2969 // Set hints address family.
2971 if( !gGAIPOSIX_Family
) hints
.ai_family
= AF_UNSPEC
;
2972 else if( strcasecmp( gGAIPOSIX_Family
, "inet" ) == 0 ) hints
.ai_family
= AF_INET
;
2973 else if( strcasecmp( gGAIPOSIX_Family
, "inet6" ) == 0 ) hints
.ai_family
= AF_INET6
;
2974 else if( strcasecmp( gGAIPOSIX_Family
, "unspec" ) == 0 ) hints
.ai_family
= AF_UNSPEC
;
2977 FPrintF( stderr
, "Invalid address family: %s.\n", gGAIPOSIX_Family
);
2984 if( gGAIPOSIXFlag_AddrConfig
) hints
.ai_flags
|= AI_ADDRCONFIG
;
2985 if( gGAIPOSIXFlag_All
) hints
.ai_flags
|= AI_ALL
;
2986 if( gGAIPOSIXFlag_CanonName
) hints
.ai_flags
|= AI_CANONNAME
;
2987 if( gGAIPOSIXFlag_NumericHost
) hints
.ai_flags
|= AI_NUMERICHOST
;
2988 if( gGAIPOSIXFlag_NumericServ
) hints
.ai_flags
|= AI_NUMERICSERV
;
2989 if( gGAIPOSIXFlag_Passive
) hints
.ai_flags
|= AI_PASSIVE
;
2990 if( gGAIPOSIXFlag_V4Mapped
) hints
.ai_flags
|= AI_V4MAPPED
;
2991 #if( defined( AI_V4MAPPED_CFG ) )
2992 if( gGAIPOSIXFlag_V4MappedCFG
) hints
.ai_flags
|= AI_V4MAPPED_CFG
;
2994 #if( defined( AI_DEFAULT ) )
2995 if( gGAIPOSIXFlag_Default
) hints
.ai_flags
|= AI_DEFAULT
;
2997 #if( defined( AI_UNUSABLE ) )
2998 if( gGAIPOSIXFlag_Unusable
) hints
.ai_flags
|= AI_UNUSABLE
;
3003 FPrintF( stdout
, "Hostname: %s\n", gGAIPOSIX_HostName
);
3004 FPrintF( stdout
, "Servname: %s\n", gGAIPOSIX_ServName
);
3005 FPrintF( stdout
, "Address family: %s\n", AddressFamilyStr( hints
.ai_family
) );
3006 FPrintF( stdout
, "Flags: 0x%X < ", hints
.ai_flags
);
3007 for( pair
= kGAIPOSIXFlagStringPairs
; pair
->str
!= NULL
; ++pair
)
3009 if( ( (unsigned int) hints
.ai_flags
) & pair
->flag
) FPrintF( stdout
, "%s ", pair
->str
);
3011 FPrintF( stdout
, ">\n" );
3012 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
3013 FPrintF( stdout
, "---\n" );
3015 // Call getaddrinfo().
3017 err
= getaddrinfo( gGAIPOSIX_HostName
, gGAIPOSIX_ServName
, &hints
, &addrInfoList
);
3018 GetTimestampStr( time
);
3021 FPrintF( stderr
, "Error %d: %s.\n", err
, gai_strerror( err
) );
3027 for( addrInfo
= addrInfoList
; addrInfo
; addrInfo
= addrInfo
->ai_next
) { ++addrCount
; }
3029 FPrintF( stdout
, "Addresses (%d total):\n", addrCount
);
3030 for( addrInfo
= addrInfoList
; addrInfo
; addrInfo
= addrInfo
->ai_next
)
3032 FPrintF( stdout
, "%##a\n", addrInfo
->ai_addr
);
3035 FPrintF( stdout
, "---\n" );
3036 FPrintF( stdout
, "End time: %s\n", time
);
3039 if( addrInfoList
) freeaddrinfo( addrInfoList
);
3040 if( err
) exit( 1 );
3043 //===========================================================================================================================
3045 //===========================================================================================================================
3047 static void ReverseLookupCmd( void )
3050 QueryRecordContext
* context
= NULL
;
3051 DNSServiceRef sdRef
;
3052 dispatch_source_t signalSource
= NULL
;
3054 uint8_t ipv6Addr
[ 16 ];
3055 char recordName
[ ( 16 * 4 ) + 9 + 1 ];
3056 int useMainConnection
;
3057 const char * endPtr
;
3059 // Set up SIGINT handler.
3061 signal( SIGINT
, SIG_IGN
);
3062 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3063 require_noerr( err
, exit
);
3064 dispatch_resume( signalSource
);
3068 context
= (QueryRecordContext
*) calloc( 1, sizeof( *context
) );
3069 require_action( context
, exit
, err
= kNoMemoryErr
);
3071 // Check command parameters.
3073 if( gReverseLookup_TimeLimitSecs
< 0 )
3075 FPrintF( stderr
, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs
);
3080 // Create main connection.
3082 if( gConnectionOpt
)
3084 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
3085 require_noerr_quiet( err
, exit
);
3086 useMainConnection
= true;
3090 useMainConnection
= false;
3095 context
->flags
= GetDNSSDFlagsFromOpts();
3096 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
3098 // Get interface index.
3100 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3101 require_noerr_quiet( err
, exit
);
3103 // Create reverse lookup record name.
3105 err
= StringToIPv4Address( gReverseLookup_IPAddr
, kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
,
3106 &ipv4Addr
, NULL
, NULL
, NULL
, &endPtr
);
3107 if( err
|| ( *endPtr
!= '\0' ) )
3112 err
= StringToIPv6Address( gReverseLookup_IPAddr
,
3113 kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
| kStringToIPAddressFlagsNoScope
,
3114 ipv6Addr
, NULL
, NULL
, NULL
, &endPtr
);
3115 if( err
|| ( *endPtr
!= '\0' ) )
3117 FPrintF( stderr
, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr
);
3122 for( i
= 15; i
>= 0; --i
)
3124 *dst
++ = kHexDigitsLowercase
[ ipv6Addr
[ i
] & 0x0F ];
3126 *dst
++ = kHexDigitsLowercase
[ ipv6Addr
[ i
] >> 4 ];
3129 strcpy( dst
, "ip6.arpa." );
3130 check( ( strlen( recordName
) + 1 ) <= sizeof( recordName
) );
3134 SNPrintF( recordName
, sizeof( recordName
), "%u.%u.%u.%u.in-addr.arpa.",
3136 ( ipv4Addr
>> 8 ) & 0xFF,
3137 ( ipv4Addr
>> 16 ) & 0xFF,
3138 ( ipv4Addr
>> 24 ) & 0xFF );
3141 // Set remaining parameters.
3143 context
->recordName
= recordName
;
3144 context
->recordType
= kDNSServiceType_PTR
;
3145 context
->timeLimitSecs
= gReverseLookup_TimeLimitSecs
;
3146 context
->oneShotMode
= gReverseLookup_OneShot
? true : false;
3150 QueryRecordPrintPrologue( context
);
3154 if( useMainConnection
) sdRef
= context
->mainRef
;
3155 err
= DNSServiceQueryRecord( &sdRef
, context
->flags
, context
->ifIndex
, context
->recordName
, context
->recordType
,
3156 kDNSServiceClass_IN
, QueryRecordCallback
, context
);
3157 require_noerr( err
, exit
);
3159 context
->opRef
= sdRef
;
3160 if( !useMainConnection
)
3162 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3163 require_noerr( err
, exit
);
3168 if( context
->timeLimitSecs
> 0 )
3170 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(),
3171 kExitReason_TimeLimit
, Exit
);
3176 dispatch_source_forget( &signalSource
);
3177 if( context
) QueryRecordContextFree( context
);
3178 if( err
) exit( 1 );
3181 //===========================================================================================================================
3183 //===========================================================================================================================
3187 DNSServiceRef mainRef
; // Main sdRef for shared connection.
3188 DNSServiceRef opRef
; // sdRef for the DNSServiceNATPortMappingCreate operation.
3189 DNSServiceFlags flags
; // Flags for DNSServiceNATPortMappingCreate operation.
3190 uint32_t ifIndex
; // Interface index argument for DNSServiceNATPortMappingCreate operation.
3191 DNSServiceProtocol protocols
; // Protocols argument for DNSServiceNATPortMappingCreate operation.
3192 uint32_t ttl
; // TTL argument for DNSServiceNATPortMappingCreate operation.
3193 uint16_t internalPort
; // Internal port argument for DNSServiceNATPortMappingCreate operation.
3194 uint16_t externalPort
; // External port argument for DNSServiceNATPortMappingCreate operation.
3195 Boolean printedHeader
; // True if results header was printed.
3197 } PortMappingContext
;
3199 static void PortMappingPrintPrologue( const PortMappingContext
*inContext
);
3200 static void PortMappingContextFree( PortMappingContext
*inContext
);
3201 static void DNSSD_API
3202 PortMappingCallback(
3203 DNSServiceRef inSDRef
,
3204 DNSServiceFlags inFlags
,
3205 uint32_t inInterfaceIndex
,
3206 DNSServiceErrorType inError
,
3207 uint32_t inExternalIPv4Address
,
3208 DNSServiceProtocol inProtocol
,
3209 uint16_t inInternalPort
,
3210 uint16_t inExternalPort
,
3214 static void PortMappingCmd( void )
3217 PortMappingContext
* context
= NULL
;
3218 DNSServiceRef sdRef
;
3219 dispatch_source_t signalSource
= NULL
;
3220 int useMainConnection
;
3222 // Set up SIGINT handler.
3224 signal( SIGINT
, SIG_IGN
);
3225 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
3226 require_noerr( err
, exit
);
3227 dispatch_resume( signalSource
);
3231 context
= (PortMappingContext
*) calloc( 1, sizeof( *context
) );
3232 require_action( context
, exit
, err
= kNoMemoryErr
);
3234 // Check command parameters.
3236 if( ( gPortMapping_InternalPort
< 0 ) || ( gPortMapping_InternalPort
> UINT16_MAX
) )
3238 FPrintF( stderr
, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort
);
3243 if( ( gPortMapping_ExternalPort
< 0 ) || ( gPortMapping_ExternalPort
> UINT16_MAX
) )
3245 FPrintF( stderr
, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort
);
3250 // Create main connection.
3252 if( gConnectionOpt
)
3254 err
= CreateConnectionFromArgString( gConnectionOpt
, dispatch_get_main_queue(), &context
->mainRef
, NULL
);
3255 require_noerr_quiet( err
, exit
);
3256 useMainConnection
= true;
3260 useMainConnection
= false;
3265 context
->flags
= GetDNSSDFlagsFromOpts();
3266 if( useMainConnection
) context
->flags
|= kDNSServiceFlagsShareConnection
;
3268 // Get interface index.
3270 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3271 require_noerr_quiet( err
, exit
);
3273 // Set remaining parameters.
3275 if( gPortMapping_ProtocolTCP
) context
->protocols
|= kDNSServiceProtocol_TCP
;
3276 if( gPortMapping_ProtocolUDP
) context
->protocols
|= kDNSServiceProtocol_UDP
;
3277 context
->ttl
= (uint32_t) gPortMapping_TTL
;
3278 context
->internalPort
= (uint16_t) gPortMapping_InternalPort
;
3279 context
->externalPort
= (uint16_t) gPortMapping_ExternalPort
;
3283 PortMappingPrintPrologue( context
);
3287 if( useMainConnection
) sdRef
= context
->mainRef
;
3288 err
= DNSServiceNATPortMappingCreate( &sdRef
, context
->flags
, context
->ifIndex
, context
->protocols
,
3289 htons( context
->internalPort
), htons( context
->externalPort
), context
->ttl
, PortMappingCallback
, context
);
3290 require_noerr( err
, exit
);
3292 context
->opRef
= sdRef
;
3293 if( !useMainConnection
)
3295 err
= DNSServiceSetDispatchQueue( context
->opRef
, dispatch_get_main_queue() );
3296 require_noerr( err
, exit
);
3302 dispatch_source_forget( &signalSource
);
3303 if( context
) PortMappingContextFree( context
);
3304 if( err
) exit( 1 );
3307 //===========================================================================================================================
3308 // PortMappingPrintPrologue
3309 //===========================================================================================================================
3311 static void PortMappingPrintPrologue( const PortMappingContext
*inContext
)
3313 char ifName
[ kInterfaceNameBufLen
];
3314 char time
[ kTimestampBufLen
];
3316 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3318 FPrintF( stdout
, "Flags: %#{flags}\n", inContext
->flags
, kDNSServiceFlagsDescriptors
);
3319 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3320 FPrintF( stdout
, "Protocols: %#{flags}\n", inContext
->protocols
, kDNSServiceProtocolDescriptors
);
3321 FPrintF( stdout
, "Internal Port: %u\n", inContext
->internalPort
);
3322 FPrintF( stdout
, "External Port: %u\n", inContext
->externalPort
);
3323 FPrintF( stdout
, "TTL: %u%?s\n", inContext
->ttl
, !inContext
->ttl
, " (system will use a default value.)" );
3324 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
3325 FPrintF( stdout
, "---\n" );
3329 //===========================================================================================================================
3330 // PortMappingContextFree
3331 //===========================================================================================================================
3333 static void PortMappingContextFree( PortMappingContext
*inContext
)
3335 DNSServiceForget( &inContext
->opRef
);
3336 DNSServiceForget( &inContext
->mainRef
);
3340 //===========================================================================================================================
3341 // PortMappingCallback
3342 //===========================================================================================================================
3344 static void DNSSD_API
3345 PortMappingCallback(
3346 DNSServiceRef inSDRef
,
3347 DNSServiceFlags inFlags
,
3348 uint32_t inInterfaceIndex
,
3349 DNSServiceErrorType inError
,
3350 uint32_t inExternalIPv4Address
,
3351 DNSServiceProtocol inProtocol
,
3352 uint16_t inInternalPort
,
3353 uint16_t inExternalPort
,
3357 PortMappingContext
* const context
= (PortMappingContext
*) inContext
;
3358 char time
[ kTimestampBufLen
];
3359 char errorStr
[ 128 ];
3364 GetTimestampStr( time
);
3366 if( inError
) SNPrintF( errorStr
, sizeof( errorStr
), " (error: %#m)", inError
);
3367 if( !context
->printedHeader
)
3369 FPrintF( stdout
, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" );
3370 context
->printedHeader
= true;
3372 FPrintF( stdout
, "%-26s %2u %7u %15.4a %7u %6u %#{flags}%?s\n",
3373 time
, inInterfaceIndex
, ntohs( inInternalPort
), &inExternalIPv4Address
, ntohs( inExternalPort
), inTTL
,
3374 inProtocol
, kDNSServiceProtocolDescriptors
, inError
, errorStr
);
3377 //===========================================================================================================================
3379 //===========================================================================================================================
3381 typedef struct BrowseDomain BrowseDomain
;
3382 typedef struct BrowseType BrowseType
;
3383 typedef struct BrowseOp BrowseOp
;
3384 typedef struct BrowseInstance BrowseInstance
;
3385 typedef struct BrowseIPAddr BrowseIPAddr
;
3390 DNSServiceRef mainRef
;
3391 DNSServiceRef domainsQuery
;
3392 const char * domain
;
3393 BrowseDomain
* domainList
;
3394 char ** serviceTypes
;
3395 size_t serviceTypesCount
;
3396 dispatch_source_t exitTimer
;
3398 int pendingConnectCount
;
3400 int maxConnectTimeSecs
;
3401 Boolean includeAWDL
;
3402 Boolean useColoredText
;
3408 BrowseDomain
* next
;
3410 DNSServiceRef servicesQuery
;
3411 BrowseAllContext
* context
;
3412 BrowseType
* typeList
;
3419 BrowseOp
* browseList
;
3425 BrowseAllContext
* context
;
3426 DNSServiceRef browse
;
3427 uint64_t startTicks
;
3428 BrowseInstance
* instanceList
;
3433 struct BrowseInstance
3435 BrowseInstance
* next
;
3436 BrowseAllContext
* context
;
3438 uint64_t foundTicks
;
3439 DNSServiceRef resolve
;
3440 uint64_t resolveStartTicks
;
3441 uint64_t resolveDoneTicks
;
3442 DNSServiceRef getAddr
;
3443 uint64_t getAddrStartTicks
;
3444 BrowseIPAddr
* addrList
;
3455 kConnectStatus_None
= 0,
3456 kConnectStatus_Pending
= 1,
3457 kConnectStatus_Succeeded
= 2,
3458 kConnectStatus_Failed
= 3
3464 BrowseIPAddr
* next
;
3467 BrowseAllContext
* context
;
3468 uint64_t foundTicks
;
3469 AsyncConnectionRef connection
;
3470 ConnectStatus connectStatus
;
3471 CFTimeInterval connectTimeSecs
;
3472 OSStatus connectError
;
3475 static void BrowseAllPrintPrologue( const BrowseAllContext
*inContext
);
3476 static void DNSSD_API
3477 BrowseAllQueryDomainsCallback(
3478 DNSServiceRef inSDRef
,
3479 DNSServiceFlags inFlags
,
3480 uint32_t inInterfaceIndex
,
3481 DNSServiceErrorType inError
,
3482 const char * inFullName
,
3485 uint16_t inRDataLen
,
3486 const void * inRDataPtr
,
3489 static void DNSSD_API
3490 BrowseAllQueryCallback(
3491 DNSServiceRef inSDRef
,
3492 DNSServiceFlags inFlags
,
3493 uint32_t inInterfaceIndex
,
3494 DNSServiceErrorType inError
,
3495 const char * inFullName
,
3498 uint16_t inRDataLen
,
3499 const void * inRDataPtr
,
3502 static void DNSSD_API
3503 BrowseAllBrowseCallback(
3504 DNSServiceRef inSDRef
,
3505 DNSServiceFlags inFlags
,
3506 uint32_t inInterfaceIndex
,
3507 DNSServiceErrorType inError
,
3508 const char * inName
,
3509 const char * inRegType
,
3510 const char * inDomain
,
3512 static void DNSSD_API
3513 BrowseAllResolveCallback(
3514 DNSServiceRef inSDRef
,
3515 DNSServiceFlags inFlags
,
3516 uint32_t inInterfaceIndex
,
3517 DNSServiceErrorType inError
,
3518 const char * inFullName
,
3519 const char * inHostname
,
3522 const unsigned char * inTXTPtr
,
3524 static void DNSSD_API
3525 BrowseAllGAICallback(
3526 DNSServiceRef inSDRef
,
3527 DNSServiceFlags inFlags
,
3528 uint32_t inInterfaceIndex
,
3529 DNSServiceErrorType inError
,
3530 const char * inHostname
,
3531 const struct sockaddr
* inSockAddr
,
3534 static void BrowseAllConnectionProgress( int inPhase
, const void *inDetails
, void *inArg
);
3535 static void BrowseAllConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
);
3536 static void BrowseAllStop( void *inContext
);
3537 static void BrowseAllExit( void *inContext
);
3538 static OSStatus
BrowseAllAddDomain( BrowseAllContext
*inContext
, const char *inName
);
3539 static OSStatus
BrowseAllRemoveDomain( BrowseAllContext
*inContext
, const char *inName
);
3540 static void BrowseAllContextRelease( BrowseAllContext
*inContext
);
3542 BrowseAllAddServiceType(
3543 BrowseAllContext
* inContext
,
3544 BrowseDomain
* inDomain
,
3545 const char * inName
,
3547 Boolean inIncludeAWDL
);
3549 BrowseAllRemoveServiceType(
3550 BrowseAllContext
* inContext
,
3551 BrowseDomain
* inDomain
,
3552 const char * inName
,
3553 uint32_t inIfIndex
);
3555 BrowseAllAddServiceInstance(
3556 BrowseAllContext
* inContext
,
3557 BrowseOp
* inBrowse
,
3558 const char * inName
,
3559 const char * inRegType
,
3560 const char * inDomain
,
3561 uint32_t inIfIndex
);
3563 BrowseAllRemoveServiceInstance(
3564 BrowseAllContext
* inContext
,
3565 BrowseOp
* inBrowse
,
3566 const char * inName
,
3567 uint32_t inIfIndex
);
3569 BrowseAllAddIPAddress(
3570 BrowseAllContext
* inContext
,
3571 BrowseInstance
* inInstance
,
3572 const struct sockaddr
* inSockAddr
);
3574 BrowseAllRemoveIPAddress(
3575 BrowseAllContext
* inContext
,
3576 BrowseInstance
* inInstance
,
3577 const struct sockaddr
* inSockAddr
);
3578 static void BrowseDomainFree( BrowseDomain
*inDomain
);
3579 static void BrowseTypeFree( BrowseType
*inType
);
3580 static void BrowseOpFree( BrowseOp
*inBrowse
);
3581 static void BrowseInstanceFree( BrowseInstance
*inInstance
);
3582 static void BrowseIPAddrRelease( BrowseIPAddr
*inAddr
);
3583 static void BrowseIPAddrReleaseList( BrowseIPAddr
*inList
);
3585 #define ForgetIPAddressList( X ) ForgetCustom( X, BrowseIPAddrReleaseList )
3586 #define ForgetBrowseAllContext( X ) ForgetCustom( X, BrowseAllContextRelease )
3588 #define kBrowseAllOpenFileMin 4096
3590 static void BrowseAllCmd( void )
3593 BrowseAllContext
* context
= NULL
;
3595 // Check command parameters.
3597 if( gBrowseAll_BrowseTimeSecs
<= 0 )
3599 FPrintF( stdout
, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs
);
3604 #if( TARGET_OS_POSIX )
3605 // Set open file minimum.
3608 struct rlimit fdLimits
;
3610 err
= getrlimit( RLIMIT_NOFILE
, &fdLimits
);
3611 err
= map_global_noerr_errno( err
);
3612 require_noerr( err
, exit
);
3614 if( fdLimits
.rlim_cur
< kBrowseAllOpenFileMin
)
3616 fdLimits
.rlim_cur
= kBrowseAllOpenFileMin
;
3617 err
= setrlimit( RLIMIT_NOFILE
, &fdLimits
);
3618 err
= map_global_noerr_errno( err
);
3619 require_noerr( err
, exit
);
3624 context
= (BrowseAllContext
*) calloc( 1, sizeof( *context
) );
3625 require_action( context
, exit
, err
= kNoMemoryErr
);
3627 context
->refCount
= 1;
3628 context
->domain
= gBrowseAll_Domain
;
3629 context
->serviceTypes
= gBrowseAll_ServiceTypes
;
3630 context
->serviceTypesCount
= gBrowseAll_ServiceTypesCount
;
3631 gBrowseAll_ServiceTypes
= NULL
;
3632 gBrowseAll_ServiceTypesCount
= 0;
3633 context
->browseTimeSecs
= gBrowseAll_BrowseTimeSecs
;
3634 context
->maxConnectTimeSecs
= gBrowseAll_MaxConnectTimeSecs
;
3635 context
->includeAWDL
= gBrowseAll_IncludeAWDL
? true : false;
3636 #if( TARGET_OS_POSIX )
3637 context
->useColoredText
= isatty( STDOUT_FILENO
) ? true : false;
3640 err
= DNSServiceCreateConnection( &context
->mainRef
);
3641 require_noerr( err
, exit
);
3643 err
= DNSServiceSetDispatchQueue( context
->mainRef
, dispatch_get_main_queue() );
3644 require_noerr( err
, exit
);
3646 // Set interface index.
3648 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
3649 require_noerr_quiet( err
, exit
);
3651 BrowseAllPrintPrologue( context
);
3653 if( context
->domain
)
3655 err
= BrowseAllAddDomain( context
, context
->domain
);
3656 require_noerr( err
, exit
);
3660 DNSServiceRef sdRef
;
3662 sdRef
= context
->mainRef
;
3663 err
= DNSServiceQueryRecord( &sdRef
, kDNSServiceFlagsShareConnection
, kDNSServiceInterfaceIndexLocalOnly
,
3664 "b._dns-sd._udp.local.", kDNSServiceType_PTR
, kDNSServiceClass_IN
, BrowseAllQueryDomainsCallback
, context
);
3665 require_noerr( err
, exit
);
3667 context
->domainsQuery
= sdRef
;
3670 dispatch_after_f( dispatch_time_seconds( context
->browseTimeSecs
), dispatch_get_main_queue(), context
, BrowseAllStop
);
3674 if( context
) BrowseAllContextRelease( context
);
3675 if( err
) exit( 1 );
3678 //===========================================================================================================================
3679 // BrowseAllPrintPrologue
3680 //===========================================================================================================================
3682 static void BrowseAllPrintPrologue( const BrowseAllContext
*inContext
)
3685 char ifName
[ kInterfaceNameBufLen
];
3686 char time
[ kTimestampBufLen
];
3688 InterfaceIndexToName( inContext
->ifIndex
, ifName
);
3690 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, ifName
);
3691 FPrintF( stdout
, "Service types: ");
3692 if( inContext
->serviceTypesCount
> 0 )
3694 FPrintF( stdout
, "%s", inContext
->serviceTypes
[ 0 ] );
3695 for( i
= 1; i
< inContext
->serviceTypesCount
; ++i
) FPrintF( stdout
, ", %s", inContext
->serviceTypes
[ i
] );
3696 FPrintF( stdout
, "\n" );
3700 FPrintF( stdout
, "all services\n" );
3702 FPrintF( stdout
, "Domain: %s\n", inContext
->domain
? inContext
->domain
: "default domains" );
3703 FPrintF( stdout
, "Browse time: %d second%?c\n", inContext
->browseTimeSecs
, inContext
->browseTimeSecs
!= 1, 's' );
3704 FPrintF( stdout
, "Max connect time: %d second%?c\n",
3705 inContext
->maxConnectTimeSecs
, inContext
->maxConnectTimeSecs
!= 1, 's' );
3706 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
3707 FPrintF( stdout
, "---\n" );
3710 //===========================================================================================================================
3711 // BrowseAllQueryDomainsCallback
3712 //===========================================================================================================================
3714 static void DNSSD_API
3715 BrowseAllQueryDomainsCallback(
3716 DNSServiceRef inSDRef
,
3717 DNSServiceFlags inFlags
,
3718 uint32_t inInterfaceIndex
,
3719 DNSServiceErrorType inError
,
3720 const char * inFullName
,
3723 uint16_t inRDataLen
,
3724 const void * inRDataPtr
,
3729 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
3730 char domainStr
[ kDNSServiceMaxDomainName
];
3733 Unused( inInterfaceIndex
);
3734 Unused( inFullName
);
3740 require_noerr( err
, exit
);
3742 err
= DomainNameToString( inRDataPtr
, ( (const uint8_t *) inRDataPtr
) + inRDataLen
, domainStr
, NULL
);
3743 require_noerr( err
, exit
);
3745 if( inFlags
& kDNSServiceFlagsAdd
)
3747 err
= BrowseAllAddDomain( context
, domainStr
);
3748 if( err
== kDuplicateErr
) err
= kNoErr
;
3749 require_noerr( err
, exit
);
3753 err
= BrowseAllRemoveDomain( context
, domainStr
);
3754 if( err
== kNotFoundErr
) err
= kNoErr
;
3755 require_noerr( err
, exit
);
3759 if( err
) exit( 1 );
3762 //===========================================================================================================================
3763 // BrowseAllQueryCallback
3764 //===========================================================================================================================
3766 static void DNSSD_API
3767 BrowseAllQueryCallback(
3768 DNSServiceRef inSDRef
,
3769 DNSServiceFlags inFlags
,
3770 uint32_t inInterfaceIndex
,
3771 DNSServiceErrorType inError
,
3772 const char * inFullName
,
3775 uint16_t inRDataLen
,
3776 const void * inRDataPtr
,
3781 BrowseDomain
* const domain
= (BrowseDomain
*) inContext
;
3782 const uint8_t * firstLabel
;
3783 const uint8_t * secondLabel
;
3784 char * serviceTypeStr
= NULL
;
3785 const uint8_t * const end
= ( (uint8_t * ) inRDataPtr
) + inRDataLen
;
3788 Unused( inFullName
);
3794 require_noerr( err
, exit
);
3796 check( inType
== kDNSServiceType_PTR
);
3797 check( inClass
== kDNSServiceClass_IN
);
3798 require_action( inRDataLen
> 0, exit
, err
= kSizeErr
);
3800 firstLabel
= inRDataPtr
;
3801 require_action_quiet( ( firstLabel
+ 1 + firstLabel
[ 0 ] ) < end
, exit
, err
= kUnderrunErr
);
3803 secondLabel
= firstLabel
+ 1 + firstLabel
[ 0 ];
3804 require_action_quiet( ( secondLabel
+ 1 + secondLabel
[ 0 ] ) < end
, exit
, err
= kUnderrunErr
);
3806 ASPrintF( &serviceTypeStr
, "%#s.%#s", firstLabel
, secondLabel
);
3807 require_action( serviceTypeStr
, exit
, err
= kNoMemoryErr
);
3809 if( inFlags
& kDNSServiceFlagsAdd
)
3811 err
= BrowseAllAddServiceType( domain
->context
, domain
, serviceTypeStr
, inInterfaceIndex
, false );
3812 if( err
== kDuplicateErr
) err
= kNoErr
;
3813 require_noerr( err
, exit
);
3817 err
= BrowseAllRemoveServiceType( domain
->context
, domain
, serviceTypeStr
, inInterfaceIndex
);
3818 if( err
== kNotFoundErr
) err
= kNoErr
;
3819 require_noerr( err
, exit
);
3823 FreeNullSafe( serviceTypeStr
);
3826 //===========================================================================================================================
3827 // BrowseAllBrowseCallback
3828 //===========================================================================================================================
3830 static void DNSSD_API
3831 BrowseAllBrowseCallback(
3832 DNSServiceRef inSDRef
,
3833 DNSServiceFlags inFlags
,
3834 uint32_t inInterfaceIndex
,
3835 DNSServiceErrorType inError
,
3836 const char * inName
,
3837 const char * inRegType
,
3838 const char * inDomain
,
3842 BrowseOp
* const browse
= (BrowseOp
*) inContext
;
3847 require_noerr( err
, exit
);
3849 if( inFlags
& kDNSServiceFlagsAdd
)
3851 err
= BrowseAllAddServiceInstance( browse
->context
, browse
, inName
, inRegType
, inDomain
, inInterfaceIndex
);
3852 if( err
== kDuplicateErr
) err
= kNoErr
;
3853 require_noerr( err
, exit
);
3857 err
= BrowseAllRemoveServiceInstance( browse
->context
, browse
, inName
, inInterfaceIndex
);
3858 if( err
== kNotFoundErr
) err
= kNoErr
;
3859 require_noerr( err
, exit
);
3866 //===========================================================================================================================
3867 // BrowseAllResolveCallback
3868 //===========================================================================================================================
3870 static void DNSSD_API
3871 BrowseAllResolveCallback(
3872 DNSServiceRef inSDRef
,
3873 DNSServiceFlags inFlags
,
3874 uint32_t inInterfaceIndex
,
3875 DNSServiceErrorType inError
,
3876 const char * inFullName
,
3877 const char * inHostname
,
3880 const unsigned char * inTXTPtr
,
3884 const uint64_t nowTicks
= UpTicks();
3885 BrowseInstance
* const instance
= (BrowseInstance
*) inContext
;
3889 Unused( inInterfaceIndex
);
3890 Unused( inFullName
);
3893 require_noerr( err
, exit
);
3895 if( !MemEqual( instance
->txtPtr
, instance
->txtLen
, inTXTPtr
, inTXTLen
) )
3897 FreeNullSafe( instance
->txtPtr
);
3898 instance
->txtPtr
= malloc( inTXTLen
);
3899 require_action( instance
->txtPtr
, exit
, err
= kNoMemoryErr
);
3901 memcpy( instance
->txtPtr
, inTXTPtr
, inTXTLen
);
3902 instance
->txtLen
= inTXTLen
;
3905 instance
->port
= ntohs( inPort
);
3907 if( !instance
->hostname
|| ( strcasecmp( instance
->hostname
, inHostname
) != 0 ) )
3909 DNSServiceRef sdRef
;
3911 if( !instance
->hostname
) instance
->resolveDoneTicks
= nowTicks
;
3912 FreeNullSafe( instance
->hostname
);
3913 instance
->hostname
= strdup( inHostname
);
3914 require_action( instance
->hostname
, exit
, err
= kNoMemoryErr
);
3916 DNSServiceForget( &instance
->getAddr
);
3917 ForgetIPAddressList( &instance
->addrList
);
3919 sdRef
= instance
->context
->mainRef
;
3920 instance
->getAddrStartTicks
= UpTicks();
3921 err
= DNSServiceGetAddrInfo( &sdRef
, kDNSServiceFlagsShareConnection
, instance
->ifIndex
,
3922 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, instance
->hostname
, BrowseAllGAICallback
, instance
);
3923 require_noerr( err
, exit
);
3925 instance
->getAddr
= sdRef
;
3929 if( err
) exit( 1 );
3932 //===========================================================================================================================
3933 // BrowseAllGAICallback
3934 //===========================================================================================================================
3936 static void DNSSD_API
3937 BrowseAllGAICallback(
3938 DNSServiceRef inSDRef
,
3939 DNSServiceFlags inFlags
,
3940 uint32_t inInterfaceIndex
,
3941 DNSServiceErrorType inError
,
3942 const char * inHostname
,
3943 const struct sockaddr
* inSockAddr
,
3948 BrowseInstance
* const instance
= (BrowseInstance
*) inContext
;
3951 Unused( inInterfaceIndex
);
3952 Unused( inHostname
);
3956 require_noerr( err
, exit
);
3958 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
3960 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
3964 if( inFlags
& kDNSServiceFlagsAdd
)
3966 err
= BrowseAllAddIPAddress( instance
->context
, instance
, inSockAddr
);
3967 if( err
== kDuplicateErr
) err
= kNoErr
;
3968 require_noerr( err
, exit
);
3972 err
= BrowseAllRemoveIPAddress( instance
->context
, instance
, inSockAddr
);
3973 if( err
== kNotFoundErr
) err
= kNoErr
;
3974 require_noerr( err
, exit
);
3981 //===========================================================================================================================
3982 // BrowseAllConnectionProgress
3983 //===========================================================================================================================
3985 static void BrowseAllConnectionProgress( int inPhase
, const void *inDetails
, void *inArg
)
3987 BrowseIPAddr
* const addr
= (BrowseIPAddr
*) inArg
;
3989 if( inPhase
== kAsyncConnectionPhase_Connected
)
3991 const AsyncConnectedInfo
* const info
= (AsyncConnectedInfo
*) inDetails
;
3993 addr
->connectTimeSecs
= info
->connectSecs
;
3997 //===========================================================================================================================
3998 // BrowseAllConnectionHandler
3999 //===========================================================================================================================
4001 static void BrowseAllConnectionHandler( SocketRef inSock
, OSStatus inError
, void *inArg
)
4003 BrowseIPAddr
* const addr
= (BrowseIPAddr
*) inArg
;
4004 BrowseAllContext
* const context
= addr
->context
;
4008 addr
->connectStatus
= kConnectStatus_Failed
;
4009 addr
->connectError
= inError
;
4013 addr
->connectStatus
= kConnectStatus_Succeeded
;
4016 check( context
->pendingConnectCount
> 0 );
4017 if( --context
->pendingConnectCount
== 0 )
4019 if( context
->exitTimer
)
4021 dispatch_source_forget( &context
->exitTimer
);
4022 dispatch_async_f( dispatch_get_main_queue(), context
, BrowseAllExit
);
4026 ForgetSocket( &inSock
);
4027 BrowseIPAddrRelease( addr
);
4030 //===========================================================================================================================
4032 //===========================================================================================================================
4034 static void BrowseAllStop( void *inContext
)
4037 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
4038 BrowseDomain
* domain
;
4041 BrowseInstance
* instance
;
4043 DNSServiceForget( &context
->domainsQuery
);
4044 for( domain
= context
->domainList
; domain
; domain
= domain
->next
)
4046 DNSServiceForget( &domain
->servicesQuery
);
4047 for( type
= domain
->typeList
; type
; type
= type
->next
)
4049 for( browse
= type
->browseList
; browse
; browse
= browse
->next
)
4051 DNSServiceForget( &browse
->browse
);
4052 for( instance
= browse
->instanceList
; instance
; instance
= instance
->next
)
4054 DNSServiceForget( &instance
->resolve
);
4055 DNSServiceForget( &instance
->getAddr
);
4060 DNSServiceForget( &context
->mainRef
);
4062 if( ( context
->pendingConnectCount
> 0 ) && ( context
->maxConnectTimeSecs
> 0 ) )
4064 check( !context
->exitTimer
);
4065 err
= DispatchTimerCreate( dispatch_time_seconds( context
->maxConnectTimeSecs
), DISPATCH_TIME_FOREVER
,
4066 100 * kNanosecondsPerMillisecond
, BrowseAllExit
, NULL
, context
, &context
->exitTimer
);
4067 require_noerr( err
, exit
);
4068 dispatch_resume( context
->exitTimer
);
4072 dispatch_async_f( dispatch_get_main_queue(), context
, BrowseAllExit
);
4077 if( err
) exit( 1 );
4080 //===========================================================================================================================
4082 //===========================================================================================================================
4084 #define kStatusStr_CouldConnect "connected"
4085 #define kStatusStr_CouldConnectColored kANSIGreen kStatusStr_CouldConnect kANSINormal
4086 #define kStatusStr_CouldNotConnect "could not connect"
4087 #define kStatusStr_CouldNotConnectColored kANSIRed kStatusStr_CouldNotConnect kANSINormal
4088 #define kStatusStr_NoConnectionAttempted "no connection attempted"
4089 #define kStatusStr_Unknown "unknown"
4091 #define Indent( X ) ( (X) * 4 ), ""
4093 static void BrowseAllExit( void *inContext
)
4095 BrowseAllContext
* const context
= (BrowseAllContext
*) inContext
;
4096 BrowseDomain
* domain
;
4099 BrowseInstance
* instance
;
4100 BrowseIPAddr
* addr
;
4102 dispatch_source_forget( &context
->exitTimer
);
4104 for( domain
= context
->domainList
; domain
; domain
= domain
->next
)
4106 FPrintF( stdout
, "%s\n\n", domain
->name
);
4108 for( type
= domain
->typeList
; type
; type
= type
->next
)
4112 desc
= ServiceTypeDescription( type
->name
);
4113 if( desc
) FPrintF( stdout
, "%*s" "%s (%s)\n\n", Indent( 1 ), desc
, type
->name
);
4114 else FPrintF( stdout
, "%*s" "%s\n\n", Indent( 1 ), type
->name
);
4116 for( browse
= type
->browseList
; browse
; browse
= browse
->next
)
4118 for( instance
= browse
->instanceList
; instance
; instance
= instance
->next
)
4120 char ifname
[ IF_NAMESIZE
+ 1 ];
4122 FPrintF( stdout
, "%*s" "%s via ", Indent( 2 ), instance
->name
);
4123 if( instance
->ifIndex
== 0 )
4125 FPrintF( stdout
, "the Internet" );
4127 else if( if_indextoname( instance
->ifIndex
, ifname
) )
4129 NetTransportType netType
;
4131 SocketGetInterfaceInfo( kInvalidSocketRef
, ifname
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
4133 FPrintF( stdout
, "%s (%s)",
4134 ( netType
== kNetTransportType_Ethernet
) ? "ethernet" : NetTransportTypeToString( netType
),
4139 FPrintF( stdout
, "interface index %u", instance
->ifIndex
);
4141 FPrintF( stdout
, "\n\n" );
4143 if( instance
->hostname
)
4147 SNPrintF( buffer
, sizeof( buffer
), "%s:%u", instance
->hostname
, instance
->port
);
4148 FPrintF( stdout
, "%*s" "%-51s %4llu ms\n", Indent( 3 ), buffer
,
4149 UpTicksToMilliseconds( instance
->resolveDoneTicks
- instance
->resolveStartTicks
) );
4153 FPrintF( stdout
, "%*s" "%s:%u\n", Indent( 3 ), instance
->hostname
, instance
->port
);
4156 for( addr
= instance
->addrList
; addr
; addr
= addr
->next
)
4158 AsyncConnection_Forget( &addr
->connection
);
4160 if( addr
->connectStatus
== kConnectStatus_Pending
)
4162 addr
->connectStatus
= kConnectStatus_Failed
;
4163 addr
->connectError
= kTimeoutErr
;
4166 FPrintF( stdout
, "%*s" "%-##47a %4llu ms", Indent( 4 ),
4167 &addr
->sip
.sa
, UpTicksToMilliseconds( addr
->foundTicks
- instance
->getAddrStartTicks
) );
4168 if( context
->maxConnectTimeSecs
<= 0 )
4170 FPrintF( stdout
, "\n" );
4173 switch( addr
->connectStatus
)
4175 case kConnectStatus_None
:
4176 FPrintF( stdout
, " (%s)\n", kStatusStr_NoConnectionAttempted
);
4179 case kConnectStatus_Succeeded
:
4180 FPrintF( stdout
, " (%s in %.2f ms)\n",
4181 context
->useColoredText
? kStatusStr_CouldConnectColored
: kStatusStr_CouldConnect
,
4182 addr
->connectTimeSecs
* 1000 );
4185 case kConnectStatus_Failed
:
4186 FPrintF( stdout
, " (%s: %m)\n",
4187 context
->useColoredText
? kStatusStr_CouldNotConnectColored
: kStatusStr_CouldNotConnect
,
4188 addr
->connectError
);
4192 FPrintF( stdout
, " (%s)\n", kStatusStr_Unknown
);
4197 FPrintF( stdout
, "\n" );
4198 if( instance
->txtLen
== 0 ) continue;
4200 FPrintF( stdout
, "%*s" "TXT record:\n", Indent( 3 ) );
4201 if( instance
->txtLen
> 1 )
4203 FPrintF( stdout
, "%3{txt}", instance
->txtPtr
, instance
->txtLen
);
4207 FPrintF( stdout
, "%*s" "%#H\n", Indent( 3 ), instance
->txtPtr
, (int) instance
->txtLen
, INT_MAX
);
4209 FPrintF( stdout
, "\n" );
4212 FPrintF( stdout
, "\n" );
4216 while( ( domain
= context
->domainList
) != NULL
)
4218 context
->domainList
= domain
->next
;
4219 BrowseDomainFree( domain
);
4222 BrowseAllContextRelease( context
);
4226 //===========================================================================================================================
4227 // BrowseAllAddDomain
4228 //===========================================================================================================================
4230 static OSStatus
BrowseAllAddDomain( BrowseAllContext
*inContext
, const char *inName
)
4233 BrowseDomain
* domain
;
4235 BrowseDomain
* newDomain
= NULL
;
4237 for( p
= &inContext
->domainList
; ( domain
= *p
) != NULL
; p
= &domain
->next
)
4239 if( strcasecmp( domain
->name
, inName
) == 0 ) break;
4241 require_action_quiet( !domain
, exit
, err
= kDuplicateErr
);
4243 newDomain
= (BrowseDomain
*) calloc( 1, sizeof( *newDomain
) );
4244 require_action( newDomain
, exit
, err
= kNoMemoryErr
);
4246 ++inContext
->refCount
;
4247 newDomain
->context
= inContext
;
4249 newDomain
->name
= strdup( inName
);
4250 require_action( newDomain
->name
, exit
, err
= kNoMemoryErr
);
4252 if( inContext
->serviceTypesCount
> 0 )
4256 for( i
= 0; i
< inContext
->serviceTypesCount
; ++i
)
4258 err
= BrowseAllAddServiceType( inContext
, newDomain
, inContext
->serviceTypes
[ i
], inContext
->ifIndex
,
4259 inContext
->includeAWDL
);
4260 if( err
== kDuplicateErr
) err
= kNoErr
;
4261 require_noerr( err
, exit
);
4267 DNSServiceFlags flags
;
4268 DNSServiceRef sdRef
;
4270 ASPrintF( &recordName
, "_services._dns-sd._udp.%s", newDomain
->name
);
4271 require_action( recordName
, exit
, err
= kNoMemoryErr
);
4273 flags
= kDNSServiceFlagsShareConnection
;
4274 if( inContext
->includeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
4276 sdRef
= newDomain
->context
->mainRef
;
4277 err
= DNSServiceQueryRecord( &sdRef
, flags
, inContext
->ifIndex
, recordName
, kDNSServiceType_PTR
, kDNSServiceClass_IN
,
4278 BrowseAllQueryCallback
, newDomain
);
4280 require_noerr( err
, exit
);
4282 newDomain
->servicesQuery
= sdRef
;
4290 if( newDomain
) BrowseDomainFree( newDomain
);
4294 //===========================================================================================================================
4295 // BrowseAllRemoveDomain
4296 //===========================================================================================================================
4298 static OSStatus
BrowseAllRemoveDomain( BrowseAllContext
*inContext
, const char *inName
)
4301 BrowseDomain
* domain
;
4304 for( p
= &inContext
->domainList
; ( domain
= *p
) != NULL
; p
= &domain
->next
)
4306 if( strcasecmp( domain
->name
, inName
) == 0 ) break;
4312 BrowseDomainFree( domain
);
4323 //===========================================================================================================================
4324 // BrowseAllContextRelease
4325 //===========================================================================================================================
4327 static void BrowseAllContextRelease( BrowseAllContext
*inContext
)
4329 if( --inContext
->refCount
== 0 )
4331 check( !inContext
->domainsQuery
);
4332 check( !inContext
->domainList
);
4333 check( !inContext
->exitTimer
);
4334 check( !inContext
->pendingConnectCount
);
4335 DNSServiceForget( &inContext
->mainRef
);
4336 if( inContext
->serviceTypes
)
4338 StringArray_Free( inContext
->serviceTypes
, inContext
->serviceTypesCount
);
4339 inContext
->serviceTypes
= NULL
;
4340 inContext
->serviceTypesCount
= 0;
4346 //===========================================================================================================================
4347 // BrowseAllAddServiceType
4348 //===========================================================================================================================
4351 BrowseAllAddServiceType(
4352 BrowseAllContext
* inContext
,
4353 BrowseDomain
* inDomain
,
4354 const char * inName
,
4356 Boolean inIncludeAWDL
)
4359 DNSServiceRef sdRef
;
4360 DNSServiceFlags flags
;
4362 BrowseType
** typePtr
;
4363 BrowseType
* newType
= NULL
;
4365 BrowseOp
** browsePtr
;
4366 BrowseOp
* newBrowse
= NULL
;
4368 for( typePtr
= &inDomain
->typeList
; ( type
= *typePtr
) != NULL
; typePtr
= &type
->next
)
4370 if( strcasecmp( type
->name
, inName
) == 0 ) break;
4374 newType
= (BrowseType
*) calloc( 1, sizeof( *newType
) );
4375 require_action( newType
, exit
, err
= kNoMemoryErr
);
4377 newType
->name
= strdup( inName
);
4378 require_action( newType
->name
, exit
, err
= kNoMemoryErr
);
4383 for( browsePtr
= &type
->browseList
; ( browse
= *browsePtr
) != NULL
; browsePtr
= &browse
->next
)
4385 if( browse
->ifIndex
== inIfIndex
) break;
4387 require_action_quiet( !browse
, exit
, err
= kDuplicateErr
);
4389 newBrowse
= (BrowseOp
*) calloc( 1, sizeof( *newBrowse
) );
4390 require_action( newBrowse
, exit
, err
= kNoMemoryErr
);
4392 ++inContext
->refCount
;
4393 newBrowse
->context
= inContext
;
4394 newBrowse
->ifIndex
= inIfIndex
;
4395 if( stricmp_suffix( inName
, "._tcp" ) == 0 ) newBrowse
->isTCP
= true;
4397 flags
= kDNSServiceFlagsShareConnection
;
4398 if( inIncludeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
4400 newBrowse
->startTicks
= UpTicks();
4402 sdRef
= inContext
->mainRef
;
4403 err
= DNSServiceBrowse( &sdRef
, flags
, newBrowse
->ifIndex
, type
->name
, inDomain
->name
, BrowseAllBrowseCallback
,
4405 require_noerr( err
, exit
);
4407 newBrowse
->browse
= sdRef
;
4408 *browsePtr
= newBrowse
;
4418 if( newBrowse
) BrowseOpFree( newBrowse
);
4419 if( newType
) BrowseTypeFree( newType
);
4423 //===========================================================================================================================
4424 // BrowseAllRemoveServiceType
4425 //===========================================================================================================================
4428 BrowseAllRemoveServiceType(
4429 BrowseAllContext
* inContext
,
4430 BrowseDomain
* inDomain
,
4431 const char * inName
,
4432 uint32_t inIfIndex
)
4436 BrowseType
** typePtr
;
4438 BrowseOp
** browsePtr
;
4440 Unused( inContext
);
4442 for( typePtr
= &inDomain
->typeList
; ( type
= *typePtr
) != NULL
; typePtr
= &type
->next
)
4444 if( strcasecmp( type
->name
, inName
) == 0 ) break;
4446 require_action_quiet( type
, exit
, err
= kNotFoundErr
);
4448 for( browsePtr
= &type
->browseList
; ( browse
= *browsePtr
) != NULL
; browsePtr
= &browse
->next
)
4450 if( browse
->ifIndex
== inIfIndex
) break;
4452 require_action_quiet( browse
, exit
, err
= kNotFoundErr
);
4454 *browsePtr
= browse
->next
;
4455 BrowseOpFree( browse
);
4456 if( !type
->browseList
)
4458 *typePtr
= type
->next
;
4459 BrowseTypeFree( type
);
4467 //===========================================================================================================================
4468 // BrowseAllAddServiceInstance
4469 //===========================================================================================================================
4472 BrowseAllAddServiceInstance(
4473 BrowseAllContext
* inContext
,
4474 BrowseOp
* inBrowse
,
4475 const char * inName
,
4476 const char * inRegType
,
4477 const char * inDomain
,
4478 uint32_t inIfIndex
)
4481 DNSServiceRef sdRef
;
4482 BrowseInstance
* instance
;
4483 BrowseInstance
** p
;
4484 const uint64_t nowTicks
= UpTicks();
4485 BrowseInstance
* newInstance
= NULL
;
4487 for( p
= &inBrowse
->instanceList
; ( instance
= *p
) != NULL
; p
= &instance
->next
)
4489 if( ( instance
->ifIndex
== inIfIndex
) && ( strcasecmp( instance
->name
, inName
) == 0 ) ) break;
4491 require_action_quiet( !instance
, exit
, err
= kDuplicateErr
);
4493 newInstance
= (BrowseInstance
*) calloc( 1, sizeof( *newInstance
) );
4494 require_action( newInstance
, exit
, err
= kNoMemoryErr
);
4496 ++inContext
->refCount
;
4497 newInstance
->context
= inContext
;
4498 newInstance
->foundTicks
= nowTicks
;
4499 newInstance
->ifIndex
= inIfIndex
;
4500 newInstance
->isTCP
= inBrowse
->isTCP
;
4502 newInstance
->name
= strdup( inName
);
4503 require_action( newInstance
->name
, exit
, err
= kNoMemoryErr
);
4505 sdRef
= inContext
->mainRef
;
4506 newInstance
->resolveStartTicks
= UpTicks();
4507 err
= DNSServiceResolve( &sdRef
, kDNSServiceFlagsShareConnection
, newInstance
->ifIndex
, inName
, inRegType
, inDomain
,
4508 BrowseAllResolveCallback
, newInstance
);
4509 require_noerr( err
, exit
);
4511 newInstance
->resolve
= sdRef
;
4516 if( newInstance
) BrowseInstanceFree( newInstance
);
4520 //===========================================================================================================================
4521 // BrowseAllRemoveServiceInstance
4522 //===========================================================================================================================
4525 BrowseAllRemoveServiceInstance(
4526 BrowseAllContext
* inContext
,
4527 BrowseOp
* inBrowse
,
4528 const char * inName
,
4529 uint32_t inIfIndex
)
4532 BrowseInstance
* instance
;
4533 BrowseInstance
** p
;
4535 Unused( inContext
);
4537 for( p
= &inBrowse
->instanceList
; ( instance
= *p
) != NULL
; p
= &instance
->next
)
4539 if( ( instance
->ifIndex
== inIfIndex
) && ( strcasecmp( instance
->name
, inName
) == 0 ) ) break;
4541 require_action_quiet( instance
, exit
, err
= kNotFoundErr
);
4543 *p
= instance
->next
;
4544 BrowseInstanceFree( instance
);
4551 //===========================================================================================================================
4552 // BrowseAllAddIPAddress
4553 //===========================================================================================================================
4555 #define kDiscardProtocolPort 9
4558 BrowseAllAddIPAddress(
4559 BrowseAllContext
* inContext
,
4560 BrowseInstance
* inInstance
,
4561 const struct sockaddr
* inSockAddr
)
4564 BrowseIPAddr
* addr
;
4566 const uint64_t nowTicks
= UpTicks();
4567 BrowseIPAddr
* newAddr
= NULL
;
4569 if( ( inSockAddr
->sa_family
!= AF_INET
) && ( inSockAddr
->sa_family
!= AF_INET6
) )
4571 dlogassert( "Unexpected address family: %d", inSockAddr
->sa_family
);
4576 for( p
= &inInstance
->addrList
; ( addr
= *p
) != NULL
; p
= &addr
->next
)
4578 if( SockAddrCompareAddr( &addr
->sip
, inSockAddr
) == 0 ) break;
4580 require_action_quiet( !addr
, exit
, err
= kDuplicateErr
);
4582 newAddr
= (BrowseIPAddr
*) calloc( 1, sizeof( *newAddr
) );
4583 require_action( newAddr
, exit
, err
= kNoMemoryErr
);
4585 ++inContext
->refCount
;
4586 newAddr
->refCount
= 1;
4587 newAddr
->context
= inContext
;
4588 newAddr
->foundTicks
= nowTicks
;
4589 SockAddrCopy( inSockAddr
, &newAddr
->sip
.sa
);
4591 if( ( inContext
->maxConnectTimeSecs
> 0 ) && inInstance
->isTCP
&& ( inInstance
->port
!= kDiscardProtocolPort
) )
4593 char destination
[ kSockAddrStringMaxSize
];
4595 err
= SockAddrToString( &newAddr
->sip
, kSockAddrStringFlagsNoPort
, destination
);
4596 require_noerr( err
, exit
);
4598 err
= AsyncConnection_Connect( &newAddr
->connection
, destination
, -inInstance
->port
, kAsyncConnectionFlag_P2P
,
4599 kAsyncConnectionNoTimeout
, kSocketBufferSize_DontSet
, kSocketBufferSize_DontSet
,
4600 BrowseAllConnectionProgress
, newAddr
, BrowseAllConnectionHandler
, newAddr
, dispatch_get_main_queue() );
4601 require_noerr( err
, exit
);
4603 ++newAddr
->refCount
;
4604 newAddr
->connectStatus
= kConnectStatus_Pending
;
4605 ++inContext
->pendingConnectCount
;
4613 if( newAddr
) BrowseIPAddrRelease( newAddr
);
4617 //===========================================================================================================================
4618 // BrowseAllRemoveIPAddress
4619 //===========================================================================================================================
4622 BrowseAllRemoveIPAddress(
4623 BrowseAllContext
* inContext
,
4624 BrowseInstance
* inInstance
,
4625 const struct sockaddr
* inSockAddr
)
4628 BrowseIPAddr
* addr
;
4631 Unused( inContext
);
4633 for( p
= &inInstance
->addrList
; ( addr
= *p
) != NULL
; p
= &addr
->next
)
4635 if( SockAddrCompareAddr( &addr
->sip
.sa
, inSockAddr
) == 0 ) break;
4637 require_action_quiet( addr
, exit
, err
= kNotFoundErr
);
4640 BrowseIPAddrRelease( addr
);
4647 //===========================================================================================================================
4649 //===========================================================================================================================
4651 static void BrowseDomainFree( BrowseDomain
*inDomain
)
4655 ForgetBrowseAllContext( &inDomain
->context
);
4656 ForgetMem( &inDomain
->name
);
4657 DNSServiceForget( &inDomain
->servicesQuery
);
4658 while( ( type
= inDomain
->typeList
) != NULL
)
4660 inDomain
->typeList
= type
->next
;
4661 BrowseTypeFree( type
);
4666 //===========================================================================================================================
4668 //===========================================================================================================================
4670 static void BrowseTypeFree( BrowseType
*inType
)
4674 ForgetMem( &inType
->name
);
4675 while( ( browse
= inType
->browseList
) != NULL
)
4677 inType
->browseList
= browse
->next
;
4678 BrowseOpFree( browse
);
4683 //===========================================================================================================================
4685 //===========================================================================================================================
4687 static void BrowseOpFree( BrowseOp
*inBrowse
)
4689 BrowseInstance
* instance
;
4691 ForgetBrowseAllContext( &inBrowse
->context
);
4692 DNSServiceForget( &inBrowse
->browse
);
4693 while( ( instance
= inBrowse
->instanceList
) != NULL
)
4695 inBrowse
->instanceList
= instance
->next
;
4696 BrowseInstanceFree( instance
);
4701 //===========================================================================================================================
4702 // BrowseInstanceFree
4703 //===========================================================================================================================
4705 static void BrowseInstanceFree( BrowseInstance
*inInstance
)
4707 ForgetBrowseAllContext( &inInstance
->context
);
4708 ForgetMem( &inInstance
->name
);
4709 DNSServiceForget( &inInstance
->resolve
);
4710 DNSServiceForget( &inInstance
->getAddr
);
4711 ForgetMem( &inInstance
->txtPtr
);
4712 ForgetMem( &inInstance
->hostname
);
4713 ForgetIPAddressList( &inInstance
->addrList
);
4717 //===========================================================================================================================
4718 // BrowseIPAddrRelease
4719 //===========================================================================================================================
4721 static void BrowseIPAddrRelease( BrowseIPAddr
*inAddr
)
4723 AsyncConnection_Forget( &inAddr
->connection
);
4724 if( --inAddr
->refCount
== 0 )
4726 ForgetBrowseAllContext( &inAddr
->context
);
4731 //===========================================================================================================================
4732 // BrowseIPAddrReleaseList
4733 //===========================================================================================================================
4735 static void BrowseIPAddrReleaseList( BrowseIPAddr
*inList
)
4737 BrowseIPAddr
* addr
;
4739 while( ( addr
= inList
) != NULL
)
4741 inList
= addr
->next
;
4742 BrowseIPAddrRelease( addr
);
4746 //===========================================================================================================================
4747 // GetAddrInfoStressCmd
4748 //===========================================================================================================================
4752 DNSServiceRef mainRef
;
4753 DNSServiceRef sdRef
;
4754 DNSServiceFlags flags
;
4755 unsigned int interfaceIndex
;
4756 unsigned int connectionNumber
;
4757 unsigned int requestCount
;
4758 unsigned int requestCountMax
;
4759 unsigned int requestCountLimit
;
4760 unsigned int durationMinMs
;
4761 unsigned int durationMaxMs
;
4765 static void GetAddrInfoStressEvent( void *inContext
);
4766 static void DNSSD_API
4767 GetAddrInfoStressCallback(
4768 DNSServiceRef inSDRef
,
4769 DNSServiceFlags inFlags
,
4770 uint32_t inInterfaceIndex
,
4771 DNSServiceErrorType inError
,
4772 const char * inHostname
,
4773 const struct sockaddr
* inSockAddr
,
4777 static void GetAddrInfoStressCmd( void )
4780 GAIStressContext
* context
= NULL
;
4782 DNSServiceFlags flags
;
4784 char ifName
[ kInterfaceNameBufLen
];
4785 char time
[ kTimestampBufLen
];
4787 if( gGAIStress_TestDurationSecs
< 0 )
4789 FPrintF( stdout
, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs
);
4793 if( gGAIStress_ConnectionCount
<= 0 )
4795 FPrintF( stdout
, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount
);
4799 if( gGAIStress_DurationMinMs
<= 0 )
4801 FPrintF( stdout
, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs
);
4805 if( gGAIStress_DurationMaxMs
<= 0 )
4807 FPrintF( stdout
, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs
);
4811 if( gGAIStress_DurationMinMs
> gGAIStress_DurationMaxMs
)
4813 FPrintF( stdout
, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
4814 gGAIStress_DurationMinMs
, gGAIStress_DurationMaxMs
);
4818 if( gGAIStress_RequestCountMax
<= 0 )
4820 FPrintF( stdout
, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax
);
4827 flags
= GetDNSSDFlagsFromOpts();
4829 // Set interface index.
4831 err
= InterfaceIndexFromArgString( gInterface
, &ifIndex
);
4832 require_noerr_quiet( err
, exit
);
4834 for( i
= 0; i
< gGAIStress_ConnectionCount
; ++i
)
4836 context
= (GAIStressContext
*) calloc( 1, sizeof( *context
) );
4837 require_action( context
, exit
, err
= kNoMemoryErr
);
4839 context
->flags
= flags
;
4840 context
->interfaceIndex
= ifIndex
;
4841 context
->connectionNumber
= (unsigned int)( i
+ 1 );
4842 context
->requestCountMax
= (unsigned int) gGAIStress_RequestCountMax
;
4843 context
->durationMinMs
= (unsigned int) gGAIStress_DurationMinMs
;
4844 context
->durationMaxMs
= (unsigned int) gGAIStress_DurationMaxMs
;
4846 dispatch_async_f( dispatch_get_main_queue(), context
, GetAddrInfoStressEvent
);
4850 if( gGAIStress_TestDurationSecs
> 0 )
4852 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs
), dispatch_get_main_queue(), NULL
, Exit
);
4855 FPrintF( stdout
, "Flags: %#{flags}\n", flags
, kDNSServiceFlagsDescriptors
);
4856 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) ifIndex
, InterfaceIndexToName( ifIndex
, ifName
) );
4857 FPrintF( stdout
, "Test duration: " );
4858 if( gGAIStress_TestDurationSecs
== 0 )
4860 FPrintF( stdout
, "∞\n" );
4864 FPrintF( stdout
, "%d s\n", gGAIStress_TestDurationSecs
);
4866 FPrintF( stdout
, "Connection count: %d\n", gGAIStress_ConnectionCount
);
4867 FPrintF( stdout
, "Request duration min: %d ms\n", gGAIStress_DurationMinMs
);
4868 FPrintF( stdout
, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs
);
4869 FPrintF( stdout
, "Request count max: %d\n", gGAIStress_RequestCountMax
);
4870 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
4871 FPrintF( stdout
, "---\n" );
4876 FreeNullSafe( context
);
4877 if( err
) exit( 1 );
4880 //===========================================================================================================================
4881 // GetAddrInfoStressEvent
4882 //===========================================================================================================================
4884 #define kStressRandStrLen 5
4886 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
4888 static void GetAddrInfoStressEvent( void *inContext
)
4890 GAIStressContext
* const context
= (GAIStressContext
*) inContext
;
4892 DNSServiceRef sdRef
;
4893 unsigned int nextMs
;
4894 char randomStr
[ kStressRandStrLen
+ 1 ];
4895 char hostname
[ kStressRandStrLen
+ 4 + 1 ];
4896 char time
[ kTimestampBufLen
];
4897 Boolean isConnectionNew
= false;
4898 static Boolean printedHeader
= false;
4900 if( !context
->mainRef
|| ( context
->requestCount
>= context
->requestCountLimit
) )
4902 DNSServiceForget( &context
->mainRef
);
4903 context
->sdRef
= NULL
;
4904 context
->requestCount
= 0;
4905 context
->requestCountLimit
= RandomRange( 1, context
->requestCountMax
);
4907 err
= DNSServiceCreateConnection( &context
->mainRef
);
4908 require_noerr( err
, exit
);
4910 err
= DNSServiceSetDispatchQueue( context
->mainRef
, dispatch_get_main_queue() );
4911 require_noerr( err
, exit
);
4913 isConnectionNew
= true;
4916 RandomString( kLowercaseAlphaCharSet
, sizeof_string( kLowercaseAlphaCharSet
), 2, kStressRandStrLen
, randomStr
);
4917 SNPrintF( hostname
, sizeof( hostname
), "%s.com", randomStr
);
4919 nextMs
= RandomRange( context
->durationMinMs
, context
->durationMaxMs
);
4921 if( !printedHeader
)
4923 FPrintF( stdout
, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
4924 printedHeader
= true;
4926 FPrintF( stdout
, "%-26s %3u%c %9s %8u\n",
4927 GetTimestampStr( time
), context
->connectionNumber
, isConnectionNew
? '*': ' ', hostname
, nextMs
);
4929 DNSServiceForget( &context
->sdRef
);
4930 sdRef
= context
->mainRef
;
4931 err
= DNSServiceGetAddrInfo( &sdRef
, context
->flags
| kDNSServiceFlagsShareConnection
, context
->interfaceIndex
,
4932 kDNSServiceProtocol_IPv4
| kDNSServiceProtocol_IPv6
, hostname
, GetAddrInfoStressCallback
, NULL
);
4933 require_noerr( err
, exit
);
4934 context
->sdRef
= sdRef
;
4936 context
->requestCount
++;
4938 dispatch_after_f( dispatch_time_milliseconds( nextMs
), dispatch_get_main_queue(), context
, GetAddrInfoStressEvent
);
4941 if( err
) exit( 1 );
4944 //===========================================================================================================================
4945 // GetAddrInfoStressCallback
4946 //===========================================================================================================================
4948 static void DNSSD_API
4949 GetAddrInfoStressCallback(
4950 DNSServiceRef inSDRef
,
4951 DNSServiceFlags inFlags
,
4952 uint32_t inInterfaceIndex
,
4953 DNSServiceErrorType inError
,
4954 const char * inHostname
,
4955 const struct sockaddr
* inSockAddr
,
4961 Unused( inInterfaceIndex
);
4963 Unused( inHostname
);
4964 Unused( inSockAddr
);
4966 Unused( inContext
);
4969 //===========================================================================================================================
4971 //===========================================================================================================================
4977 sockaddr_ip serverAddr
;
4983 dispatch_source_t readSource
;
4990 Boolean printRawRData
; // True if RDATA results are not to be formatted.
4991 uint8_t msgBuf
[ 512 ];
4995 static void DNSQueryPrintPrologue( const DNSQueryContext
*inContext
);
4996 static void DNSQueryReadHandler( void *inContext
);
4997 static void DNSQueryCancelHandler( void *inContext
);
4999 static void DNSQueryCmd( void )
5002 DNSQueryContext
* context
= NULL
;
5004 size_t msgLen
, sendLen
;
5006 // Check command parameters.
5008 if( gDNSQuery_TimeLimitSecs
< -1 )
5010 FPrintF( stdout
, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs
);
5014 if( ( gDNSQuery_Flags
< INT16_MIN
) || ( gDNSQuery_Flags
> UINT16_MAX
) )
5016 FPrintF( stdout
, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags
);
5023 context
= (DNSQueryContext
*) calloc( 1, sizeof( *context
) );
5024 require_action( context
, exit
, err
= kNoMemoryErr
);
5026 context
->name
= gDNSQuery_Name
;
5027 context
->sock
= kInvalidSocketRef
;
5028 context
->timeLimitSecs
= gDNSQuery_TimeLimitSecs
;
5029 context
->queryID
= (uint16_t) Random32();
5030 context
->useTCP
= gDNSQuery_UseTCP
? true : false;
5031 context
->printRawRData
= gDNSQuery_RawRData
? true : false;
5033 #if( TARGET_OS_DARWIN )
5034 if( gDNSQuery_Server
)
5037 err
= StringToSockAddr( gDNSQuery_Server
, &context
->serverAddr
, sizeof( context
->serverAddr
), NULL
);
5038 require_noerr( err
, exit
);
5040 #if( TARGET_OS_DARWIN )
5043 err
= GetDefaultDNSServer( &context
->serverAddr
);
5044 require_noerr( err
, exit
);
5047 if( SockAddrGetPort( &context
->serverAddr
) == 0 ) SockAddrSetPort( &context
->serverAddr
, kDNSPort
);
5049 err
= RecordTypeFromArgString( gDNSQuery_Type
, &context
->type
);
5050 require_noerr( err
, exit
);
5052 // Write query message.
5054 check_compile_time_code( sizeof( context
->msgBuf
) >= ( kDNSQueryMessageMaxLen
+ 2 ) );
5056 msgPtr
= context
->useTCP
? &context
->msgBuf
[ 2 ] : context
->msgBuf
;
5057 err
= WriteDNSQueryMessage( msgPtr
, context
->queryID
, (uint16_t) gDNSQuery_Flags
, context
->name
, context
->type
,
5058 kDNSServiceClass_IN
, &msgLen
);
5059 require_noerr( err
, exit
);
5060 check( msgLen
<= UINT16_MAX
);
5062 if( context
->useTCP
)
5064 WriteBig16( context
->msgBuf
, msgLen
);
5065 sendLen
= 2 + msgLen
;
5072 DNSQueryPrintPrologue( context
);
5074 if( gDNSQuery_Verbose
)
5076 FPrintF( stdout
, "DNS message to send:\n\n" );
5077 PrintUDNSMessage( msgPtr
, msgLen
, false );
5078 FPrintF( stdout
, "---\n" );
5081 if( context
->useTCP
)
5083 // Create TCP socket.
5085 context
->sock
= socket( context
->serverAddr
.sa
.sa_family
, SOCK_STREAM
, IPPROTO_TCP
);
5086 err
= map_socket_creation_errno( context
->sock
);
5087 require_noerr( err
, exit
);
5089 err
= SocketConnect( context
->sock
, &context
->serverAddr
, 5 );
5090 require_noerr( err
, exit
);
5094 // Create UDP socket.
5096 err
= UDPClientSocketOpen( AF_UNSPEC
, &context
->serverAddr
, 0, -1, NULL
, &context
->sock
);
5097 require_noerr( err
, exit
);
5100 context
->sendTicks
= UpTicks();
5101 err
= SocketWriteAll( context
->sock
, context
->msgBuf
, sendLen
, 5 );
5102 require_noerr( err
, exit
);
5104 if( context
->timeLimitSecs
== 0 ) goto exit
;
5106 err
= DispatchReadSourceCreate( context
->sock
, DNSQueryReadHandler
, DNSQueryCancelHandler
, context
,
5107 &context
->readSource
);
5108 require_noerr( err
, exit
);
5109 dispatch_resume( context
->readSource
);
5111 if( context
->timeLimitSecs
> 0 )
5113 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
5121 dispatch_source_forget( &context
->readSource
);
5122 ForgetSocket( &context
->sock
);
5125 if( err
) exit( 1 );
5128 //===========================================================================================================================
5129 // DNSQueryPrintPrologue
5130 //===========================================================================================================================
5132 static void DNSQueryPrintPrologue( const DNSQueryContext
*inContext
)
5134 const int timeLimitSecs
= inContext
->timeLimitSecs
;
5135 char time
[ kTimestampBufLen
];
5137 FPrintF( stdout
, "Name: %s\n", inContext
->name
);
5138 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->type
), inContext
->type
);
5139 FPrintF( stdout
, "Server: %##a\n", &inContext
->serverAddr
);
5140 FPrintF( stdout
, "Transport: %s\n", inContext
->useTCP
? "TCP" : "UDP" );
5141 FPrintF( stdout
, "Time limit: " );
5142 if( timeLimitSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", timeLimitSecs
, timeLimitSecs
!= 1, 's' );
5143 else FPrintF( stdout
, "∞\n" );
5144 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
5145 FPrintF( stdout
, "---\n" );
5148 //===========================================================================================================================
5149 // DNSQueryReadHandler
5150 //===========================================================================================================================
5152 static void DNSQueryReadHandler( void *inContext
)
5155 const uint64_t nowTicks
= UpTicks();
5156 DNSQueryContext
* const context
= (DNSQueryContext
*) inContext
;
5157 char time
[ kTimestampBufLen
];
5159 GetTimestampStr( time
);
5161 if( context
->useTCP
)
5163 if( !context
->haveTCPLen
)
5165 err
= SocketReadData( context
->sock
, &context
->msgBuf
, 2, &context
->msgOffset
);
5166 if( err
== EWOULDBLOCK
) { err
= kNoErr
; goto exit
; }
5167 require_noerr( err
, exit
);
5169 context
->msgOffset
= 0;
5170 context
->msgLen
= ReadBig16( context
->msgBuf
);
5171 context
->haveTCPLen
= true;
5172 if( context
->msgLen
<= sizeof( context
->msgBuf
) )
5174 context
->msgPtr
= context
->msgBuf
;
5178 context
->msgPtr
= (uint8_t *) malloc( context
->msgLen
);
5179 require_action( context
->msgPtr
, exit
, err
= kNoMemoryErr
);
5183 err
= SocketReadData( context
->sock
, context
->msgPtr
, context
->msgLen
, &context
->msgOffset
);
5184 if( err
== EWOULDBLOCK
) { err
= kNoErr
; goto exit
; }
5185 require_noerr( err
, exit
);
5186 context
->msgOffset
= 0;
5187 context
->haveTCPLen
= false;
5191 sockaddr_ip fromAddr
;
5193 context
->msgPtr
= context
->msgBuf
;
5194 err
= SocketRecvFrom( context
->sock
, context
->msgPtr
, sizeof( context
->msgBuf
), &context
->msgLen
, &fromAddr
,
5195 sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
5196 require_noerr( err
, exit
);
5198 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
5201 FPrintF( stdout
, "Receive time: %s\n", time
);
5202 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
5203 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
5204 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
5205 PrintUDNSMessage( context
->msgPtr
, context
->msgLen
, context
->printRawRData
);
5207 if( ( context
->msgLen
>= kDNSHeaderLength
) && ( DNSHeaderGetID( (DNSHeader
*) context
->msgPtr
) == context
->queryID
) )
5209 Exit( kExitReason_ReceivedResponse
);
5213 if( err
) dispatch_source_forget( &context
->readSource
);
5216 //===========================================================================================================================
5217 // DNSQueryCancelHandler
5218 //===========================================================================================================================
5220 static void DNSQueryCancelHandler( void *inContext
)
5222 DNSQueryContext
* const context
= (DNSQueryContext
*) inContext
;
5224 check( !context
->readSource
);
5225 ForgetSocket( &context
->sock
);
5226 if( context
->msgPtr
!= context
->msgBuf
) ForgetMem( &context
->msgPtr
);
5228 dispatch_async_f( dispatch_get_main_queue(), NULL
, Exit
);
5231 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
5232 //===========================================================================================================================
5234 //===========================================================================================================================
5236 #define kDNSCryptPort 443
5238 #define kDNSCryptMinPadLength 8
5239 #define kDNSCryptMaxPadLength 256
5240 #define kDNSCryptBlockSize 64
5241 #define kDNSCryptCertMinimumLength 124
5242 #define kDNSCryptClientMagicLength 8
5243 #define kDNSCryptResolverMagicLength 8
5244 #define kDNSCryptHalfNonceLength 12
5245 #define kDNSCryptCertMagicLength 4
5247 check_compile_time( ( kDNSCryptHalfNonceLength
* 2 ) == crypto_box_NONCEBYTES
);
5249 static const uint8_t kDNSCryptCertMagic
[ kDNSCryptCertMagicLength
] = { 'D', 'N', 'S', 'C' };
5250 static const uint8_t kDNSCryptResolverMagic
[ kDNSCryptResolverMagicLength
] =
5252 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
5257 uint8_t certMagic
[ kDNSCryptCertMagicLength
];
5258 uint8_t esVersion
[ 2 ];
5259 uint8_t minorVersion
[ 2 ];
5260 uint8_t signature
[ crypto_sign_BYTES
];
5261 uint8_t publicKey
[ crypto_box_PUBLICKEYBYTES
];
5262 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5263 uint8_t serial
[ 4 ];
5264 uint8_t startTime
[ 4 ];
5265 uint8_t endTime
[ 4 ];
5266 uint8_t extensions
[ 1 ]; // Variably-sized extension data.
5270 check_compile_time( offsetof( DNSCryptCert
, extensions
) == kDNSCryptCertMinimumLength
);
5274 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5275 uint8_t clientPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5276 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5277 uint8_t poly1305MAC
[ 16 ];
5279 } DNSCryptQueryHeader
;
5281 check_compile_time( sizeof( DNSCryptQueryHeader
) == 68 );
5282 check_compile_time( sizeof( DNSCryptQueryHeader
) >= crypto_box_ZEROBYTES
);
5283 check_compile_time( ( sizeof( DNSCryptQueryHeader
) - crypto_box_ZEROBYTES
+ crypto_box_BOXZEROBYTES
) ==
5284 offsetof( DNSCryptQueryHeader
, poly1305MAC
) );
5288 uint8_t resolverMagic
[ kDNSCryptResolverMagicLength
];
5289 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5290 uint8_t resolverNonce
[ kDNSCryptHalfNonceLength
];
5291 uint8_t poly1305MAC
[ 16 ];
5293 } DNSCryptResponseHeader
;
5295 check_compile_time( sizeof( DNSCryptResponseHeader
) == 48 );
5296 check_compile_time( offsetof( DNSCryptResponseHeader
, poly1305MAC
) >= crypto_box_BOXZEROBYTES
);
5297 check_compile_time( ( offsetof( DNSCryptResponseHeader
, poly1305MAC
) - crypto_box_BOXZEROBYTES
+ crypto_box_ZEROBYTES
) ==
5298 sizeof( DNSCryptResponseHeader
) );
5302 sockaddr_ip serverAddr
;
5304 const char * providerName
;
5306 const uint8_t * certPtr
;
5308 dispatch_source_t readSource
;
5313 Boolean printRawRData
;
5314 uint8_t serverPublicSignKey
[ crypto_sign_PUBLICKEYBYTES
];
5315 uint8_t serverPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5316 uint8_t clientPublicKey
[ crypto_box_PUBLICKEYBYTES
];
5317 uint8_t clientSecretKey
[ crypto_box_SECRETKEYBYTES
];
5318 uint8_t clientMagic
[ kDNSCryptClientMagicLength
];
5319 uint8_t clientNonce
[ kDNSCryptHalfNonceLength
];
5320 uint8_t nmKey
[ crypto_box_BEFORENMBYTES
];
5321 uint8_t msgBuf
[ 512 ];
5325 static void DNSCryptReceiveCertHandler( void *inContext
);
5326 static void DNSCryptReceiveResponseHandler( void *inContext
);
5327 static void DNSCryptProceed( void *inContext
);
5328 static OSStatus
DNSCryptProcessCert( DNSCryptContext
*inContext
);
5329 static OSStatus
DNSCryptBuildQuery( DNSCryptContext
*inContext
);
5330 static OSStatus
DNSCryptSendQuery( DNSCryptContext
*inContext
);
5331 static void DNSCryptPrintCertificate( const DNSCryptCert
*inCert
, size_t inLen
);
5333 static void DNSCryptCmd( void )
5336 DNSCryptContext
* context
= NULL
;
5337 size_t writtenBytes
;
5339 SocketContext
* sockContext
;
5340 SocketRef sock
= kInvalidSocketRef
;
5343 // Check command parameters.
5345 if( gDNSCrypt_TimeLimitSecs
< -1 )
5347 FPrintF( stdout
, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs
);
5354 context
= (DNSCryptContext
*) calloc( 1, sizeof( *context
) );
5355 require_action( context
, exit
, err
= kNoMemoryErr
);
5357 context
->providerName
= gDNSCrypt_ProviderName
;
5358 context
->qname
= gDNSCrypt_Name
;
5359 context
->timeLimitSecs
= gDNSCrypt_TimeLimitSecs
;
5360 context
->printRawRData
= gDNSCrypt_RawRData
? true : false;
5362 err
= crypto_box_keypair( context
->clientPublicKey
, context
->clientSecretKey
);
5363 require_noerr( err
, exit
);
5365 err
= HexToData( gDNSCrypt_ProviderKey
, kSizeCString
, kHexToData_DefaultFlags
,
5366 context
->serverPublicSignKey
, sizeof( context
->serverPublicSignKey
), &writtenBytes
, &totalBytes
, &ptr
);
5367 if( err
|| ( *ptr
!= '\0' ) )
5369 FPrintF( stderr
, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey
);
5372 else if( totalBytes
!= sizeof( context
->serverPublicSignKey
) )
5374 FPrintF( stderr
, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
5375 totalBytes
, sizeof( context
->serverPublicSignKey
) );
5379 check( writtenBytes
== totalBytes
);
5381 err
= StringToSockAddr( gDNSCrypt_Server
, &context
->serverAddr
, sizeof( context
->serverAddr
), NULL
);
5382 require_noerr( err
, exit
);
5383 if( SockAddrGetPort( &context
->serverAddr
) == 0 ) SockAddrSetPort( &context
->serverAddr
, kDNSCryptPort
);
5385 err
= RecordTypeFromArgString( gDNSCrypt_Type
, &context
->qtype
);
5386 require_noerr( err
, exit
);
5388 // Write query message.
5390 context
->queryID
= (uint16_t) Random32();
5391 err
= WriteDNSQueryMessage( context
->msgBuf
, context
->queryID
, kDNSHeaderFlag_RecursionDesired
, context
->providerName
,
5392 kDNSServiceType_TXT
, kDNSServiceClass_IN
, &context
->msgLen
);
5393 require_noerr( err
, exit
);
5395 // Create UDP socket.
5397 err
= UDPClientSocketOpen( AF_UNSPEC
, &context
->serverAddr
, 0, -1, NULL
, &sock
);
5398 require_noerr( err
, exit
);
5402 context
->sendTicks
= UpTicks();
5403 err
= SocketWriteAll( sock
, context
->msgBuf
, context
->msgLen
, 5 );
5404 require_noerr( err
, exit
);
5406 sockContext
= (SocketContext
*) calloc( 1, sizeof( *sockContext
) );
5407 require_action( sockContext
, exit
, err
= kNoMemoryErr
);
5409 err
= DispatchReadSourceCreate( sock
, DNSCryptReceiveCertHandler
, SocketContextCancelHandler
, sockContext
,
5410 &context
->readSource
);
5411 if( err
) ForgetMem( &sockContext
);
5412 require_noerr( err
, exit
);
5414 sockContext
->context
= context
;
5415 sockContext
->sock
= sock
;
5416 sock
= kInvalidSocketRef
;
5417 dispatch_resume( context
->readSource
);
5419 if( context
->timeLimitSecs
> 0 )
5421 dispatch_after_f( dispatch_time_seconds( context
->timeLimitSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
5427 if( context
) free( context
);
5428 ForgetSocket( &sock
);
5429 if( err
) exit( 1 );
5432 //===========================================================================================================================
5433 // DNSCryptReceiveCertHandler
5434 //===========================================================================================================================
5436 static void DNSCryptReceiveCertHandler( void *inContext
)
5439 const uint64_t nowTicks
= UpTicks();
5440 SocketContext
* const sockContext
= (SocketContext
*) inContext
;
5441 DNSCryptContext
* const context
= (DNSCryptContext
*) sockContext
->context
;
5442 const DNSHeader
* hdr
;
5443 sockaddr_ip fromAddr
;
5444 const uint8_t * ptr
;
5445 const uint8_t * txtPtr
;
5447 unsigned int answerCount
, i
;
5448 uint8_t targetName
[ kDomainNameLengthMax
];
5449 char time
[ kTimestampBufLen
];
5451 GetTimestampStr( time
);
5453 dispatch_source_forget( &context
->readSource
);
5455 err
= SocketRecvFrom( sockContext
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &context
->msgLen
,
5456 &fromAddr
, sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
5457 require_noerr( err
, exit
);
5458 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
5460 FPrintF( stdout
, "Receive time: %s\n", time
);
5461 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
5462 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
5463 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
5465 PrintUDNSMessage( context
->msgBuf
, context
->msgLen
, context
->printRawRData
);
5467 require_action_quiet( context
->msgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
5469 hdr
= (DNSHeader
*) context
->msgBuf
;
5470 require_action_quiet( DNSHeaderGetID( hdr
) == context
->queryID
, exit
, err
= kMismatchErr
);
5472 err
= DNSMessageGetAnswerSection( context
->msgBuf
, context
->msgLen
, &ptr
);
5473 require_noerr( err
, exit
);
5475 err
= DomainNameFromString( targetName
, context
->providerName
, NULL
);
5476 require_noerr( err
, exit
);
5478 answerCount
= DNSHeaderGetAnswerCount( hdr
);
5479 for( i
= 0; i
< answerCount
; ++i
)
5483 uint8_t name
[ kDomainNameLengthMax
];
5485 err
= DNSMessageExtractRecord( context
->msgBuf
, context
->msgLen
, ptr
, name
, &type
, &class, NULL
, &txtPtr
, &txtLen
,
5487 require_noerr( err
, exit
);
5489 if( ( type
== kDNSServiceType_TXT
) && ( class == kDNSServiceClass_IN
) && DomainNameEqual( name
, targetName
) )
5495 if( txtLen
< ( 1 + kDNSCryptCertMinimumLength
) )
5497 FPrintF( stderr
, "TXT record length is too short (%u < %u)\n", txtLen
, kDNSCryptCertMinimumLength
+ 1 );
5501 if( txtPtr
[ 0 ] < kDNSCryptCertMinimumLength
)
5503 FPrintF( stderr
, "TXT record value length is too short (%u < %u)\n", txtPtr
[ 0 ], kDNSCryptCertMinimumLength
);
5508 context
->certLen
= txtPtr
[ 0 ];
5509 context
->certPtr
= &txtPtr
[ 1 ];
5511 dispatch_async_f( dispatch_get_main_queue(), context
, DNSCryptProceed
);
5514 if( err
) Exit( NULL
);
5517 //===========================================================================================================================
5518 // DNSCryptReceiveResponseHandler
5519 //===========================================================================================================================
5521 static void DNSCryptReceiveResponseHandler( void *inContext
)
5524 const uint64_t nowTicks
= UpTicks();
5525 SocketContext
* const sockContext
= (SocketContext
*) inContext
;
5526 DNSCryptContext
* const context
= (DNSCryptContext
*) sockContext
->context
;
5527 sockaddr_ip fromAddr
;
5528 DNSCryptResponseHeader
* hdr
;
5529 const uint8_t * end
;
5530 uint8_t * ciphertext
;
5531 uint8_t * plaintext
;
5532 const uint8_t * response
;
5533 char time
[ kTimestampBufLen
];
5534 uint8_t nonce
[ crypto_box_NONCEBYTES
];
5536 GetTimestampStr( time
);
5538 dispatch_source_forget( &context
->readSource
);
5540 err
= SocketRecvFrom( sockContext
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &context
->msgLen
,
5541 &fromAddr
, sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
5542 require_noerr( err
, exit
);
5543 check( SockAddrCompareAddr( &fromAddr
, &context
->serverAddr
) == 0 );
5545 FPrintF( stdout
, "Receive time: %s\n", time
);
5546 FPrintF( stdout
, "Source: %##a\n", &context
->serverAddr
);
5547 FPrintF( stdout
, "Message size: %zu\n", context
->msgLen
);
5548 FPrintF( stdout
, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks
- context
->sendTicks
) );
5550 if( context
->msgLen
< sizeof( DNSCryptResponseHeader
) )
5552 FPrintF( stderr
, "DNSCrypt response is too short.\n" );
5557 hdr
= (DNSCryptResponseHeader
*) context
->msgBuf
;
5559 if( memcmp( hdr
->resolverMagic
, kDNSCryptResolverMagic
, kDNSCryptResolverMagicLength
) != 0 )
5561 FPrintF( stderr
, "DNSCrypt response resolver magic %#H != %#H\n",
5562 hdr
->resolverMagic
, kDNSCryptResolverMagicLength
, INT_MAX
,
5563 kDNSCryptResolverMagic
, kDNSCryptResolverMagicLength
, INT_MAX
);
5568 if( memcmp( hdr
->clientNonce
, context
->clientNonce
, kDNSCryptHalfNonceLength
) != 0 )
5570 FPrintF( stderr
, "DNSCrypt response client nonce mismatch.\n" );
5575 memcpy( nonce
, hdr
->clientNonce
, crypto_box_NONCEBYTES
);
5577 ciphertext
= hdr
->poly1305MAC
- crypto_box_BOXZEROBYTES
;
5578 memset( ciphertext
, 0, crypto_box_BOXZEROBYTES
);
5580 plaintext
= (uint8_t *)( hdr
+ 1 ) - crypto_box_ZEROBYTES
;
5581 check( plaintext
== ciphertext
);
5583 end
= context
->msgBuf
+ context
->msgLen
;
5585 err
= crypto_box_open_afternm( plaintext
, ciphertext
, (size_t)( end
- ciphertext
), nonce
, context
->nmKey
);
5586 require_noerr( err
, exit
);
5588 response
= plaintext
+ crypto_box_ZEROBYTES
;
5589 PrintUDNSMessage( response
, (size_t)( end
- response
), context
->printRawRData
);
5590 Exit( kExitReason_ReceivedResponse
);
5593 if( err
) Exit( NULL
);
5596 //===========================================================================================================================
5598 //===========================================================================================================================
5600 static void DNSCryptProceed( void *inContext
)
5603 DNSCryptContext
* const context
= (DNSCryptContext
*) inContext
;
5605 err
= DNSCryptProcessCert( context
);
5606 require_noerr_quiet( err
, exit
);
5608 err
= DNSCryptBuildQuery( context
);
5609 require_noerr_quiet( err
, exit
);
5611 err
= DNSCryptSendQuery( context
);
5612 require_noerr_quiet( err
, exit
);
5615 if( err
) Exit( NULL
);
5618 //===========================================================================================================================
5619 // DNSCryptProcessCert
5620 //===========================================================================================================================
5622 static OSStatus
DNSCryptProcessCert( DNSCryptContext
*inContext
)
5625 const DNSCryptCert
* const cert
= (DNSCryptCert
*) inContext
->certPtr
;
5626 const uint8_t * const certEnd
= inContext
->certPtr
+ inContext
->certLen
;
5628 time_t startTimeSecs
, endTimeSecs
;
5631 unsigned long long tempLen
;
5633 DNSCryptPrintCertificate( cert
, inContext
->certLen
);
5635 if( memcmp( cert
->certMagic
, kDNSCryptCertMagic
, kDNSCryptCertMagicLength
) != 0 )
5637 FPrintF( stderr
, "DNSCrypt certificate magic %#H != %#H\n",
5638 cert
->certMagic
, kDNSCryptCertMagicLength
, INT_MAX
,
5639 kDNSCryptCertMagic
, kDNSCryptCertMagicLength
, INT_MAX
);
5644 startTimeSecs
= (time_t) ReadBig32( cert
->startTime
);
5645 endTimeSecs
= (time_t) ReadBig32( cert
->endTime
);
5647 gettimeofday( &now
, NULL
);
5648 if( now
.tv_sec
< startTimeSecs
)
5650 FPrintF( stderr
, "DNSCrypt certificate start time is in the future.\n" );
5654 if( now
.tv_sec
>= endTimeSecs
)
5656 FPrintF( stderr
, "DNSCrypt certificate has expired.\n" );
5661 signedLen
= (size_t)( certEnd
- cert
->signature
);
5662 tempBuf
= (uint8_t *) malloc( signedLen
);
5663 require_action( tempBuf
, exit
, err
= kNoMemoryErr
);
5664 err
= crypto_sign_open( tempBuf
, &tempLen
, cert
->signature
, signedLen
, inContext
->serverPublicSignKey
);
5668 FPrintF( stderr
, "DNSCrypt certificate failed verification.\n" );
5669 err
= kAuthenticationErr
;
5673 memcpy( inContext
->serverPublicKey
, cert
->publicKey
, crypto_box_PUBLICKEYBYTES
);
5674 memcpy( inContext
->clientMagic
, cert
->clientMagic
, kDNSCryptClientMagicLength
);
5676 err
= crypto_box_beforenm( inContext
->nmKey
, inContext
->serverPublicKey
, inContext
->clientSecretKey
);
5677 require_noerr( err
, exit
);
5679 inContext
->certPtr
= NULL
;
5680 inContext
->certLen
= 0;
5681 inContext
->msgLen
= 0;
5687 //===========================================================================================================================
5688 // DNSCryptBuildQuery
5689 //===========================================================================================================================
5691 static OSStatus
DNSCryptPadQuery( uint8_t *inMsgPtr
, size_t inMsgLen
, size_t inMaxLen
, size_t *outPaddedLen
);
5693 static OSStatus
DNSCryptBuildQuery( DNSCryptContext
*inContext
)
5696 DNSCryptQueryHeader
* const hdr
= (DNSCryptQueryHeader
*) inContext
->msgBuf
;
5697 uint8_t * const queryPtr
= (uint8_t *)( hdr
+ 1 );
5699 size_t paddedQueryLen
;
5700 const uint8_t * const msgLimit
= inContext
->msgBuf
+ sizeof( inContext
->msgBuf
);
5701 const uint8_t * padLimit
;
5702 uint8_t nonce
[ crypto_box_NONCEBYTES
];
5704 check_compile_time_code( sizeof( inContext
->msgBuf
) >= ( sizeof( DNSCryptQueryHeader
) + kDNSQueryMessageMaxLen
) );
5706 inContext
->queryID
= (uint16_t) Random32();
5707 err
= WriteDNSQueryMessage( queryPtr
, inContext
->queryID
, kDNSHeaderFlag_RecursionDesired
, inContext
->qname
,
5708 inContext
->qtype
, kDNSServiceClass_IN
, &queryLen
);
5709 require_noerr( err
, exit
);
5711 padLimit
= &queryPtr
[ queryLen
+ kDNSCryptMaxPadLength
];
5712 if( padLimit
> msgLimit
) padLimit
= msgLimit
;
5714 err
= DNSCryptPadQuery( queryPtr
, queryLen
, (size_t)( padLimit
- queryPtr
), &paddedQueryLen
);
5715 require_noerr( err
, exit
);
5717 memset( queryPtr
- crypto_box_ZEROBYTES
, 0, crypto_box_ZEROBYTES
);
5718 RandomBytes( inContext
->clientNonce
, kDNSCryptHalfNonceLength
);
5719 memcpy( nonce
, inContext
->clientNonce
, kDNSCryptHalfNonceLength
);
5720 memset( &nonce
[ kDNSCryptHalfNonceLength
], 0, kDNSCryptHalfNonceLength
);
5722 err
= crypto_box_afternm( queryPtr
- crypto_box_ZEROBYTES
, queryPtr
- crypto_box_ZEROBYTES
,
5723 paddedQueryLen
+ crypto_box_ZEROBYTES
, nonce
, inContext
->nmKey
);
5724 require_noerr( err
, exit
);
5726 memcpy( hdr
->clientMagic
, inContext
->clientMagic
, kDNSCryptClientMagicLength
);
5727 memcpy( hdr
->clientPublicKey
, inContext
->clientPublicKey
, crypto_box_PUBLICKEYBYTES
);
5728 memcpy( hdr
->clientNonce
, nonce
, kDNSCryptHalfNonceLength
);
5730 inContext
->msgLen
= (size_t)( &queryPtr
[ paddedQueryLen
] - inContext
->msgBuf
);
5736 static OSStatus
DNSCryptPadQuery( uint8_t *inMsgPtr
, size_t inMsgLen
, size_t inMaxLen
, size_t *outPaddedLen
)
5741 require_action_quiet( ( inMsgLen
+ kDNSCryptMinPadLength
) <= inMaxLen
, exit
, err
= kSizeErr
);
5743 paddedLen
= inMsgLen
+ kDNSCryptMinPadLength
+
5744 arc4random_uniform( (uint32_t)( inMaxLen
- ( inMsgLen
+ kDNSCryptMinPadLength
) + 1 ) );
5745 paddedLen
+= ( kDNSCryptBlockSize
- ( paddedLen
% kDNSCryptBlockSize
) );
5746 if( paddedLen
> inMaxLen
) paddedLen
= inMaxLen
;
5748 inMsgPtr
[ inMsgLen
] = 0x80;
5749 memset( &inMsgPtr
[ inMsgLen
+ 1 ], 0, paddedLen
- ( inMsgLen
+ 1 ) );
5751 if( outPaddedLen
) *outPaddedLen
= paddedLen
;
5758 //===========================================================================================================================
5759 // DNSCryptSendQuery
5760 //===========================================================================================================================
5762 static OSStatus
DNSCryptSendQuery( DNSCryptContext
*inContext
)
5765 SocketContext
* sockContext
;
5766 SocketRef sock
= kInvalidSocketRef
;
5768 check( inContext
->msgLen
> 0 );
5769 check( !inContext
->readSource
);
5771 err
= UDPClientSocketOpen( AF_UNSPEC
, &inContext
->serverAddr
, 0, -1, NULL
, &sock
);
5772 require_noerr( err
, exit
);
5774 inContext
->sendTicks
= UpTicks();
5775 err
= SocketWriteAll( sock
, inContext
->msgBuf
, inContext
->msgLen
, 5 );
5776 require_noerr( err
, exit
);
5778 sockContext
= (SocketContext
*) calloc( 1, sizeof( *sockContext
) );
5779 require_action( sockContext
, exit
, err
= kNoMemoryErr
);
5781 err
= DispatchReadSourceCreate( sock
, DNSCryptReceiveResponseHandler
, SocketContextCancelHandler
, sockContext
,
5782 &inContext
->readSource
);
5783 if( err
) ForgetMem( &sockContext
);
5784 require_noerr( err
, exit
);
5786 sockContext
->context
= inContext
;
5787 sockContext
->sock
= sock
;
5788 sock
= kInvalidSocketRef
;
5790 dispatch_resume( inContext
->readSource
);
5793 ForgetSocket( &sock
);
5797 //===========================================================================================================================
5798 // DNSCryptPrintCertificate
5799 //===========================================================================================================================
5801 #define kCertTimeStrBufLen 32
5803 static char * CertTimeStr( time_t inTime
, char inBuffer
[ kCertTimeStrBufLen
] );
5805 static void DNSCryptPrintCertificate( const DNSCryptCert
*inCert
, size_t inLen
)
5807 time_t startTime
, endTime
;
5809 char timeBuf
[ kCertTimeStrBufLen
];
5811 check( inLen
>= kDNSCryptCertMinimumLength
);
5813 startTime
= (time_t) ReadBig32( inCert
->startTime
);
5814 endTime
= (time_t) ReadBig32( inCert
->endTime
);
5816 FPrintF( stdout
, "DNSCrypt certificate (%zu bytes):\n", inLen
);
5817 FPrintF( stdout
, "Cert Magic: %#H\n", inCert
->certMagic
, kDNSCryptCertMagicLength
, INT_MAX
);
5818 FPrintF( stdout
, "ES Version: %u\n", ReadBig16( inCert
->esVersion
) );
5819 FPrintF( stdout
, "Minor Version: %u\n", ReadBig16( inCert
->minorVersion
) );
5820 FPrintF( stdout
, "Signature: %H\n", inCert
->signature
, crypto_sign_BYTES
/ 2, INT_MAX
);
5821 FPrintF( stdout
, " %H\n", &inCert
->signature
[ crypto_sign_BYTES
/ 2 ], crypto_sign_BYTES
/ 2, INT_MAX
);
5822 FPrintF( stdout
, "Public Key: %H\n", inCert
->publicKey
, sizeof( inCert
->publicKey
), INT_MAX
);
5823 FPrintF( stdout
, "Client Magic: %H\n", inCert
->clientMagic
, kDNSCryptClientMagicLength
, INT_MAX
);
5824 FPrintF( stdout
, "Serial: %u\n", ReadBig32( inCert
->serial
) );
5825 FPrintF( stdout
, "Start Time: %u (%s)\n", (uint32_t) startTime
, CertTimeStr( startTime
, timeBuf
) );
5826 FPrintF( stdout
, "End Time: %u (%s)\n", (uint32_t) endTime
, CertTimeStr( endTime
, timeBuf
) );
5828 if( inLen
> kDNSCryptCertMinimumLength
)
5830 extLen
= (int)( inLen
- kDNSCryptCertMinimumLength
);
5831 FPrintF( stdout
, "Extensions: %.1H\n", inCert
->extensions
, extLen
, extLen
);
5833 FPrintF( stdout
, "\n" );
5836 static char * CertTimeStr( time_t inTime
, char inBuffer
[ kCertTimeStrBufLen
] )
5840 tm
= localtime( &inTime
);
5843 dlogassert( "localtime() returned a NULL pointer.\n" );
5848 strftime( inBuffer
, kCertTimeStrBufLen
, "%a %b %d %H:%M:%S %Z %Y", tm
);
5854 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
5856 //===========================================================================================================================
5858 //===========================================================================================================================
5860 #define kMDNSPort 5353
5862 #define kDefaultMDNSMessageID 0
5863 #define kDefaultMDNSQueryFlags 0
5867 const char * qnameStr
; // Name (QNAME) of the record being queried as a C string.
5868 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
5869 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
5870 int localPort
; // The port number to which the sockets are bound.
5871 int receiveSecs
; // After send, the amount of time to spend receiving.
5872 uint32_t ifIndex
; // Index of the interface over which to send the query.
5873 uint16_t qtype
; // The type (QTYPE) of the record being queried.
5874 Boolean isQU
; // True if the query is QU, i.e., requests unicast responses.
5875 Boolean allResponses
; // True if all mDNS messages received should be printed.
5876 Boolean printRawRData
; // True if RDATA should be printed as hexdumps.
5877 Boolean useIPv4
; // True if the query should be sent via IPv4 multicast.
5878 Boolean useIPv6
; // True if the query should be sent via IPv6 multicast.
5879 char ifName
[ IF_NAMESIZE
+ 1 ]; // Name of the interface over which to send the query.
5880 uint8_t qname
[ kDomainNameLengthMax
]; // Buffer to hold the QNAME in DNS label format.
5881 uint8_t msgBuf
[ 8940 ]; // Message buffer. 8940 is max size used by mDNSResponder.
5885 static void MDNSQueryPrintPrologue( const MDNSQueryContext
*inContext
);
5886 static void MDNSQueryReadHandler( void *inContext
);
5888 static void MDNSQueryCmd( void )
5891 MDNSQueryContext
* context
;
5892 struct sockaddr_in mcastAddr4
;
5893 struct sockaddr_in6 mcastAddr6
;
5894 SocketRef sockV4
= kInvalidSocketRef
;
5895 SocketRef sockV6
= kInvalidSocketRef
;
5897 const char * ifNamePtr
;
5899 unsigned int sendCount
;
5901 // Check command parameters.
5903 if( gMDNSQuery_ReceiveSecs
< -1 )
5905 FPrintF( stdout
, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs
);
5910 context
= (MDNSQueryContext
*) calloc( 1, sizeof( *context
) );
5911 require_action( context
, exit
, err
= kNoMemoryErr
);
5913 context
->qnameStr
= gMDNSQuery_Name
;
5914 context
->receiveSecs
= gMDNSQuery_ReceiveSecs
;
5915 context
->isQU
= gMDNSQuery_IsQU
? true : false;
5916 context
->allResponses
= gMDNSQuery_AllResponses
? true : false;
5917 context
->printRawRData
= gMDNSQuery_RawRData
? true : false;
5918 context
->useIPv4
= ( gMDNSQuery_UseIPv4
|| !gMDNSQuery_UseIPv6
) ? true : false;
5919 context
->useIPv6
= ( gMDNSQuery_UseIPv6
|| !gMDNSQuery_UseIPv4
) ? true : false;
5921 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifIndex
);
5922 require_noerr_quiet( err
, exit
);
5924 ifNamePtr
= if_indextoname( context
->ifIndex
, context
->ifName
);
5925 require_action( ifNamePtr
, exit
, err
= kNameErr
);
5927 err
= RecordTypeFromArgString( gMDNSQuery_Type
, &context
->qtype
);
5928 require_noerr( err
, exit
);
5930 // Set up IPv4 socket.
5932 if( context
->useIPv4
)
5934 err
= ServerSocketOpen( AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
,
5935 gMDNSQuery_SourcePort
? gMDNSQuery_SourcePort
: ( context
->isQU
? context
->localPort
: kMDNSPort
),
5936 &context
->localPort
, kSocketBufferSize_DontSet
, &sockV4
);
5937 require_noerr( err
, exit
);
5939 err
= SocketSetMulticastInterface( sockV4
, ifNamePtr
, context
->ifIndex
);
5940 require_noerr( err
, exit
);
5942 err
= setsockopt( sockV4
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *) &(uint8_t){ 1 }, (socklen_t
) sizeof( uint8_t ) );
5943 err
= map_socket_noerr_errno( sockV4
, err
);
5944 require_noerr( err
, exit
);
5946 memset( &mcastAddr4
, 0, sizeof( mcastAddr4
) );
5947 SIN_LEN_SET( &mcastAddr4
);
5948 mcastAddr4
.sin_family
= AF_INET
;
5949 mcastAddr4
.sin_port
= htons( kMDNSPort
);
5950 mcastAddr4
.sin_addr
.s_addr
= htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251
5952 if( !context
->isQU
&& ( context
->localPort
== kMDNSPort
) )
5954 err
= SocketJoinMulticast( sockV4
, &mcastAddr4
, ifNamePtr
, context
->ifIndex
);
5955 require_noerr( err
, exit
);
5959 // Set up IPv6 socket.
5961 if( context
->useIPv6
)
5963 err
= ServerSocketOpen( AF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
,
5964 gMDNSQuery_SourcePort
? gMDNSQuery_SourcePort
: ( context
->isQU
? context
->localPort
: kMDNSPort
),
5965 &context
->localPort
, kSocketBufferSize_DontSet
, &sockV6
);
5966 require_noerr( err
, exit
);
5968 err
= SocketSetMulticastInterface( sockV6
, ifNamePtr
, context
->ifIndex
);
5969 require_noerr( err
, exit
);
5971 err
= setsockopt( sockV6
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *) &(int){ 1 }, (socklen_t
) sizeof( int ) );
5972 err
= map_socket_noerr_errno( sockV6
, err
);
5973 require_noerr( err
, exit
);
5975 memset( &mcastAddr6
, 0, sizeof( mcastAddr6
) );
5976 SIN6_LEN_SET( &mcastAddr6
);
5977 mcastAddr6
.sin6_family
= AF_INET6
;
5978 mcastAddr6
.sin6_port
= htons( kMDNSPort
);
5979 mcastAddr6
.sin6_addr
.s6_addr
[ 0 ] = 0xFF; // mDNS IPv6 multicast address FF02::FB
5980 mcastAddr6
.sin6_addr
.s6_addr
[ 1 ] = 0x02;
5981 mcastAddr6
.sin6_addr
.s6_addr
[ 15 ] = 0xFB;
5983 if( !context
->isQU
&& ( context
->localPort
== kMDNSPort
) )
5985 err
= SocketJoinMulticast( sockV6
, &mcastAddr6
, ifNamePtr
, context
->ifIndex
);
5986 require_noerr( err
, exit
);
5990 // Craft mDNS query message.
5992 check_compile_time_code( sizeof( context
->msgBuf
) >= kDNSQueryMessageMaxLen
);
5993 err
= WriteDNSQueryMessage( context
->msgBuf
, kDefaultMDNSMessageID
, kDefaultMDNSQueryFlags
, context
->qnameStr
,
5994 context
->qtype
, context
->isQU
? ( kDNSServiceClass_IN
| kQClassUnicastResponseBit
) : kDNSServiceClass_IN
, &msgLen
);
5995 require_noerr( err
, exit
);
5999 MDNSQueryPrintPrologue( context
);
6001 // Send mDNS query message.
6004 if( IsValidSocket( sockV4
) )
6006 n
= sendto( sockV4
, context
->msgBuf
, msgLen
, 0, (struct sockaddr
*) &mcastAddr4
, (socklen_t
) sizeof( mcastAddr4
) );
6007 err
= map_socket_value_errno( sockV4
, n
== (ssize_t
) msgLen
, n
);
6010 FPrintF( stderr
, "*** Failed to send query on IPv4 socket with error %#m\n", err
);
6011 ForgetSocket( &sockV4
);
6018 if( IsValidSocket( sockV6
) )
6020 n
= sendto( sockV6
, context
->msgBuf
, msgLen
, 0, (struct sockaddr
*) &mcastAddr6
, (socklen_t
) sizeof( mcastAddr6
) );
6021 err
= map_socket_value_errno( sockV6
, n
== (ssize_t
) msgLen
, n
);
6024 FPrintF( stderr
, "*** Failed to send query on IPv6 socket with error %#m\n", err
);
6025 ForgetSocket( &sockV6
);
6032 require_action_quiet( sendCount
> 0, exit
, err
= kUnexpectedErr
);
6034 // If there's no wait period after the send, then exit.
6036 if( context
->receiveSecs
== 0 ) goto exit
;
6038 // Create dispatch read sources for socket(s).
6040 if( IsValidSocket( sockV4
) )
6042 SocketContext
* sockContext
;
6044 sockContext
= (SocketContext
*) calloc( 1, sizeof( *sockContext
) );
6045 require_action( sockContext
, exit
, err
= kNoMemoryErr
);
6047 err
= DispatchReadSourceCreate( sockV4
, MDNSQueryReadHandler
, SocketContextCancelHandler
, sockContext
,
6048 &context
->readSourceV4
);
6049 if( err
) ForgetMem( &sockContext
);
6050 require_noerr( err
, exit
);
6052 sockContext
->context
= context
;
6053 sockContext
->sock
= sockV4
;
6054 sockV4
= kInvalidSocketRef
;
6055 dispatch_resume( context
->readSourceV4
);
6058 if( IsValidSocket( sockV6
) )
6060 SocketContext
* sockContext
;
6062 sockContext
= (SocketContext
*) calloc( 1, sizeof( *sockContext
) );
6063 require_action( sockContext
, exit
, err
= kNoMemoryErr
);
6065 err
= DispatchReadSourceCreate( sockV6
, MDNSQueryReadHandler
, SocketContextCancelHandler
, sockContext
,
6066 &context
->readSourceV6
);
6067 if( err
) ForgetMem( &sockContext
);
6068 require_noerr( err
, exit
);
6070 sockContext
->context
= context
;
6071 sockContext
->sock
= sockV6
;
6072 sockV6
= kInvalidSocketRef
;
6073 dispatch_resume( context
->readSourceV6
);
6076 if( context
->receiveSecs
> 0 )
6078 dispatch_after_f( dispatch_time_seconds( context
->receiveSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
6084 ForgetSocket( &sockV4
);
6085 ForgetSocket( &sockV6
);
6086 if( err
) exit( 1 );
6089 //===========================================================================================================================
6090 // MDNSQueryPrintPrologue
6091 //===========================================================================================================================
6093 static void MDNSQueryPrintPrologue( const MDNSQueryContext
*inContext
)
6095 const int receiveSecs
= inContext
->receiveSecs
;
6096 char time
[ kTimestampBufLen
];
6098 FPrintF( stdout
, "Interface: %d (%s)\n", (int32_t) inContext
->ifIndex
, inContext
->ifName
);
6099 FPrintF( stdout
, "Name: %s\n", inContext
->qnameStr
);
6100 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( inContext
->qtype
), inContext
->qtype
);
6101 FPrintF( stdout
, "Class: IN (%s)\n", inContext
->isQU
? "QU" : "QM" );
6102 FPrintF( stdout
, "Local port: %d\n", inContext
->localPort
);
6103 FPrintF( stdout
, "IP protocols: %?s%?s%?s\n",
6104 inContext
->useIPv4
, "IPv4", ( inContext
->useIPv4
&& inContext
->useIPv6
), ", ", inContext
->useIPv6
, "IPv6" );
6105 FPrintF( stdout
, "Receive duration: " );
6106 if( receiveSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", receiveSecs
, receiveSecs
!= 1, 's' );
6107 else FPrintF( stdout
, "∞\n" );
6108 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
6111 //===========================================================================================================================
6112 // MDNSQueryReadHandler
6113 //===========================================================================================================================
6115 static void MDNSQueryReadHandler( void *inContext
)
6118 SocketContext
* const sockContext
= (SocketContext
*) inContext
;
6119 MDNSQueryContext
* const context
= (MDNSQueryContext
*) sockContext
->context
;
6121 sockaddr_ip fromAddr
;
6122 char time
[ kTimestampBufLen
];
6123 Boolean foundAnswer
= false;
6125 GetTimestampStr( time
);
6127 err
= SocketRecvFrom( sockContext
->sock
, context
->msgBuf
, sizeof( context
->msgBuf
), &msgLen
, &fromAddr
,
6128 sizeof( fromAddr
), NULL
, NULL
, NULL
, NULL
);
6129 require_noerr( err
, exit
);
6131 if( !context
->allResponses
&& ( msgLen
>= kDNSHeaderLength
) )
6133 const uint8_t * ptr
;
6134 const DNSHeader
* const hdr
= (DNSHeader
*) context
->msgBuf
;
6135 unsigned int rrCount
, i
;
6136 uint16_t type
, class;
6137 uint8_t name
[ kDomainNameLengthMax
];
6139 err
= DNSMessageGetAnswerSection( context
->msgBuf
, msgLen
, &ptr
);
6140 require_noerr( err
, exit
);
6142 if( context
->qname
[ 0 ] == 0 )
6144 err
= DomainNameAppendString( context
->qname
, context
->qnameStr
, NULL
);
6145 require_noerr( err
, exit
);
6148 rrCount
= DNSHeaderGetAnswerCount( hdr
) + DNSHeaderGetAuthorityCount( hdr
) + DNSHeaderGetAdditionalCount( hdr
);
6149 for( i
= 0; i
< rrCount
; ++i
)
6151 err
= DNSMessageExtractRecord( context
->msgBuf
, msgLen
, ptr
, name
, &type
, &class, NULL
, NULL
, NULL
, &ptr
);
6152 require_noerr( err
, exit
);
6154 if( ( ( context
->qtype
== kDNSServiceType_ANY
) || ( type
== context
->qtype
) ) &&
6155 DomainNameEqual( name
, context
->qname
) )
6162 if( context
->allResponses
|| foundAnswer
)
6164 FPrintF( stdout
, "---\n" );
6165 FPrintF( stdout
, "Receive time: %s\n", time
);
6166 FPrintF( stdout
, "Source: %##a\n", &fromAddr
);
6167 FPrintF( stdout
, "Message size: %zu\n\n", msgLen
);
6169 PrintMDNSMessage( context
->msgBuf
, msgLen
, context
->printRawRData
);
6173 if( err
) exit( 1 );
6176 //===========================================================================================================================
6178 //===========================================================================================================================
6180 static void PIDToUUIDCmd( void )
6184 struct proc_uniqidentifierinfo info
;
6186 n
= proc_pidinfo( gPIDToUUID_PID
, PROC_PIDUNIQIDENTIFIERINFO
, 1, &info
, sizeof( info
) );
6187 require_action_quiet( n
== (int) sizeof( info
), exit
, err
= kUnknownErr
);
6189 FPrintF( stdout
, "%#U\n", info
.p_uuid
);
6193 if( err
) exit( 1 );
6196 //===========================================================================================================================
6198 //===========================================================================================================================
6200 #define kSSDPPort 1900
6204 HTTPHeader header
; // HTTP header object for sending and receiving.
6205 dispatch_source_t readSourceV4
; // Read dispatch source for IPv4 socket.
6206 dispatch_source_t readSourceV6
; // Read dispatch source for IPv6 socket.
6207 int receiveSecs
; // After send, the amount of time to spend receiving.
6208 uint32_t ifindex
; // Index of the interface over which to send the query.
6209 Boolean useIPv4
; // True if the query should be sent via IPv4 multicast.
6210 Boolean useIPv6
; // True if the query should be sent via IPv6 multicast.
6212 } SSDPDiscoverContext
;
6214 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext
*inContext
);
6215 static void SSDPDiscoverReadHandler( void *inContext
);
6216 static int SocketToPortNumber( SocketRef inSock
);
6217 static OSStatus
WriteSSDPSearchRequest( HTTPHeader
*inHeader
, const void *inHostSA
, int inMX
, const char *inST
);
6219 static void SSDPDiscoverCmd( void )
6222 SSDPDiscoverContext
* context
;
6223 dispatch_source_t signalSource
= NULL
;
6224 SocketRef sockV4
= kInvalidSocketRef
;
6225 SocketRef sockV6
= kInvalidSocketRef
;
6228 char time
[ kTimestampBufLen
];
6230 // Set up SIGINT handler.
6232 signal( SIGINT
, SIG_IGN
);
6233 err
= DispatchSignalSourceCreate( SIGINT
, Exit
, kExitReason_SIGINT
, &signalSource
);
6234 require_noerr( err
, exit
);
6235 dispatch_resume( signalSource
);
6237 // Check command parameters.
6239 if( gSSDPDiscover_ReceiveSecs
< -1 )
6241 FPrintF( stdout
, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs
);
6248 context
= (SSDPDiscoverContext
*) calloc( 1, sizeof( *context
) );
6249 require_action( context
, exit
, err
= kNoMemoryErr
);
6251 context
->receiveSecs
= gSSDPDiscover_ReceiveSecs
;
6252 context
->useIPv4
= ( gSSDPDiscover_UseIPv4
|| !gSSDPDiscover_UseIPv6
) ? true : false;
6253 context
->useIPv6
= ( gSSDPDiscover_UseIPv6
|| !gSSDPDiscover_UseIPv4
) ? true : false;
6255 err
= InterfaceIndexFromArgString( gInterface
, &context
->ifindex
);
6256 require_noerr_quiet( err
, exit
);
6258 // Set up IPv4 socket.
6260 if( context
->useIPv4
)
6263 err
= UDPClientSocketOpen( AF_INET
, NULL
, 0, -1, &port
, &sockV4
);
6264 require_noerr( err
, exit
);
6266 err
= SocketSetMulticastInterface( sockV4
, NULL
, context
->ifindex
);
6267 require_noerr( err
, exit
);
6269 err
= setsockopt( sockV4
, IPPROTO_IP
, IP_MULTICAST_LOOP
, (char *) &(uint8_t){ 1 }, (socklen_t
) sizeof( uint8_t ) );
6270 err
= map_socket_noerr_errno( sockV4
, err
);
6271 require_noerr( err
, exit
);
6274 // Set up IPv6 socket.
6276 if( context
->useIPv6
)
6278 err
= UDPClientSocketOpen( AF_INET6
, NULL
, 0, -1, NULL
, &sockV6
);
6279 require_noerr( err
, exit
);
6281 err
= SocketSetMulticastInterface( sockV6
, NULL
, context
->ifindex
);
6282 require_noerr( err
, exit
);
6284 err
= setsockopt( sockV6
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, (char *) &(int){ 1 }, (socklen_t
) sizeof( int ) );
6285 err
= map_socket_noerr_errno( sockV6
, err
);
6286 require_noerr( err
, exit
);
6291 SSDPDiscoverPrintPrologue( context
);
6293 // Send mDNS query message.
6296 if( IsValidSocket( sockV4
) )
6298 struct sockaddr_in mcastAddr4
;
6300 memset( &mcastAddr4
, 0, sizeof( mcastAddr4
) );
6301 SIN_LEN_SET( &mcastAddr4
);
6302 mcastAddr4
.sin_family
= AF_INET
;
6303 mcastAddr4
.sin_port
= htons( kSSDPPort
);
6304 mcastAddr4
.sin_addr
.s_addr
= htonl( 0xEFFFFFFA ); // 239.255.255.250
6306 err
= WriteSSDPSearchRequest( &context
->header
, &mcastAddr4
, gSSDPDiscover_MX
, gSSDPDiscover_ST
);
6307 require_noerr( err
, exit
);
6309 n
= sendto( sockV4
, context
->header
.buf
, context
->header
.len
, 0, (const struct sockaddr
*) &mcastAddr4
,
6310 (socklen_t
) sizeof( mcastAddr4
) );
6311 err
= map_socket_value_errno( sockV4
, n
== (ssize_t
) context
->header
.len
, n
);
6314 FPrintF( stderr
, "*** Failed to send query on IPv4 socket with error %#m\n", err
);
6315 ForgetSocket( &sockV4
);
6319 if( gSSDPDiscover_Verbose
)
6321 GetTimestampStr( time
);
6322 FPrintF( stdout
, "---\n" );
6323 FPrintF( stdout
, "Send time: %s\n", time
);
6324 FPrintF( stdout
, "Source Port: %d\n", SocketToPortNumber( sockV4
) );
6325 FPrintF( stdout
, "Destination: %##a\n", &mcastAddr4
);
6326 FPrintF( stdout
, "Message size: %zu\n", context
->header
.len
);
6327 FPrintF( stdout
, "HTTP header:\n%1{text}", context
->header
.buf
, context
->header
.len
);
6333 if( IsValidSocket( sockV6
) )
6335 struct sockaddr_in6 mcastAddr6
;
6337 memset( &mcastAddr6
, 0, sizeof( mcastAddr6
) );
6338 SIN6_LEN_SET( &mcastAddr6
);
6339 mcastAddr6
.sin6_family
= AF_INET6
;
6340 mcastAddr6
.sin6_port
= htons( kSSDPPort
);
6341 mcastAddr6
.sin6_addr
.s6_addr
[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C
6342 mcastAddr6
.sin6_addr
.s6_addr
[ 1 ] = 0x02;
6343 mcastAddr6
.sin6_addr
.s6_addr
[ 15 ] = 0x0C;
6345 err
= WriteSSDPSearchRequest( &context
->header
, &mcastAddr6
, gSSDPDiscover_MX
, gSSDPDiscover_ST
);
6346 require_noerr( err
, exit
);
6348 n
= sendto( sockV6
, context
->header
.buf
, context
->header
.len
, 0, (const struct sockaddr
*) &mcastAddr6
,
6349 (socklen_t
) sizeof( mcastAddr6
) );
6350 err
= map_socket_value_errno( sockV6
, n
== (ssize_t
) context
->header
.len
, n
);
6353 FPrintF( stderr
, "*** Failed to send query on IPv6 socket with error %#m\n", err
);
6354 ForgetSocket( &sockV6
);
6358 if( gSSDPDiscover_Verbose
)
6360 GetTimestampStr( time
);
6361 FPrintF( stdout
, "---\n" );
6362 FPrintF( stdout
, "Send time: %s\n", time
);
6363 FPrintF( stdout
, "Source Port: %d\n", SocketToPortNumber( sockV6
) );
6364 FPrintF( stdout
, "Destination: %##a\n", &mcastAddr6
);
6365 FPrintF( stdout
, "Message size: %zu\n", context
->header
.len
);
6366 FPrintF( stdout
, "HTTP header:\n%1{text}", context
->header
.buf
, context
->header
.len
);
6371 require_action_quiet( sendCount
> 0, exit
, err
= kUnexpectedErr
);
6373 // If there's no wait period after the send, then exit.
6375 if( context
->receiveSecs
== 0 ) goto exit
;
6377 // Create dispatch read sources for socket(s).
6379 if( IsValidSocket( sockV4
) )
6381 SocketContext
* sockContext
;
6383 sockContext
= (SocketContext
*) calloc( 1, sizeof( *sockContext
) );
6384 require_action( sockContext
, exit
, err
= kNoMemoryErr
);
6386 err
= DispatchReadSourceCreate( sockV4
, SSDPDiscoverReadHandler
, SocketContextCancelHandler
, sockContext
,
6387 &context
->readSourceV4
);
6388 if( err
) ForgetMem( &sockContext
);
6389 require_noerr( err
, exit
);
6391 sockContext
->context
= context
;
6392 sockContext
->sock
= sockV4
;
6393 sockV4
= kInvalidSocketRef
;
6394 dispatch_resume( context
->readSourceV4
);
6397 if( IsValidSocket( sockV6
) )
6399 SocketContext
* sockContext
;
6401 sockContext
= (SocketContext
*) calloc( 1, sizeof( *sockContext
) );
6402 require_action( sockContext
, exit
, err
= kNoMemoryErr
);
6404 err
= DispatchReadSourceCreate( sockV6
, SSDPDiscoverReadHandler
, SocketContextCancelHandler
, sockContext
,
6405 &context
->readSourceV6
);
6406 if( err
) ForgetMem( &sockContext
);
6407 require_noerr( err
, exit
);
6409 sockContext
->context
= context
;
6410 sockContext
->sock
= sockV6
;
6411 sockV6
= kInvalidSocketRef
;
6412 dispatch_resume( context
->readSourceV6
);
6415 if( context
->receiveSecs
> 0 )
6417 dispatch_after_f( dispatch_time_seconds( context
->receiveSecs
), dispatch_get_main_queue(), kExitReason_Timeout
,
6423 ForgetSocket( &sockV4
);
6424 ForgetSocket( &sockV6
);
6425 dispatch_source_forget( &signalSource
);
6426 if( err
) exit( 1 );
6429 static int SocketToPortNumber( SocketRef inSock
)
6435 len
= (socklen_t
) sizeof( sip
);
6436 err
= getsockname( inSock
, &sip
.sa
, &len
);
6437 err
= map_socket_noerr_errno( inSock
, err
);
6439 return( err
? -1 : SockAddrGetPort( &sip
) );
6442 static OSStatus
WriteSSDPSearchRequest( HTTPHeader
*inHeader
, const void *inHostSA
, int inMX
, const char *inST
)
6446 err
= HTTPHeader_InitRequest( inHeader
, "M-SEARCH", "*", "HTTP/1.1" );
6447 require_noerr( err
, exit
);
6449 err
= HTTPHeader_SetField( inHeader
, "Host", "%##a", inHostSA
);
6450 require_noerr( err
, exit
);
6452 err
= HTTPHeader_SetField( inHeader
, "ST", "%s", inST
? inST
: "ssdp:all" );
6453 require_noerr( err
, exit
);
6455 err
= HTTPHeader_SetField( inHeader
, "Man", "\"ssdp:discover\"" );
6456 require_noerr( err
, exit
);
6458 err
= HTTPHeader_SetField( inHeader
, "MX", "%d", inMX
);
6459 require_noerr( err
, exit
);
6461 err
= HTTPHeader_Commit( inHeader
);
6462 require_noerr( err
, exit
);
6468 //===========================================================================================================================
6469 // SSDPDiscoverPrintPrologue
6470 //===========================================================================================================================
6472 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext
*inContext
)
6474 const int receiveSecs
= inContext
->receiveSecs
;
6475 const char * ifName
;
6476 char ifNameBuf
[ IF_NAMESIZE
+ 1 ];
6477 NetTransportType ifType
;
6478 char time
[ kTimestampBufLen
];
6480 ifName
= if_indextoname( inContext
->ifindex
, ifNameBuf
);
6482 ifType
= kNetTransportType_Undefined
;
6483 if( ifName
) SocketGetInterfaceInfo( kInvalidSocketRef
, ifName
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &ifType
);
6485 FPrintF( stdout
, "Interface: %s/%d/%s\n",
6486 ifName
? ifName
: "?", inContext
->ifindex
, NetTransportTypeToString( ifType
) );
6487 FPrintF( stdout
, "IP protocols: %?s%?s%?s\n",
6488 inContext
->useIPv4
, "IPv4", ( inContext
->useIPv4
&& inContext
->useIPv6
), ", ", inContext
->useIPv6
, "IPv6" );
6489 FPrintF( stdout
, "Receive duration: " );
6490 if( receiveSecs
>= 0 ) FPrintF( stdout
, "%d second%?c\n", receiveSecs
, receiveSecs
!= 1, 's' );
6491 else FPrintF( stdout
, "∞\n" );
6492 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
6495 //===========================================================================================================================
6496 // SSDPDiscoverReadHandler
6497 //===========================================================================================================================
6499 static void SSDPDiscoverReadHandler( void *inContext
)
6502 SocketContext
* const sockContext
= (SocketContext
*) inContext
;
6503 SSDPDiscoverContext
* const context
= (SSDPDiscoverContext
*) sockContext
->context
;
6504 HTTPHeader
* const header
= &context
->header
;
6505 sockaddr_ip fromAddr
;
6507 char time
[ kTimestampBufLen
];
6509 GetTimestampStr( time
);
6511 err
= SocketRecvFrom( sockContext
->sock
, header
->buf
, sizeof( header
->buf
), &msgLen
, &fromAddr
, sizeof( fromAddr
),
6512 NULL
, NULL
, NULL
, NULL
);
6513 require_noerr( err
, exit
);
6515 FPrintF( stdout
, "---\n" );
6516 FPrintF( stdout
, "Receive time: %s\n", time
);
6517 FPrintF( stdout
, "Source: %##a\n", &fromAddr
);
6518 FPrintF( stdout
, "Message size: %zu\n", msgLen
);
6519 header
->len
= msgLen
;
6520 if( HTTPHeader_Validate( header
) )
6522 FPrintF( stdout
, "HTTP header:\n%1{text}", header
->buf
, header
->len
);
6523 if( header
->extraDataLen
> 0 )
6525 FPrintF( stdout
, "HTTP body: %1.1H", header
->extraDataPtr
, (int) header
->extraDataLen
, INT_MAX
);
6530 FPrintF( stdout
, "Invalid HTTP message:\n%1.1H", header
->buf
, (int) msgLen
, INT_MAX
);
6535 if( err
) exit( 1 );
6538 //===========================================================================================================================
6539 // HTTPHeader_Validate
6541 // Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid.
6542 // This assumes the "buf" and "len" fields are set. The other fields are set by this function.
6544 // Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework.
6545 //===========================================================================================================================
6547 Boolean
HTTPHeader_Validate( HTTPHeader
*inHeader
)
6552 // Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
6554 require( inHeader
->len
< sizeof( inHeader
->buf
), exit
);
6555 src
= inHeader
->buf
;
6556 end
= src
+ inHeader
->len
;
6557 if( ( ( end
- src
) >= 4 ) && ( src
[ 0 ] == '$' ) )
6563 // Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted.
6564 // $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over.
6568 while( ( src
< end
) && ( src
[ 0 ] != '\n' ) ) ++src
;
6569 if( src
>= end
) goto exit
;
6571 if( ( ( end
- src
) >= 2 ) && ( src
[ 0 ] == '\r' ) && ( src
[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF
6576 else if( ( ( end
- src
) >= 1 ) && ( src
[ 0 ] == '\n' ) ) // LFLF
6583 inHeader
->extraDataPtr
= src
;
6584 inHeader
->extraDataLen
= (size_t)( end
- src
);
6585 inHeader
->len
= (size_t)( src
- inHeader
->buf
);
6592 #if( TARGET_OS_DARWIN )
6593 //===========================================================================================================================
6595 //===========================================================================================================================
6597 // res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h).
6599 SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv
);
6600 SOFT_LINK_FUNCTION_EX( resolv
, res_9_query
,
6602 ( const char *dname
, int class, int type
, u_char
*answer
, int anslen
),
6603 ( dname
, class, type
, answer
, anslen
) );
6605 // res_query() from libinfo
6607 SOFT_LINK_LIBRARY_EX( "/usr/lib", info
);
6608 SOFT_LINK_FUNCTION_EX( info
, res_query
,
6610 ( const char *dname
, int class, int type
, u_char
*answer
, int anslen
),
6611 ( dname
, class, type
, answer
, anslen
) );
6613 typedef int ( *res_query_f
)( const char *dname
, int class, int type
, u_char
*answer
, int anslen
);
6615 static void ResQueryCmd( void )
6618 res_query_f res_query_ptr
;
6620 uint16_t type
, class;
6621 char time
[ kTimestampBufLen
];
6622 uint8_t answer
[ 1024 ];
6624 // Get pointer to one of the res_query() functions.
6626 if( gResQuery_UseLibInfo
)
6628 if( !SOFT_LINK_HAS_FUNCTION( info
, res_query
) )
6630 FPrintF( stderr
, "Failed to soft link res_query from libinfo.\n" );
6634 res_query_ptr
= soft_res_query
;
6638 if( !SOFT_LINK_HAS_FUNCTION( resolv
, res_9_query
) )
6640 FPrintF( stderr
, "Failed to soft link res_query from libresolv.\n" );
6644 res_query_ptr
= soft_res_9_query
;
6649 err
= RecordTypeFromArgString( gResQuery_Type
, &type
);
6650 require_noerr( err
, exit
);
6652 // Get record class.
6654 if( gResQuery_Class
)
6656 err
= RecordClassFromArgString( gResQuery_Class
, &class );
6657 require_noerr( err
, exit
);
6661 class = kDNSServiceClass_IN
;
6666 FPrintF( stdout
, "Name: %s\n", gResQuery_Name
);
6667 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
6668 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
6669 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
6670 FPrintF( stdout
, "---\n" );
6672 // Call res_query().
6674 n
= res_query_ptr( gResQuery_Name
, class, type
, (u_char
*) answer
, (int) sizeof( answer
) );
6677 FPrintF( stderr
, "res_query() failed with error: %d (%s).\n", h_errno
, hstrerror( h_errno
) );
6684 FPrintF( stdout
, "Message size: %d\n\n", n
);
6685 PrintUDNSMessage( answer
, (size_t) n
, false );
6688 if( err
) exit( 1 );
6691 //===========================================================================================================================
6692 // ResolvDNSQueryCmd
6693 //===========================================================================================================================
6695 // dns_handle_t is defined as a pointer to a privately-defined struct in /usr/include/dns.h. It's defined as a void * here to
6696 // avoid including the header file.
6698 typedef void * dns_handle_t
;
6700 SOFT_LINK_FUNCTION_EX( resolv
, dns_open
, dns_handle_t
, ( const char *path
), ( path
) );
6701 SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv
, dns_free
, ( dns_handle_t
*dns
), ( dns
) );
6702 SOFT_LINK_FUNCTION_EX( resolv
, dns_query
,
6710 struct sockaddr
* from
,
6711 uint32_t * fromlen
),
6712 ( dns
, name
, dnsclass
, dnstype
, buf
, len
, from
, fromlen
) );
6714 static void ResolvDNSQueryCmd( void )
6718 dns_handle_t dns
= NULL
;
6719 uint16_t type
, class;
6722 char time
[ kTimestampBufLen
];
6723 uint8_t answer
[ 1024 ];
6725 // Make sure that the required symbols are available.
6727 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_open
) )
6729 FPrintF( stderr
, "Failed to soft link dns_open from libresolv.\n" );
6734 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_free
) )
6736 FPrintF( stderr
, "Failed to soft link dns_free from libresolv.\n" );
6741 if( !SOFT_LINK_HAS_FUNCTION( resolv
, dns_query
) )
6743 FPrintF( stderr
, "Failed to soft link dns_query from libresolv.\n" );
6750 err
= RecordTypeFromArgString( gResolvDNSQuery_Type
, &type
);
6751 require_noerr( err
, exit
);
6753 // Get record class.
6755 if( gResolvDNSQuery_Class
)
6757 err
= RecordClassFromArgString( gResolvDNSQuery_Class
, &class );
6758 require_noerr( err
, exit
);
6762 class = kDNSServiceClass_IN
;
6767 dns
= soft_dns_open( gResolvDNSQuery_Path
);
6770 FPrintF( stderr
, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path
);
6777 FPrintF( stdout
, "Name: %s\n", gResolvDNSQuery_Name
);
6778 FPrintF( stdout
, "Type: %s (%u)\n", RecordTypeToString( type
), type
);
6779 FPrintF( stdout
, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN
) ? "IN" : "???", class );
6780 FPrintF( stdout
, "Path: %s\n", gResolvDNSQuery_Path
? gResolvDNSQuery_Name
: "<NULL>" );
6781 FPrintF( stdout
, "Start time: %s\n", GetTimestampStr( time
) );
6782 FPrintF( stdout
, "---\n" );
6784 // Call dns_query().
6786 memset( &from
, 0, sizeof( from
) );
6787 fromLen
= (uint32_t) sizeof( from
);
6788 n
= soft_dns_query( dns
, gResolvDNSQuery_Name
, class, type
, (char *) answer
, (uint32_t) sizeof( answer
), &from
.sa
,
6792 FPrintF( stderr
, "dns_query() failed with error: %d (%s).\n", h_errno
, hstrerror( h_errno
) );
6799 FPrintF( stdout
, "From: %##a\n", &from
);
6800 FPrintF( stdout
, "Message size: %d\n\n", n
);
6801 PrintUDNSMessage( answer
, (size_t) n
, false );
6804 if( dns
) soft_dns_free( dns
);
6805 if( err
) exit( 1 );
6807 #endif // TARGET_OS_DARWIN
6809 //===========================================================================================================================
6811 //===========================================================================================================================
6813 static void DaemonVersionCmd( void )
6816 uint32_t size
, version
;
6819 size
= (uint32_t) sizeof( version
);
6820 err
= DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion
, &version
, &size
);
6821 require_noerr( err
, exit
);
6823 FPrintF( stdout
, "Daemon version: %s\n", SourceVersionToCString( version
, strBuf
) );
6826 if( err
) exit( 1 );
6829 //===========================================================================================================================
6831 //===========================================================================================================================
6833 static void Exit( void *inContext
)
6835 const char * const reason
= (const char *) inContext
;
6836 char time
[ kTimestampBufLen
];
6838 FPrintF( stdout
, "---\n" );
6839 FPrintF( stdout
, "End time: %s\n", GetTimestampStr( time
) );
6840 if( reason
) FPrintF( stdout
, "End reason: %s\n", reason
);
6844 //===========================================================================================================================
6846 //===========================================================================================================================
6848 static char * GetTimestampStr( char inBuffer
[ kTimestampBufLen
] )
6854 gettimeofday( &now
, NULL
);
6855 tm
= localtime( &now
.tv_sec
);
6856 require_action( tm
, exit
, *inBuffer
= '\0' );
6858 len
= strftime( inBuffer
, kTimestampBufLen
, "%Y-%m-%d %H:%M:%S", tm
);
6859 SNPrintF( &inBuffer
[ len
], kTimestampBufLen
- len
, ".%06u", (unsigned int) now
.tv_usec
);
6865 //===========================================================================================================================
6866 // GetDNSSDFlagsFromOpts
6867 //===========================================================================================================================
6869 static DNSServiceFlags
GetDNSSDFlagsFromOpts( void )
6871 DNSServiceFlags flags
;
6873 flags
= (DNSServiceFlags
) gDNSSDFlags
;
6874 if( flags
& kDNSServiceFlagsShareConnection
)
6876 FPrintF( stderr
, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
6877 kDNSServiceFlagsShareConnection
);
6880 if( gDNSSDFlag_BrowseDomains
) flags
|= kDNSServiceFlagsBrowseDomains
;
6881 if( gDNSSDFlag_DenyCellular
) flags
|= kDNSServiceFlagsDenyCellular
;
6882 if( gDNSSDFlag_DenyExpensive
) flags
|= kDNSServiceFlagsDenyExpensive
;
6883 if( gDNSSDFlag_ForceMulticast
) flags
|= kDNSServiceFlagsForceMulticast
;
6884 if( gDNSSDFlag_IncludeAWDL
) flags
|= kDNSServiceFlagsIncludeAWDL
;
6885 if( gDNSSDFlag_NoAutoRename
) flags
|= kDNSServiceFlagsNoAutoRename
;
6886 if( gDNSSDFlag_PathEvaluationDone
) flags
|= kDNSServiceFlagsPathEvaluationDone
;
6887 if( gDNSSDFlag_RegistrationDomains
) flags
|= kDNSServiceFlagsRegistrationDomains
;
6888 if( gDNSSDFlag_ReturnIntermediates
) flags
|= kDNSServiceFlagsReturnIntermediates
;
6889 if( gDNSSDFlag_Shared
) flags
|= kDNSServiceFlagsShared
;
6890 if( gDNSSDFlag_SuppressUnusable
) flags
|= kDNSServiceFlagsSuppressUnusable
;
6891 if( gDNSSDFlag_Timeout
) flags
|= kDNSServiceFlagsTimeout
;
6892 if( gDNSSDFlag_UnicastResponse
) flags
|= kDNSServiceFlagsUnicastResponse
;
6893 if( gDNSSDFlag_Unique
) flags
|= kDNSServiceFlagsUnique
;
6894 if( gDNSSDFlag_WakeOnResolve
) flags
|= kDNSServiceFlagsWakeOnResolve
;
6899 //===========================================================================================================================
6900 // CreateConnectionFromArgString
6901 //===========================================================================================================================
6904 CreateConnectionFromArgString(
6905 const char * inString
,
6906 dispatch_queue_t inQueue
,
6907 DNSServiceRef
* outSDRef
,
6908 ConnectionDesc
* outDesc
)
6911 DNSServiceRef sdRef
= NULL
;
6912 ConnectionType type
;
6913 int32_t pid
= -1; // Initializing because the analyzer claims pid may be used uninitialized.
6916 if( strcasecmp( inString
, kConnectionArg_Normal
) == 0 )
6918 err
= DNSServiceCreateConnection( &sdRef
);
6919 require_noerr( err
, exit
);
6920 type
= kConnectionType_Normal
;
6922 else if( stricmp_prefix( inString
, kConnectionArgPrefix_PID
) == 0 )
6924 const char * const pidStr
= inString
+ sizeof_string( kConnectionArgPrefix_PID
);
6926 err
= StringToInt32( pidStr
, &pid
);
6929 FPrintF( stderr
, "Invalid delegate connection PID value: %s\n", pidStr
);
6934 memset( uuid
, 0, sizeof( uuid
) );
6935 err
= DNSServiceCreateDelegateConnection( &sdRef
, pid
, uuid
);
6938 FPrintF( stderr
, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err
, pid
);
6941 type
= kConnectionType_DelegatePID
;
6943 else if( stricmp_prefix( inString
, kConnectionArgPrefix_UUID
) == 0 )
6945 const char * const uuidStr
= inString
+ sizeof_string( kConnectionArgPrefix_UUID
);
6947 check_compile_time_code( sizeof( uuid
) == sizeof( uuid_t
) );
6949 err
= StringToUUID( uuidStr
, kSizeCString
, false, uuid
);
6952 FPrintF( stderr
, "Invalid delegate connection UUID value: %s\n", uuidStr
);
6957 err
= DNSServiceCreateDelegateConnection( &sdRef
, 0, uuid
);
6960 FPrintF( stderr
, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err
, uuid
);
6963 type
= kConnectionType_DelegateUUID
;
6967 FPrintF( stderr
, "Unrecognized connection string \"%s\".\n", inString
);
6972 err
= DNSServiceSetDispatchQueue( sdRef
, inQueue
);
6973 require_noerr( err
, exit
);
6978 outDesc
->type
= type
;
6979 if( type
== kConnectionType_DelegatePID
) outDesc
->delegate
.pid
= pid
;
6980 else if( type
== kConnectionType_DelegateUUID
) memcpy( outDesc
->delegate
.uuid
, uuid
, 16 );
6985 if( sdRef
) DNSServiceRefDeallocate( sdRef
);
6989 //===========================================================================================================================
6990 // InterfaceIndexFromArgString
6991 //===========================================================================================================================
6993 static OSStatus
InterfaceIndexFromArgString( const char *inString
, uint32_t *outIndex
)
7000 ifIndex
= if_nametoindex( inString
);
7003 err
= StringToUInt32( inString
, &ifIndex
);
7006 FPrintF( stderr
, "Invalid interface value: %s\n", inString
);
7017 *outIndex
= ifIndex
;
7024 //===========================================================================================================================
7025 // RecordDataFromArgString
7026 //===========================================================================================================================
7028 #define kRDataMaxLen UINT16_C( 0xFFFF )
7030 static OSStatus
StringToSRVRData( const char *inString
, uint8_t **outPtr
, size_t *outLen
);
7031 static OSStatus
StringToTXTRData( const char *inString
, char inDelimiter
, uint8_t **outPtr
, size_t *outLen
);
7033 static OSStatus
RecordDataFromArgString( const char *inString
, uint8_t **outDataPtr
, size_t *outDataLen
)
7036 uint8_t * dataPtr
= NULL
;
7043 else if( stricmp_prefix( inString
, kRDataArgPrefix_Domain
) == 0 )
7045 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_Domain
);
7047 uint8_t dname
[ kDomainNameLengthMax
];
7049 err
= DomainNameFromString( dname
, str
, &end
);
7050 require_noerr( err
, exit
);
7052 dataLen
= (size_t)( end
- dname
);
7053 dataPtr
= malloc( dataLen
);
7054 require_action( dataPtr
, exit
, err
= kNoMemoryErr
);
7056 memcpy( dataPtr
, dname
, dataLen
);
7061 else if( stricmp_prefix( inString
, kRDataArgPrefix_File
) == 0 )
7063 const char * const path
= inString
+ sizeof_string( kRDataArgPrefix_File
);
7065 err
= CopyFileDataByPath( path
, (char **) &dataPtr
, &dataLen
);
7066 require_noerr( err
, exit
);
7067 require_action( dataLen
<= kRDataMaxLen
, exit
, err
= kSizeErr
);
7070 // Hexadecimal string
7072 else if( stricmp_prefix( inString
, kRDataArgPrefix_HexString
) == 0 )
7074 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_HexString
);
7076 err
= HexToDataCopy( str
, kSizeCString
, kHexToData_DefaultFlags
, &dataPtr
, &dataLen
, NULL
);
7077 require_noerr( err
, exit
);
7078 require_action( dataLen
<= kRDataMaxLen
, exit
, err
= kSizeErr
);
7081 // IPv4 address string
7083 else if( stricmp_prefix( inString
, kRDataArgPrefix_IPv4
) == 0 )
7085 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_IPv4
);
7089 dataPtr
= (uint8_t *) malloc( dataLen
);
7090 require_action( dataPtr
, exit
, err
= kNoMemoryErr
);
7092 err
= StringToIPv4Address( str
, kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
,
7093 (uint32_t *) dataPtr
, NULL
, NULL
, NULL
, &end
);
7094 if( !err
&& ( *end
!= '\0' ) ) err
= kMalformedErr
;
7095 require_noerr( err
, exit
);
7097 *( (uint32_t *) dataPtr
) = HostToBig32( *( (uint32_t *) dataPtr
) );
7100 // IPv6 address string
7102 else if( stricmp_prefix( inString
, kRDataArgPrefix_IPv6
) == 0 )
7104 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_IPv6
);
7108 dataPtr
= (uint8_t *) malloc( dataLen
);
7109 require_action( dataPtr
, exit
, err
= kNoMemoryErr
);
7111 err
= StringToIPv6Address( str
,
7112 kStringToIPAddressFlagsNoPort
| kStringToIPAddressFlagsNoPrefix
| kStringToIPAddressFlagsNoScope
,
7113 dataPtr
, NULL
, NULL
, NULL
, &end
);
7114 if( !err
&& ( *end
!= '\0' ) ) err
= kMalformedErr
;
7115 require_noerr( err
, exit
);
7120 else if( stricmp_prefix( inString
, kRDataArgPrefix_SRV
) == 0 )
7122 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_SRV
);
7124 err
= StringToSRVRData( str
, &dataPtr
, &dataLen
);
7125 require_noerr( err
, exit
);
7128 // String with escaped hex and octal bytes
7130 else if( stricmp_prefix( inString
, kRDataArgPrefix_String
) == 0 )
7132 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_String
);
7133 const char * const end
= str
+ strlen( str
);
7140 success
= ParseQuotedEscapedString( str
, end
, "", NULL
, 0, NULL
, &totalLen
, NULL
);
7141 require_action( success
, exit
, err
= kParamErr
);
7142 require_action( totalLen
<= kRDataMaxLen
, exit
, err
= kSizeErr
);
7145 dataPtr
= (uint8_t *) malloc( dataLen
);
7146 require_action( dataPtr
, exit
, err
= kNoMemoryErr
);
7148 success
= ParseQuotedEscapedString( str
, end
, "", (char *) dataPtr
, dataLen
, &copiedLen
, NULL
, NULL
);
7149 require_action( success
, exit
, err
= kParamErr
);
7150 check( copiedLen
== dataLen
);
7161 else if( stricmp_prefix( inString
, kRDataArgPrefix_TXT
) == 0 )
7163 const char * const str
= inString
+ sizeof_string( kRDataArgPrefix_TXT
);
7165 err
= StringToTXTRData( str
, ',', &dataPtr
, &dataLen
);
7166 require_noerr( err
, exit
);
7169 // Unrecognized format
7173 FPrintF( stderr
, "Unrecognized record data string \"%s\".\n", inString
);
7179 *outDataLen
= dataLen
;
7180 *outDataPtr
= dataPtr
;
7184 FreeNullSafe( dataPtr
);
7188 static OSStatus
StringToSRVRData( const char *inString
, uint8_t **outPtr
, size_t *outLen
)
7195 uint8_t target
[ kDomainNameLengthMax
];
7197 DataBuffer_Init( &dataBuf
, NULL
, 0, ( 3 * 2 ) + kDomainNameLengthMax
);
7199 // Parse and set the priority, weight, and port values (all three are unsigned 16-bit values).
7202 for( i
= 0; i
< 3; ++i
)
7208 value
= strtol( ptr
, &next
, 0 );
7209 require_action_quiet( ( next
!= ptr
) && ( *next
== ',' ), exit
, err
= kMalformedErr
);
7210 require_action_quiet( ( value
>= 0 ) && ( value
<= UINT16_MAX
), exit
, err
= kRangeErr
);
7213 WriteBig16( buf
, value
);
7215 err
= DataBuffer_Append( &dataBuf
, buf
, sizeof( buf
) );
7216 require_noerr( err
, exit
);
7219 // Set the target domain name.
7221 err
= DomainNameFromString( target
, ptr
, &end
);
7222 require_noerr_quiet( err
, exit
);
7224 err
= DataBuffer_Append( &dataBuf
, target
, (size_t)( end
- target
) );
7225 require_noerr( err
, exit
);
7227 err
= DataBuffer_Detach( &dataBuf
, outPtr
, outLen
);
7228 require_noerr( err
, exit
);
7231 DataBuffer_Free( &dataBuf
);
7235 static OSStatus
StringToTXTRData( const char *inString
, char inDelimiter
, uint8_t **outPtr
, size_t *outLen
)
7240 uint8_t txtStr
[ 256 ]; // Buffer for single TXT string: 1 length byte + up to 255 bytes of data.
7242 DataBuffer_Init( &dataBuf
, NULL
, 0, kRDataMaxLen
);
7247 uint8_t * dst
= &txtStr
[ 1 ];
7248 const uint8_t * const lim
= &txtStr
[ 256 ];
7251 while( *src
&& ( *src
!= inDelimiter
) )
7253 if( ( c
= *src
++ ) == '\\' )
7255 require_action_quiet( *src
!= '\0', exit
, err
= kUnderrunErr
);
7258 require_action_quiet( dst
< lim
, exit
, err
= kOverrunErr
);
7259 *dst
++ = (uint8_t) c
;
7261 txtStr
[ 0 ] = (uint8_t)( dst
- &txtStr
[ 1 ] );
7262 err
= DataBuffer_Append( &dataBuf
, txtStr
, 1 + txtStr
[ 0 ] );
7263 require_noerr( err
, exit
);
7265 if( *src
== '\0' ) break;
7269 err
= DataBuffer_Detach( &dataBuf
, outPtr
, outLen
);
7270 require_noerr( err
, exit
);
7273 DataBuffer_Free( &dataBuf
);
7277 //===========================================================================================================================
7278 // RecordTypeFromArgString
7279 //===========================================================================================================================
7283 uint16_t value
; // Record type's numeric value.
7284 const char * name
; // Record type's name as a string (e.g., "A", "PTR", "SRV").
7288 static const RecordType kRecordTypes
[] =
7292 { kDNSServiceType_A
, "A" },
7293 { kDNSServiceType_AAAA
, "AAAA" },
7294 { kDNSServiceType_PTR
, "PTR" },
7295 { kDNSServiceType_SRV
, "SRV" },
7296 { kDNSServiceType_TXT
, "TXT" },
7297 { kDNSServiceType_CNAME
, "CNAME" },
7298 { kDNSServiceType_SOA
, "SOA" },
7299 { kDNSServiceType_NSEC
, "NSEC" },
7300 { kDNSServiceType_NS
, "NS" },
7301 { kDNSServiceType_MX
, "MX" },
7302 { kDNSServiceType_ANY
, "ANY" },
7303 { kDNSServiceType_OPT
, "OPT" },
7305 // Less common types.
7307 { kDNSServiceType_MD
, "MD" },
7308 { kDNSServiceType_NS
, "NS" },
7309 { kDNSServiceType_MD
, "MD" },
7310 { kDNSServiceType_MF
, "MF" },
7311 { kDNSServiceType_MB
, "MB" },
7312 { kDNSServiceType_MG
, "MG" },
7313 { kDNSServiceType_MR
, "MR" },
7314 { kDNSServiceType_NULL
, "NULL" },
7315 { kDNSServiceType_WKS
, "WKS" },
7316 { kDNSServiceType_HINFO
, "HINFO" },
7317 { kDNSServiceType_MINFO
, "MINFO" },
7318 { kDNSServiceType_RP
, "RP" },
7319 { kDNSServiceType_AFSDB
, "AFSDB" },
7320 { kDNSServiceType_X25
, "X25" },
7321 { kDNSServiceType_ISDN
, "ISDN" },
7322 { kDNSServiceType_RT
, "RT" },
7323 { kDNSServiceType_NSAP
, "NSAP" },
7324 { kDNSServiceType_NSAP_PTR
, "NSAP_PTR" },
7325 { kDNSServiceType_SIG
, "SIG" },
7326 { kDNSServiceType_KEY
, "KEY" },
7327 { kDNSServiceType_PX
, "PX" },
7328 { kDNSServiceType_GPOS
, "GPOS" },
7329 { kDNSServiceType_LOC
, "LOC" },
7330 { kDNSServiceType_NXT
, "NXT" },
7331 { kDNSServiceType_EID
, "EID" },
7332 { kDNSServiceType_NIMLOC
, "NIMLOC" },
7333 { kDNSServiceType_ATMA
, "ATMA" },
7334 { kDNSServiceType_NAPTR
, "NAPTR" },
7335 { kDNSServiceType_KX
, "KX" },
7336 { kDNSServiceType_CERT
, "CERT" },
7337 { kDNSServiceType_A6
, "A6" },
7338 { kDNSServiceType_DNAME
, "DNAME" },
7339 { kDNSServiceType_SINK
, "SINK" },
7340 { kDNSServiceType_APL
, "APL" },
7341 { kDNSServiceType_DS
, "DS" },
7342 { kDNSServiceType_SSHFP
, "SSHFP" },
7343 { kDNSServiceType_IPSECKEY
, "IPSECKEY" },
7344 { kDNSServiceType_RRSIG
, "RRSIG" },
7345 { kDNSServiceType_DNSKEY
, "DNSKEY" },
7346 { kDNSServiceType_DHCID
, "DHCID" },
7347 { kDNSServiceType_NSEC3
, "NSEC3" },
7348 { kDNSServiceType_NSEC3PARAM
, "NSEC3PARAM" },
7349 { kDNSServiceType_HIP
, "HIP" },
7350 { kDNSServiceType_SPF
, "SPF" },
7351 { kDNSServiceType_UINFO
, "UINFO" },
7352 { kDNSServiceType_UID
, "UID" },
7353 { kDNSServiceType_GID
, "GID" },
7354 { kDNSServiceType_UNSPEC
, "UNSPEC" },
7355 { kDNSServiceType_TKEY
, "TKEY" },
7356 { kDNSServiceType_TSIG
, "TSIG" },
7357 { kDNSServiceType_IXFR
, "IXFR" },
7358 { kDNSServiceType_AXFR
, "AXFR" },
7359 { kDNSServiceType_MAILB
, "MAILB" },
7360 { kDNSServiceType_MAILA
, "MAILA" }
7363 static OSStatus
RecordTypeFromArgString( const char *inString
, uint16_t *outValue
)
7367 const RecordType
* type
;
7368 const RecordType
* const end
= kRecordTypes
+ countof( kRecordTypes
);
7370 for( type
= kRecordTypes
; type
< end
; ++type
)
7372 if( strcasecmp( type
->name
, inString
) == 0 )
7374 *outValue
= type
->value
;
7379 err
= StringToInt32( inString
, &i32
);
7380 require_noerr_quiet( err
, exit
);
7381 require_action_quiet( ( i32
>= 0 ) && ( i32
<= UINT16_MAX
), exit
, err
= kParamErr
);
7383 *outValue
= (uint16_t) i32
;
7389 //===========================================================================================================================
7390 // RecordClassFromArgString
7391 //===========================================================================================================================
7393 static OSStatus
RecordClassFromArgString( const char *inString
, uint16_t *outValue
)
7398 if( strcasecmp( inString
, "IN" ) == 0 )
7400 *outValue
= kDNSServiceClass_IN
;
7405 err
= StringToInt32( inString
, &i32
);
7406 require_noerr_quiet( err
, exit
);
7407 require_action_quiet( ( i32
>= 0 ) && ( i32
<= UINT16_MAX
), exit
, err
= kParamErr
);
7409 *outValue
= (uint16_t) i32
;
7415 //===========================================================================================================================
7416 // InterfaceIndexToName
7417 //===========================================================================================================================
7419 static char * InterfaceIndexToName( uint32_t inIfIndex
, char inNameBuf
[ kInterfaceNameBufLen
] )
7423 case kDNSServiceInterfaceIndexAny
:
7424 strlcpy( inNameBuf
, "Any", kInterfaceNameBufLen
);
7427 case kDNSServiceInterfaceIndexLocalOnly
:
7428 strlcpy( inNameBuf
, "LocalOnly", kInterfaceNameBufLen
);
7431 case kDNSServiceInterfaceIndexUnicast
:
7432 strlcpy( inNameBuf
, "Unicast", kInterfaceNameBufLen
);
7435 case kDNSServiceInterfaceIndexP2P
:
7436 strlcpy( inNameBuf
, "P2P", kInterfaceNameBufLen
);
7439 #if( defined( kDNSServiceInterfaceIndexBLE ) )
7440 case kDNSServiceInterfaceIndexBLE
:
7441 strlcpy( inNameBuf
, "BLE", kInterfaceNameBufLen
);
7449 name
= if_indextoname( inIfIndex
, inNameBuf
);
7450 if( !name
) strlcpy( inNameBuf
, "NO NAME", kInterfaceNameBufLen
);
7455 return( inNameBuf
);
7458 //===========================================================================================================================
7459 // RecordTypeToString
7460 //===========================================================================================================================
7462 static const char * RecordTypeToString( unsigned int inValue
)
7464 const RecordType
* type
;
7465 const RecordType
* const end
= kRecordTypes
+ countof( kRecordTypes
);
7467 for( type
= kRecordTypes
; type
< end
; ++type
)
7469 if( type
->value
== inValue
) return( type
->name
);
7474 //===========================================================================================================================
7475 // DNSMessageExtractDomainName
7476 //===========================================================================================================================
7478 #define IsCompressionByte( X ) ( ( ( X ) & 0xC0 ) == 0xC0 )
7481 DNSMessageExtractDomainName(
7482 const uint8_t * inMsgPtr
,
7484 const uint8_t * inNamePtr
,
7485 uint8_t inBuf
[ kDomainNameLengthMax
],
7486 const uint8_t ** outNextPtr
)
7489 const uint8_t * label
;
7491 const uint8_t * nextLabel
;
7492 const uint8_t * const msgEnd
= inMsgPtr
+ inMsgLen
;
7493 uint8_t * dst
= inBuf
;
7494 const uint8_t * const dstLim
= inBuf
? ( inBuf
+ kDomainNameLengthMax
) : NULL
;
7495 const uint8_t * nameEnd
= NULL
;
7497 require_action( ( inNamePtr
>= inMsgPtr
) && ( inNamePtr
< msgEnd
), exit
, err
= kRangeErr
);
7499 for( label
= inNamePtr
; ( labelLen
= label
[ 0 ] ) != 0; label
= nextLabel
)
7501 if( labelLen
<= kDomainLabelLengthMax
)
7503 nextLabel
= label
+ 1 + labelLen
;
7504 require_action( nextLabel
< msgEnd
, exit
, err
= kUnderrunErr
);
7507 require_action( ( dstLim
- dst
) > ( 1 + labelLen
), exit
, err
= kOverrunErr
);
7508 memcpy( dst
, label
, 1 + labelLen
);
7509 dst
+= ( 1 + labelLen
);
7512 else if( IsCompressionByte( labelLen
) )
7516 require_action( ( msgEnd
- label
) >= 2, exit
, err
= kUnderrunErr
);
7519 nameEnd
= label
+ 2;
7522 offset
= (uint16_t)( ( ( label
[ 0 ] & 0x3F ) << 8 ) | label
[ 1 ] );
7523 nextLabel
= inMsgPtr
+ offset
;
7524 require_action( nextLabel
< msgEnd
, exit
, err
= kUnderrunErr
);
7525 require_action( !IsCompressionByte( nextLabel
[ 0 ] ), exit
, err
= kMalformedErr
);
7529 dlogassert( "Unhandled label length 0x%02X\n", labelLen
);
7530 err
= kMalformedErr
;
7536 if( !nameEnd
) nameEnd
= label
+ 1;
7538 if( outNextPtr
) *outNextPtr
= nameEnd
;
7545 //===========================================================================================================================
7546 // DNSMessageExtractDomainNameString
7547 //===========================================================================================================================
7550 DNSMessageExtractDomainNameString(
7551 const void * inMsgPtr
,
7553 const void * inNamePtr
,
7554 char inBuf
[ kDNSServiceMaxDomainName
],
7555 const uint8_t ** outNextPtr
)
7558 const uint8_t * nextPtr
;
7559 uint8_t domainName
[ kDomainNameLengthMax
];
7561 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, inNamePtr
, domainName
, &nextPtr
);
7562 require_noerr( err
, exit
);
7564 err
= DomainNameToString( domainName
, NULL
, inBuf
, NULL
);
7565 require_noerr( err
, exit
);
7567 if( outNextPtr
) *outNextPtr
= nextPtr
;
7573 //===========================================================================================================================
7574 // DNSMessageExtractRecord
7575 //===========================================================================================================================
7582 uint8_t rdLength
[ 2 ];
7587 check_compile_time( offsetof( DNSRecordFields
, rdata
) == 10 );
7590 DNSMessageExtractRecord(
7591 const uint8_t * inMsgPtr
,
7593 const uint8_t * inPtr
,
7594 uint8_t inNameBuf
[ kDomainNameLengthMax
],
7596 uint16_t * outClass
,
7598 const uint8_t ** outRDataPtr
,
7599 size_t * outRDataLen
,
7600 const uint8_t ** outPtr
)
7603 const uint8_t * const msgEnd
= inMsgPtr
+ inMsgLen
;
7604 const uint8_t * ptr
;
7605 const DNSRecordFields
* record
;
7608 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, inPtr
, inNameBuf
, &ptr
);
7609 require_noerr_quiet( err
, exit
);
7610 require_action_quiet( (size_t)( msgEnd
- ptr
) >= offsetof( DNSRecordFields
, rdata
), exit
, err
= kUnderrunErr
);
7612 record
= (DNSRecordFields
*) ptr
;
7613 rdLength
= ReadBig16( record
->rdLength
);
7614 require_action_quiet( (size_t)( msgEnd
- record
->rdata
) >= rdLength
, exit
, err
= kUnderrunErr
);
7616 if( outType
) *outType
= ReadBig16( record
->type
);
7617 if( outClass
) *outClass
= ReadBig16( record
->class );
7618 if( outTTL
) *outTTL
= ReadBig32( record
->ttl
);
7619 if( outRDataPtr
) *outRDataPtr
= record
->rdata
;
7620 if( outRDataLen
) *outRDataLen
= rdLength
;
7621 if( outPtr
) *outPtr
= record
->rdata
+ rdLength
;
7627 //===========================================================================================================================
7628 // DNSMessageGetAnswerSection
7629 //===========================================================================================================================
7631 static OSStatus
DNSMessageGetAnswerSection( const uint8_t *inMsgPtr
, size_t inMsgLen
, const uint8_t **outPtr
)
7634 const uint8_t * const msgEnd
= inMsgPtr
+ inMsgLen
;
7635 unsigned int questionCount
, i
;
7636 const DNSHeader
* hdr
;
7637 const uint8_t * ptr
;
7639 require_action_quiet( inMsgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
7641 hdr
= (DNSHeader
*) inMsgPtr
;
7642 questionCount
= DNSHeaderGetQuestionCount( hdr
);
7644 ptr
= (uint8_t *)( hdr
+ 1 );
7645 for( i
= 0; i
< questionCount
; ++i
)
7647 err
= DNSMessageExtractDomainName( inMsgPtr
, inMsgLen
, ptr
, NULL
, &ptr
);
7648 require_noerr( err
, exit
);
7649 require_action_quiet( ( msgEnd
- ptr
) >= 4, exit
, err
= kUnderrunErr
);
7653 if( outPtr
) *outPtr
= ptr
;
7660 //===========================================================================================================================
7661 // DNSRecordDataToString
7662 //===========================================================================================================================
7665 DNSRecordDataToString(
7666 const void * inRDataPtr
,
7668 unsigned int inRDataType
,
7669 const void * inMsgPtr
,
7674 const uint8_t * const rdataPtr
= (uint8_t *) inRDataPtr
;
7675 const uint8_t * const rdataEnd
= rdataPtr
+ inRDataLen
;
7677 const uint8_t * ptr
;
7679 char domainNameStr
[ kDNSServiceMaxDomainName
];
7682 if( inRDataType
== kDNSServiceType_A
)
7684 require_action_quiet( inRDataLen
== 4, exit
, err
= kMalformedErr
);
7686 ASPrintF( &rdataStr
, "%.4a", rdataPtr
);
7687 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
7689 else if( inRDataType
== kDNSServiceType_AAAA
)
7691 require_action_quiet( inRDataLen
== 16, exit
, err
= kMalformedErr
);
7693 ASPrintF( &rdataStr
, "%.16a", rdataPtr
);
7694 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
7696 else if( ( inRDataType
== kDNSServiceType_PTR
) || ( inRDataType
== kDNSServiceType_CNAME
) ||
7697 ( inRDataType
== kDNSServiceType_NS
) )
7701 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, NULL
);
7702 require_noerr( err
, exit
);
7706 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, NULL
);
7707 require_noerr( err
, exit
);
7710 rdataStr
= strdup( domainNameStr
);
7711 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
7713 else if( inRDataType
== kDNSServiceType_SRV
)
7715 uint16_t priority
, weight
, port
;
7716 const uint8_t * target
;
7718 require_action_quiet( ( rdataPtr
+ 6 ) < rdataEnd
, exit
, err
= kMalformedErr
);
7720 priority
= ReadBig16( rdataPtr
);
7721 weight
= ReadBig16( rdataPtr
+ 2 );
7722 port
= ReadBig16( rdataPtr
+ 4 );
7723 target
= rdataPtr
+ 6;
7727 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, target
, domainNameStr
, NULL
);
7728 require_noerr( err
, exit
);
7732 err
= DomainNameToString( target
, rdataEnd
, domainNameStr
, NULL
);
7733 require_noerr( err
, exit
);
7736 ASPrintF( &rdataStr
, "%u %u %u %s", priority
, weight
, port
, domainNameStr
);
7737 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
7739 else if( inRDataType
== kDNSServiceType_TXT
)
7741 require_action_quiet( inRDataLen
> 0, exit
, err
= kMalformedErr
);
7743 if( inRDataLen
== 1 )
7745 ASPrintF( &rdataStr
, "%#H", rdataPtr
, (int) inRDataLen
, INT_MAX
);
7746 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
7750 ASPrintF( &rdataStr
, "%#{txt}", rdataPtr
, inRDataLen
);
7751 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
7754 else if( inRDataType
== kDNSServiceType_SOA
)
7756 uint32_t serial
, refresh
, retry
, expire
, minimum
;
7760 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
7761 require_noerr( err
, exit
);
7763 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
7765 rdataStr
= strdup( domainNameStr
);
7766 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
7768 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, ptr
, domainNameStr
, &ptr
);
7769 require_noerr( err
, exit
);
7773 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
7774 require_noerr( err
, exit
);
7776 rdataStr
= strdup( domainNameStr
);
7777 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
7779 err
= DomainNameToString( ptr
, rdataEnd
, domainNameStr
, &ptr
);
7780 require_noerr( err
, exit
);
7783 require_action_quiet( ( ptr
+ 20 ) == rdataEnd
, exit
, err
= kMalformedErr
);
7785 serial
= ReadBig32( ptr
);
7786 refresh
= ReadBig32( ptr
+ 4 );
7787 retry
= ReadBig32( ptr
+ 8 );
7788 expire
= ReadBig32( ptr
+ 12 );
7789 minimum
= ReadBig32( ptr
+ 16 );
7791 n
= AppendPrintF( &rdataStr
, " %s %u %u %u %u %u\n", domainNameStr
, serial
, refresh
, retry
, expire
, minimum
);
7792 require_action( n
> 0, exit
, err
= kUnknownErr
);
7794 else if( inRDataType
== kDNSServiceType_NSEC
)
7796 unsigned int windowBlock
, bitmapLen
, i
, recordType
;
7797 const uint8_t * bitmapPtr
;
7801 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, rdataPtr
, domainNameStr
, &ptr
);
7802 require_noerr( err
, exit
);
7806 err
= DomainNameToString( rdataPtr
, rdataEnd
, domainNameStr
, &ptr
);
7807 require_noerr( err
, exit
);
7810 require_action_quiet( ptr
< rdataEnd
, exit
, err
= kMalformedErr
);
7812 rdataStr
= strdup( domainNameStr
);
7813 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
7815 for( ; ptr
< rdataEnd
; ptr
+= ( 2 + bitmapLen
) )
7817 require_action_quiet( ( ptr
+ 2 ) < rdataEnd
, exit
, err
= kMalformedErr
);
7819 windowBlock
= ptr
[ 0 ];
7820 bitmapLen
= ptr
[ 1 ];
7821 bitmapPtr
= &ptr
[ 2 ];
7823 require_action_quiet( ( bitmapLen
>= 1 ) && ( bitmapLen
<= 32 ) , exit
, err
= kMalformedErr
);
7824 require_action_quiet( ( bitmapPtr
+ bitmapLen
) <= rdataEnd
, exit
, err
= kMalformedErr
);
7826 for( i
= 0; i
< BitArray_MaxBits( bitmapLen
); ++i
)
7828 if( BitArray_GetBit( bitmapPtr
, bitmapLen
, i
) )
7830 recordType
= ( windowBlock
* 256 ) + i
;
7831 n
= AppendPrintF( &rdataStr
, " %s", RecordTypeToString( recordType
) );
7832 require_action( n
> 0, exit
, err
= kUnknownErr
);
7837 else if( inRDataType
== kDNSServiceType_MX
)
7839 uint16_t preference
;
7840 const uint8_t * exchange
;
7842 require_action_quiet( ( rdataPtr
+ 2 ) < rdataEnd
, exit
, err
= kMalformedErr
);
7844 preference
= ReadBig16( rdataPtr
);
7845 exchange
= &rdataPtr
[ 2 ];
7849 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, exchange
, domainNameStr
, NULL
);
7850 require_noerr( err
, exit
);
7854 err
= DomainNameToString( exchange
, rdataEnd
, domainNameStr
, NULL
);
7855 require_noerr( err
, exit
);
7858 n
= ASPrintF( &rdataStr
, "%u %s", preference
, domainNameStr
);
7859 require_action( n
> 0, exit
, err
= kUnknownErr
);
7863 err
= kNotHandledErr
;
7868 *outString
= rdataStr
;
7873 FreeNullSafe( rdataStr
);
7877 //===========================================================================================================================
7878 // DomainNameAppendString
7879 //===========================================================================================================================
7882 DomainNameAppendString(
7883 uint8_t inDomainName
[ kDomainNameLengthMax
],
7884 const char * inString
,
7885 uint8_t ** outEndPtr
)
7890 const uint8_t * const nameLim
= inDomainName
+ kDomainNameLengthMax
;
7892 for( root
= inDomainName
; ( root
< nameLim
) && *root
; root
+= ( 1 + *root
) ) {}
7893 require_action_quiet( root
< nameLim
, exit
, err
= kMalformedErr
);
7895 // If the string is a single dot, denoting the root domain, then there are no non-empty labels.
7898 if( ( src
[ 0 ] == '.' ) && ( src
[ 1 ] == '\0' ) ) ++src
;
7901 uint8_t * const label
= root
;
7902 const uint8_t * const labelLim
= Min( &label
[ 1 + kDomainLabelLengthMax
], nameLim
- 1 );
7908 while( *src
&& ( ( c
= *src
++ ) != '.' ) )
7912 require_action_quiet( *src
!= '\0', exit
, err
= kUnderrunErr
);
7914 if( isdigit_safe( c
) && isdigit_safe( src
[ 0 ] ) && isdigit_safe( src
[ 1 ] ) )
7916 const int decimal
= ( ( c
- '0' ) * 100 ) + ( ( src
[ 0 ] - '0' ) * 10 ) + ( src
[ 1 ] - '0' );
7918 if( decimal
<= 255 )
7925 require_action_quiet( dst
< labelLim
, exit
, err
= kOverrunErr
);
7926 *dst
++ = (uint8_t) c
;
7929 labelLen
= (size_t)( dst
- &label
[ 1 ] );
7930 require_action_quiet( labelLen
> 0, exit
, err
= kMalformedErr
);
7932 label
[ 0 ] = (uint8_t) labelLen
;
7937 if( outEndPtr
) *outEndPtr
= root
+ 1;
7944 //===========================================================================================================================
7946 //===========================================================================================================================
7948 static Boolean
DomainNameEqual( const uint8_t *inName1
, const uint8_t *inName2
)
7950 const uint8_t * p1
= inName1
;
7951 const uint8_t * p2
= inName2
;
7956 if( ( len
= *p1
++ ) != *p2
++ ) return( false );
7957 if( len
== 0 ) break;
7958 for( ; len
> 0; ++p1
, ++p2
, --len
)
7960 if( tolower_safe( *p1
) != tolower_safe( *p2
) ) return( false );
7966 //===========================================================================================================================
7967 // DomainNameFromString
7968 //===========================================================================================================================
7971 DomainNameFromString(
7972 uint8_t inDomainName
[ kDomainNameLengthMax
],
7973 const char * inString
,
7974 uint8_t ** outEndPtr
)
7976 inDomainName
[ 0 ] = 0;
7977 return( DomainNameAppendString( inDomainName
, inString
, outEndPtr
) );
7980 //===========================================================================================================================
7981 // DomainNameToString
7982 //===========================================================================================================================
7986 const uint8_t * inDomainName
,
7987 const uint8_t * inEnd
,
7988 char inBuf
[ kDNSServiceMaxDomainName
],
7989 const uint8_t ** outNextPtr
)
7992 const uint8_t * label
;
7994 const uint8_t * nextLabel
;
7996 const uint8_t * src
;
7998 require_action( !inEnd
|| ( inDomainName
< inEnd
), exit
, err
= kUnderrunErr
);
8000 // Convert each label up until the root label, i.e., the zero-length label.
8003 for( label
= inDomainName
; ( labelLen
= label
[ 0 ] ) != 0; label
= nextLabel
)
8005 require_action( labelLen
<= kDomainLabelLengthMax
, exit
, err
= kMalformedErr
);
8007 nextLabel
= &label
[ 1 ] + labelLen
;
8008 require_action( ( nextLabel
- inDomainName
) < kDomainNameLengthMax
, exit
, err
= kMalformedErr
);
8009 require_action( !inEnd
|| ( nextLabel
< inEnd
), exit
, err
= kUnderrunErr
);
8011 for( src
= &label
[ 1 ]; src
< nextLabel
; ++src
)
8013 if( isprint_safe( *src
) )
8015 if( ( *src
== '.' ) || ( *src
== '\\' ) || ( *src
== ' ' ) ) *dst
++ = '\\';
8016 *dst
++ = (char) *src
;
8021 *dst
++ = '0' + ( *src
/ 100 );
8022 *dst
++ = '0' + ( ( *src
/ 10 ) % 10 );
8023 *dst
++ = '0' + ( *src
% 10 );
8029 // At this point, label points to the root label.
8030 // If the root label was the only label, then write a dot for it.
8032 if( label
== inDomainName
) *dst
++ = '.';
8034 if( outNextPtr
) *outNextPtr
= label
+ 1;
8041 //===========================================================================================================================
8043 //===========================================================================================================================
8045 #define DNSFlagsOpCodeToString( X ) ( \
8046 ( (X) == kDNSOpCode_Query ) ? "Query" : \
8047 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \
8048 ( (X) == kDNSOpCode_Status ) ? "Status" : \
8049 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \
8050 ( (X) == kDNSOpCode_Update ) ? "Update" : \
8053 #define DNSFlagsRCodeToString( X ) ( \
8054 ( (X) == kDNSRCode_NoError ) ? "NoError" : \
8055 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \
8056 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \
8057 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \
8058 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \
8059 ( (X) == kDNSRCode_Refused ) ? "Refused" : \
8062 #define DNSFlagsGetOpCode( X ) ( ( (X) >> 11 ) & 0x0F )
8063 #define DNSFlagsGetRCode( X ) ( (X) & 0x0F )
8065 static OSStatus
PrintDNSMessage( const uint8_t *inMsgPtr
, size_t inMsgLen
, const Boolean inIsMDNS
, const Boolean inPrintRaw
)
8068 const DNSHeader
* hdr
;
8069 const uint8_t * const msgEnd
= inMsgPtr
+ inMsgLen
;
8070 const uint8_t * ptr
;
8071 unsigned int id
, flags
, opcode
, rcode
;
8072 unsigned int questionCount
, answerCount
, authorityCount
, additionalCount
, i
, totalRRCount
;
8073 char nameStr
[ kDNSServiceMaxDomainName
];
8075 require_action_quiet( inMsgLen
>= kDNSHeaderLength
, exit
, err
= kSizeErr
);
8077 hdr
= (DNSHeader
*) inMsgPtr
;
8078 id
= DNSHeaderGetID( hdr
);
8079 flags
= DNSHeaderGetFlags( hdr
);
8080 questionCount
= DNSHeaderGetQuestionCount( hdr
);
8081 answerCount
= DNSHeaderGetAnswerCount( hdr
);
8082 authorityCount
= DNSHeaderGetAuthorityCount( hdr
);
8083 additionalCount
= DNSHeaderGetAdditionalCount( hdr
);
8084 opcode
= DNSFlagsGetOpCode( flags
);
8085 rcode
= DNSFlagsGetRCode( flags
);
8087 FPrintF( stdout
, "ID: 0x%04X (%u)\n", id
, id
);
8088 FPrintF( stdout
, "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA %s\n",
8090 ( flags
& kDNSHeaderFlag_Response
) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode
),
8091 ( flags
& kDNSHeaderFlag_AuthAnswer
) ? ' ' : '!',
8092 ( flags
& kDNSHeaderFlag_Truncation
) ? ' ' : '!',
8093 ( flags
& kDNSHeaderFlag_RecursionDesired
) ? ' ' : '!',
8094 ( flags
& kDNSHeaderFlag_RecursionAvailable
) ? ' ' : '!',
8095 DNSFlagsRCodeToString( rcode
) );
8096 FPrintF( stdout
, "Question count: %u\n", questionCount
);
8097 FPrintF( stdout
, "Answer count: %u\n", answerCount
);
8098 FPrintF( stdout
, "Authority count: %u\n", authorityCount
);
8099 FPrintF( stdout
, "Additional count: %u\n", additionalCount
);
8101 ptr
= (uint8_t *)( hdr
+ 1 );
8102 for( i
= 0; i
< questionCount
; ++i
)
8104 unsigned int qType
, qClass
;
8107 err
= DNSMessageExtractDomainNameString( inMsgPtr
, inMsgLen
, ptr
, nameStr
, &ptr
);
8108 require_noerr( err
, exit
);
8110 if( ( msgEnd
- ptr
) < 4 )
8116 qType
= ReadBig16( ptr
);
8118 qClass
= ReadBig16( ptr
);
8121 isQU
= ( inIsMDNS
&& ( qClass
& kQClassUnicastResponseBit
) ) ? true : false;
8122 if( inIsMDNS
) qClass
&= ~kQClassUnicastResponseBit
;
8124 if( i
== 0 ) FPrintF( stdout
, "\nQUESTION SECTION\n" );
8126 FPrintF( stdout
, "%s %2s %?2s%?2u %-5s\n",
8127 nameStr
, inIsMDNS
? ( isQU
? "QU" : "QM" ) : "",
8128 ( qClass
== kDNSServiceClass_IN
), "IN", ( qClass
!= kDNSServiceClass_IN
), qClass
,
8129 RecordTypeToString( qType
) );
8132 totalRRCount
= answerCount
+ authorityCount
+ additionalCount
;
8133 for( i
= 0; i
< totalRRCount
; ++i
)
8138 const uint8_t * rdataPtr
;
8142 uint8_t name
[ kDomainNameLengthMax
];
8144 err
= DNSMessageExtractRecord( inMsgPtr
, inMsgLen
, ptr
, name
, &type
, &class, &ttl
, &rdataPtr
, &rdataLen
, &ptr
);
8145 require_noerr( err
, exit
);
8147 err
= DomainNameToString( name
, NULL
, nameStr
, NULL
);
8148 require_noerr( err
, exit
);
8150 cacheFlush
= ( inIsMDNS
&& ( class & kRRClassCacheFlushBit
) ) ? true : false;
8151 if( inIsMDNS
) class &= ~kRRClassCacheFlushBit
;
8154 if( !inPrintRaw
) DNSRecordDataToString( rdataPtr
, rdataLen
, type
, inMsgPtr
, inMsgLen
, &rdataStr
);
8157 ASPrintF( &rdataStr
, "%#H", rdataPtr
, (int) rdataLen
, INT_MAX
);
8158 require_action( rdataStr
, exit
, err
= kNoMemoryErr
);
8161 if( answerCount
&& ( i
== 0 ) ) FPrintF( stdout
, "\nANSWER SECTION\n" );
8162 else if( authorityCount
&& ( i
== answerCount
) ) FPrintF( stdout
, "\nAUTHORITY SECTION\n" );
8163 else if( additionalCount
&& ( i
== ( answerCount
+ authorityCount
) ) ) FPrintF( stdout
, "\nADDITIONAL SECTION\n" );
8165 FPrintF( stdout
, "%-42s %6u %2s %?2s%?2u %-5s %s\n",
8166 nameStr
, ttl
, cacheFlush
? "CF" : "",
8167 ( class == kDNSServiceClass_IN
), "IN", ( class != kDNSServiceClass_IN
), class,
8168 RecordTypeToString( type
), rdataStr
);
8171 FPrintF( stdout
, "\n" );
8178 //===========================================================================================================================
8179 // WriteDNSQueryMessage
8180 //===========================================================================================================================
8183 WriteDNSQueryMessage(
8184 uint8_t inMsg
[ kDNSQueryMessageMaxLen
],
8187 const char * inQName
,
8190 size_t * outMsgLen
)
8193 DNSHeader
* const hdr
= (DNSHeader
*) inMsg
;
8197 WriteBig16( hdr
->id
, inMsgID
);
8198 WriteBig16( hdr
->flags
, inFlags
);
8199 WriteBig16( hdr
->questionCount
, 1 );
8200 WriteBig16( hdr
->answerCount
, 0 );
8201 WriteBig16( hdr
->authorityCount
, 0 );
8202 WriteBig16( hdr
->additionalCount
, 0 );
8204 ptr
= (uint8_t *)( hdr
+ 1 );
8205 err
= DomainNameFromString( ptr
, inQName
, &ptr
);
8206 require_noerr_quiet( err
, exit
);
8208 WriteBig16( ptr
, inQType
);
8210 WriteBig16( ptr
, inQClass
);
8212 msgLen
= (size_t)( ptr
- inMsg
);
8213 check( msgLen
<= kDNSQueryMessageMaxLen
);
8215 if( outMsgLen
) *outMsgLen
= msgLen
;
8221 //===========================================================================================================================
8222 // DispatchSignalSourceCreate
8223 //===========================================================================================================================
8226 DispatchSignalSourceCreate(
8228 DispatchHandler inEventHandler
,
8230 dispatch_source_t
* outSource
)
8233 dispatch_source_t source
;
8235 source
= dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL
, (uintptr_t) inSignal
, 0, dispatch_get_main_queue() );
8236 require_action( source
, exit
, err
= kUnknownErr
);
8238 dispatch_set_context( source
, inContext
);
8239 dispatch_source_set_event_handler_f( source
, inEventHandler
);
8241 *outSource
= source
;
8248 //===========================================================================================================================
8249 // DispatchReadSourceCreate
8250 //===========================================================================================================================
8253 DispatchReadSourceCreate(
8255 DispatchHandler inEventHandler
,
8256 DispatchHandler inCancelHandler
,
8258 dispatch_source_t
* outSource
)
8261 dispatch_source_t source
;
8263 source
= dispatch_source_create( DISPATCH_SOURCE_TYPE_READ
, (uintptr_t) inSock
, 0, dispatch_get_main_queue() );
8264 require_action( source
, exit
, err
= kUnknownErr
);
8266 dispatch_set_context( source
, inContext
);
8267 dispatch_source_set_event_handler_f( source
, inEventHandler
);
8268 dispatch_source_set_cancel_handler_f( source
, inCancelHandler
);
8270 *outSource
= source
;
8277 //===========================================================================================================================
8278 // DispatchTimerCreate
8279 //===========================================================================================================================
8282 DispatchTimerCreate(
8283 dispatch_time_t inStart
,
8284 uint64_t inIntervalNs
,
8285 uint64_t inLeewayNs
,
8286 DispatchHandler inEventHandler
,
8287 DispatchHandler inCancelHandler
,
8289 dispatch_source_t
* outTimer
)
8292 dispatch_source_t timer
;
8294 timer
= dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, dispatch_get_main_queue() );
8295 require_action( timer
, exit
, err
= kUnknownErr
);
8297 dispatch_source_set_timer( timer
, inStart
, inIntervalNs
, inLeewayNs
);
8298 dispatch_set_context( timer
, inContext
);
8299 dispatch_source_set_event_handler_f( timer
, inEventHandler
);
8300 dispatch_source_set_cancel_handler_f( timer
, inCancelHandler
);
8309 //===========================================================================================================================
8310 // ServiceTypeDescription
8311 //===========================================================================================================================
8315 const char * name
; // Name of the service type in two-label "_service._proto" format.
8316 const char * description
; // Description of the service type.
8320 // A Non-comprehensive table of DNS-SD service types
8322 static const ServiceType kServiceTypes
[] =
8324 { "_acp-sync._tcp", "AirPort Base Station Sync" },
8325 { "_adisk._tcp", "Automatic Disk Discovery" },
8326 { "_afpovertcp._tcp", "Apple File Sharing" },
8327 { "_airdrop._tcp", "AirDrop" },
8328 { "_airplay._tcp", "AirPlay" },
8329 { "_airport._tcp", "AirPort Base Station" },
8330 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
8331 { "_eppc._tcp", "Remote AppleEvents" },
8332 { "_ftp._tcp", "File Transfer Protocol" },
8333 { "_home-sharing._tcp", "Home Sharing" },
8334 { "_homekit._tcp", "HomeKit" },
8335 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
8336 { "_https._tcp", "HTTP over SSL/TLS" },
8337 { "_ipp._tcp", "Internet Printing Protocol" },
8338 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
8339 { "_mediaremotetv._tcp", "Media Remote" },
8340 { "_net-assistant._tcp", "Apple Remote Desktop" },
8341 { "_od-master._tcp", "OpenDirectory Master" },
8342 { "_nfs._tcp", "Network File System" },
8343 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
8344 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
8345 { "_raop._tcp", "Remote Audio Output Protocol" },
8346 { "_rfb._tcp", "Remote Frame Buffer" },
8347 { "_scanner._tcp", "Bonjour Scanning" },
8348 { "_smb._tcp", "Server Message Block over TCP/IP" },
8349 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
8350 { "_sleep-proxy._udp", "Sleep Proxy Server" },
8351 { "_ssh._tcp", "SSH Remote Login Protocol" },
8352 { "_teleport._tcp", "teleport" },
8353 { "_tftp._tcp", "Trivial File Transfer Protocol" },
8354 { "_workstation._tcp", "Workgroup Manager" },
8355 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
8356 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
8359 static const char * ServiceTypeDescription( const char *inName
)
8361 const ServiceType
* serviceType
;
8362 const ServiceType
* const end
= kServiceTypes
+ countof( kServiceTypes
);
8364 for( serviceType
= kServiceTypes
; serviceType
< end
; ++serviceType
)
8366 if( strcasecmp( inName
, serviceType
->name
) == 0 ) return( serviceType
->description
);
8371 //===========================================================================================================================
8372 // SocketContextCancelHandler
8373 //===========================================================================================================================
8375 static void SocketContextCancelHandler( void *inContext
)
8377 SocketContext
* const context
= (SocketContext
*) inContext
;
8379 ForgetSocket( &context
->sock
);
8383 //===========================================================================================================================
8385 //===========================================================================================================================
8387 static OSStatus
StringToInt32( const char *inString
, int32_t *outValue
)
8393 value
= strtol( inString
, &endPtr
, 0 );
8394 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
8395 require_action_quiet( ( value
>= INT32_MIN
) && ( value
<= INT32_MAX
), exit
, err
= kRangeErr
);
8397 *outValue
= (int32_t) value
;
8404 //===========================================================================================================================
8406 //===========================================================================================================================
8408 static OSStatus
StringToUInt32( const char *inString
, uint32_t *outValue
)
8414 value
= (uint32_t) strtol( inString
, &endPtr
, 0 );
8415 require_action_quiet( ( *endPtr
== '\0' ) && ( endPtr
!= inString
), exit
, err
= kParamErr
);
8424 #if( TARGET_OS_DARWIN )
8425 //===========================================================================================================================
8426 // GetDefaultDNSServer
8427 //===========================================================================================================================
8429 static OSStatus
GetDefaultDNSServer( sockaddr_ip
*outAddr
)
8432 dns_config_t
* config
;
8433 struct sockaddr
* addr
;
8436 config
= dns_configuration_copy();
8437 require_action( config
, exit
, err
= kUnknownErr
);
8440 for( i
= 0; i
< config
->n_resolver
; ++i
)
8442 const dns_resolver_t
* const resolver
= config
->resolver
[ i
];
8444 if( !resolver
->domain
&& ( resolver
->n_nameserver
> 0 ) )
8446 addr
= resolver
->nameserver
[ 0 ];
8450 require_action_quiet( addr
, exit
, err
= kNotFoundErr
);
8452 SockAddrCopy( addr
, outAddr
);
8456 if( config
) dns_configuration_free( config
);
8461 //===========================================================================================================================
8464 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
8465 //===========================================================================================================================
8467 OSStatus
SocketWriteAll( SocketRef inSock
, const void *inData
, size_t inSize
, int32_t inTimeoutSecs
)
8470 const uint8_t * src
;
8471 const uint8_t * end
;
8473 struct timeval timeout
;
8476 FD_ZERO( &writeSet
);
8477 src
= (const uint8_t *) inData
;
8481 FD_SET( inSock
, &writeSet
);
8482 timeout
.tv_sec
= inTimeoutSecs
;
8483 timeout
.tv_usec
= 0;
8484 n
= select( (int)( inSock
+ 1 ), NULL
, &writeSet
, NULL
, &timeout
);
8485 if( n
== 0 ) { err
= kTimeoutErr
; goto exit
; }
8486 err
= map_socket_value_errno( inSock
, n
> 0, n
);
8487 require_noerr( err
, exit
);
8489 n
= send( inSock
, (char *) src
, (size_t)( end
- src
), 0 );
8490 err
= map_socket_value_errno( inSock
, n
>= 0, n
);
8491 if( err
== EINTR
) continue;
8492 require_noerr( err
, exit
);
8502 //===========================================================================================================================
8505 // Warning: "inBuffer" may be modified even in error cases.
8507 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
8508 //===========================================================================================================================
8510 static OSStatus
ParseIPv4Address( const char *inStr
, uint8_t inBuffer
[ 4 ], const char **outStr
)
8526 for( ; ( c
= *inStr
) != '\0'; ++inStr
)
8528 if( isdigit_safe( c
) )
8530 v
= ( *dst
* 10 ) + ( c
- '0' );
8531 require_action_quiet( v
<= 255, exit
, err
= kRangeErr
);
8536 require_action_quiet( segments
<= 4, exit
, err
= kOverrunErr
);
8540 else if( ( c
== '.' ) && sawDigit
)
8542 require_action_quiet( segments
< 4, exit
, err
= kMalformedErr
);
8551 require_action_quiet( segments
== 4, exit
, err
= kUnderrunErr
);
8560 //===========================================================================================================================
8561 // StringToIPv4Address
8563 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
8564 //===========================================================================================================================
8567 StringToIPv4Address(
8569 StringToIPAddressFlags inFlags
,
8572 uint32_t * outSubnet
,
8573 uint32_t * outRouter
,
8574 const char ** outStr
)
8584 uint32_t subnetMask
;
8587 require_action( inStr
, exit
, err
= kParamErr
);
8589 // Parse the address-only part of the address (e.g. "1.2.3.4").
8591 err
= ParseIPv4Address( inStr
, buf
, &inStr
);
8592 require_noerr_quiet( err
, exit
);
8593 ip
= (uint32_t)( ( buf
[ 0 ] << 24 ) | ( buf
[ 1 ] << 16 ) | ( buf
[ 2 ] << 8 ) | buf
[ 3 ] );
8596 // Parse the port (if any).
8602 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPort
), exit
, err
= kUnexpectedErr
);
8603 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) port
= ( port
* 10 ) + ( c
- '0' );
8604 require_action_quiet( port
<= 65535, exit
, err
= kRangeErr
);
8608 // Parse the prefix length (if any).
8616 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPrefix
), exit
, err
= kUnexpectedErr
);
8617 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) prefix
= ( prefix
* 10 ) + ( c
- '0' );
8618 require_action_quiet( ( prefix
>= 0 ) && ( prefix
<= 32 ), exit
, err
= kRangeErr
);
8621 subnetMask
= ( prefix
> 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix
) ) : 0;
8622 router
= ( ip
& subnetMask
) | 1;
8625 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
8627 if( outIP
) *outIP
= ip
;
8628 if( outPort
&& hasPort
) *outPort
= port
;
8629 if( outSubnet
&& hasPrefix
) *outSubnet
= subnetMask
;
8630 if( outRouter
&& hasPrefix
) *outRouter
= router
;
8631 if( outStr
) *outStr
= inStr
;
8638 //===========================================================================================================================
8641 // Note: Parsed according to the rules specified in RFC 3513.
8642 // Warning: "inBuffer" may be modified even in error cases.
8644 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
8645 //===========================================================================================================================
8647 static OSStatus
ParseIPv6Address( const char *inStr
, int inAllowV4Mapped
, uint8_t inBuffer
[ 16 ], const char **outStr
)
8649 // Table to map uppercase hex characters - '0' to their numeric values.
8650 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
8651 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 };
8663 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
8665 for( i
= 0; i
< 16; ++i
) inBuffer
[ i
] = 0;
8667 // Special case leading :: (e.g. "::1") to simplify processing later.
8672 require_action_quiet( *inStr
== ':', exit
, err
= kMalformedErr
);
8675 // Parse the address.
8683 while( ( ( c
= *inStr
++ ) != '\0' ) && ( c
!= '%' ) && ( c
!= '/' ) && ( c
!= ']' ) )
8685 if( ( c
>= 'a' ) && ( c
<= 'f' ) ) c
-= ( 'a' - 'A' );
8686 if( ( ( c
>= '0' ) && ( c
<= '9' ) ) || ( ( c
>= 'A' ) && ( c
<= 'F' ) ) )
8689 check( c
< (int) countof( kASCIItoHexTable
) );
8690 v
= ( v
<< 4 ) | kASCIItoHexTable
[ c
];
8691 require_action_quiet( v
<= 0xFFFF, exit
, err
= kRangeErr
);
8700 require_action_quiet( !colonPtr
, exit
, err
= kMalformedErr
);
8704 require_action_quiet( *inStr
!= '\0', exit
, err
= kUnderrunErr
);
8705 require_action_quiet( ( dst
+ 2 ) <= lim
, exit
, err
= kOverrunErr
);
8706 *dst
++ = (uint8_t)( ( v
>> 8 ) & 0xFF );
8707 *dst
++ = (uint8_t)( v
& 0xFF );
8713 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
8715 if( inAllowV4Mapped
&& ( c
== '.' ) && ( ( dst
+ 4 ) <= lim
) )
8717 err
= ParseIPv4Address( ptr
, dst
, &inStr
);
8718 require_noerr_quiet( err
, exit
);
8721 ++inStr
; // Increment because the code below expects the end to be at "inStr - 1".
8727 require_action_quiet( ( dst
+ 2 ) <= lim
, exit
, err
= kOverrunErr
);
8728 *dst
++ = (uint8_t)( ( v
>> 8 ) & 0xFF );
8729 *dst
++ = (uint8_t)( v
& 0xFF );
8731 check( dst
<= lim
);
8734 require_action_quiet( dst
< lim
, exit
, err
= kOverrunErr
);
8735 n
= (int)( dst
- colonPtr
);
8736 for( i
= 1; i
<= n
; ++i
)
8738 lim
[ -i
] = colonPtr
[ n
- i
];
8739 colonPtr
[ n
- i
] = 0;
8743 require_action_quiet( dst
== lim
, exit
, err
= kUnderrunErr
);
8745 *outStr
= inStr
- 1;
8752 //===========================================================================================================================
8755 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
8756 //===========================================================================================================================
8758 static OSStatus
ParseIPv6Scope( const char *inStr
, uint32_t *outScope
, const char **outStr
)
8760 #if( TARGET_OS_POSIX )
8762 char scopeStr
[ 64 ];
8769 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
8772 lim
= dst
+ ( countof( scopeStr
) - 1 );
8773 while( ( ( c
= *inStr
) != '\0' ) && ( c
!= ':' ) && ( c
!= '/' ) && ( c
!= ']' ) && ( dst
< lim
) )
8778 check( dst
<= lim
);
8780 // First try to map as a name and if that fails, treat it as a numeric scope.
8782 scope
= if_nametoindex( scopeStr
);
8785 for( ptr
= scopeStr
; ( ( c
= *ptr
) >= '0' ) && ( c
<= '9' ); ++ptr
)
8787 scope
= ( scope
* 10 ) + ( ( (uint8_t) c
) - '0' );
8789 require_action_quiet( c
== '\0', exit
, err
= kMalformedErr
);
8790 require_action_quiet( ( ptr
!= scopeStr
) && ( ( (int)( ptr
- scopeStr
) ) <= 10 ), exit
, err
= kMalformedErr
);
8806 for( start
= inStr
; ( ( c
= *inStr
) >= '0' ) && ( c
<= '9' ); ++inStr
)
8808 scope
= ( scope
* 10 ) + ( c
- '0' );
8810 require_action_quiet( ( inStr
!= start
) && ( ( (int)( inStr
- start
) ) <= 10 ), exit
, err
= kMalformedErr
);
8821 //===========================================================================================================================
8822 // StringToIPv6Address
8824 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
8825 //===========================================================================================================================
8828 StringToIPv6Address(
8830 StringToIPAddressFlags inFlags
,
8831 uint8_t outIPv6
[ 16 ],
8832 uint32_t * outScope
,
8835 const char ** outStr
)
8849 require_action( inStr
, exit
, err
= kParamErr
);
8851 if( *inStr
== '[' ) ++inStr
; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
8853 // Parse the address-only part of the address (e.g. "1::1").
8855 err
= ParseIPv6Address( inStr
, !( inFlags
& kStringToIPAddressFlagsNoIPv4Mapped
), ipv6
, &inStr
);
8856 require_noerr_quiet( err
, exit
);
8859 // Parse the scope, port, or prefix length.
8870 if( c
== '%' ) // Scope (e.g. "%en0" or "%5")
8872 require_action_quiet( !hasScope
, exit
, err
= kMalformedErr
);
8873 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoScope
), exit
, err
= kUnexpectedErr
);
8875 err
= ParseIPv6Scope( inStr
, &scope
, &inStr
);
8876 require_noerr_quiet( err
, exit
);
8880 else if( c
== ':' ) // Port (e.g. ":80")
8882 require_action_quiet( !hasPort
, exit
, err
= kMalformedErr
);
8883 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPort
), exit
, err
= kUnexpectedErr
);
8884 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) port
= ( port
* 10 ) + ( c
- '0' );
8885 require_action_quiet( port
<= 65535, exit
, err
= kRangeErr
);
8888 else if( c
== '/' ) // Prefix Length (e.g. "/64")
8890 require_action_quiet( !hasPrefix
, exit
, err
= kMalformedErr
);
8891 require_action_quiet( !( inFlags
& kStringToIPAddressFlagsNoPrefix
), exit
, err
= kUnexpectedErr
);
8892 while( ( ( c
= *( ++inStr
) ) != '\0' ) && ( ( c
>= '0' ) && ( c
<= '9' ) ) ) prefix
= ( prefix
* 10 ) + ( c
- '0' );
8893 require_action_quiet( ( prefix
>= 0 ) && ( prefix
<= 128 ), exit
, err
= kRangeErr
);
8898 require_action_quiet( !hasBracket
, exit
, err
= kMalformedErr
);
8908 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
8910 if( outIPv6
) for( i
= 0; i
< 16; ++i
) outIPv6
[ i
] = ipv6
[ i
];
8911 if( outScope
&& hasScope
) *outScope
= scope
;
8912 if( outPort
&& hasPort
) *outPort
= port
;
8913 if( outPrefix
&& hasPrefix
) *outPrefix
= prefix
;
8914 if( outStr
) *outStr
= inStr
;
8921 //===========================================================================================================================
8922 // StringArray_Append
8924 // Note: This was copied from CoreUtils because the StringArray_Append function is currently not exported in the framework.
8925 //===========================================================================================================================
8927 OSStatus
StringArray_Append( char ***ioArray
, size_t *ioCount
, const char *inStr
)
8936 newStr
= strdup( inStr
);
8937 require_action( newStr
, exit
, err
= kNoMemoryErr
);
8939 oldCount
= *ioCount
;
8940 newCount
= oldCount
+ 1;
8941 newArray
= (char **) malloc( newCount
* sizeof( *newArray
) );
8942 require_action( newArray
, exit
, err
= kNoMemoryErr
);
8946 oldArray
= *ioArray
;
8947 memcpy( newArray
, oldArray
, oldCount
* sizeof( *oldArray
) );
8950 newArray
[ oldCount
] = newStr
;
8953 *ioArray
= newArray
;
8954 *ioCount
= newCount
;
8958 if( newStr
) free( newStr
);
8962 //===========================================================================================================================
8965 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
8966 //===========================================================================================================================
8968 void StringArray_Free( char **inArray
, size_t inCount
)
8972 for( i
= 0; i
< inCount
; ++i
)
8974 free( inArray
[ i
] );
8976 if( inCount
> 0 ) free( inArray
);
8979 //===========================================================================================================================
8980 // ParseQuotedEscapedString
8982 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
8983 //===========================================================================================================================
8986 ParseQuotedEscapedString(
8989 const char * inDelimiters
,
8992 size_t * outCopiedLen
,
8993 size_t * outTotalLen
,
8994 const char ** outSrc
)
8996 const unsigned char * src
;
8997 const unsigned char * end
;
8998 unsigned char * dst
;
8999 unsigned char * lim
;
9003 Boolean singleQuote
;
9004 Boolean doubleQuote
;
9006 if( inEnd
== NULL
) inEnd
= inSrc
+ strlen( inSrc
);
9007 src
= (const unsigned char *) inSrc
;
9008 end
= (const unsigned char *) inEnd
;
9009 dst
= (unsigned char *) inBuf
;
9010 lim
= dst
+ inMaxLen
;
9011 while( ( src
< end
) && isspace_safe( *src
) ) ++src
; // Skip leading spaces.
9012 if( src
>= end
) return( false );
9014 // Parse each argument from the string.
9016 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
9019 singleQuote
= false;
9020 doubleQuote
= false;
9026 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
9030 singleQuote
= false;
9034 else if( doubleQuote
)
9036 // Double quotes protect everything except double quotes and backslashes. A backslash can be
9037 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
9038 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
9039 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
9040 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
9044 doubleQuote
= false;
9047 else if( c
== '\\' )
9052 if( ( c2
== '"' ) || ( c2
== '\\' ) )
9057 else if( c2
== '\n' )
9062 else if( ( c2
== 'x' ) || ( c2
== 'X' ) )
9066 if( ( ( end
- src
) >= 2 ) && IsHexPair( src
) )
9068 c
= HexPairToByte( src
);
9072 else if( isoctal_safe( c2
) )
9074 if( ( ( end
- src
) >= 3 ) && IsOctalTriple( src
) )
9076 c
= OctalTripleToByte( src
);
9083 else if( strchr( inDelimiters
, c
) )
9087 else if( c
== '\\' )
9089 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
9090 // A backslash followed by a newline disappears completely.
9091 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
9092 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
9102 else if( ( c
== 'x' ) || ( c
== 'X' ) )
9105 if( ( ( end
- src
) >= 2 ) && IsHexPair( src
) )
9107 c
= HexPairToByte( src
);
9111 else if( isoctal_safe( c
) )
9113 if( ( ( end
- src
) >= 3 ) && IsOctalTriple( src
) )
9115 c
= OctalTripleToByte( src
);
9129 else if( c
== '\'' )
9142 if( inBuf
) *dst
= c
;
9148 if( outCopiedLen
) *outCopiedLen
= (size_t)( dst
- ( (unsigned char *) inBuf
) );
9149 if( outTotalLen
) *outTotalLen
= totalLen
;
9150 if( outSrc
) *outSrc
= (const char *) src
;