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;