]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/dnssdutil.c
mDNSResponder-878.20.3.tar.gz
[apple/mdnsresponder.git] / Clients / dnssdutil.c
1 /*
2 Copyright (c) 2016-2017 Apple Inc. All rights reserved.
3
4 dnssdutil is a command-line utility for testing the DNS-SD API.
5 */
6
7 #include <CoreUtils/CommonServices.h> // Include early.
8 #include <CoreUtils/AsyncConnection.h>
9 #include <CoreUtils/CommandLineUtils.h>
10 #include <CoreUtils/DataBufferUtils.h>
11 #include <CoreUtils/DebugServices.h>
12 #include <CoreUtils/MiscUtils.h>
13 #include <CoreUtils/NetUtils.h>
14 #include <CoreUtils/PrintFUtils.h>
15 #include <CoreUtils/RandomNumberUtils.h>
16 #include <CoreUtils/StringUtils.h>
17 #include <CoreUtils/TickUtils.h>
18 #include <dns_sd.h>
19 #include <dns_sd_private.h>
20
21 #if( TARGET_OS_DARWIN )
22 #include <dnsinfo.h>
23 #include <libproc.h>
24 #include <sys/proc_info.h>
25 #endif
26
27 #if( TARGET_OS_POSIX )
28 #include <sys/resource.h>
29 #endif
30
31 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
32 #include "tweetnacl.h" // TweetNaCl from <https://tweetnacl.cr.yp.to/software.html>.
33 #endif
34
35 //===========================================================================================================================
36 // Global Constants
37 //===========================================================================================================================
38
39 // Versioning
40
41 #define kDNSSDUtilNumVersion NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 )
42
43 #if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) )
44 #define DNSSDUTIL_SOURCE_VERSION "0.0.0"
45 #endif
46
47 // DNS-SD API flag descriptors
48
49 #define kDNSServiceFlagsDescriptors \
50 "\x00" "AutoTrigger\0" \
51 "\x01" "Add\0" \
52 "\x02" "Default\0" \
53 "\x03" "NoAutoRename\0" \
54 "\x04" "Shared\0" \
55 "\x05" "Unique\0" \
56 "\x06" "BrowseDomains\0" \
57 "\x07" "RegistrationDomains\0" \
58 "\x08" "LongLivedQuery\0" \
59 "\x09" "AllowRemoteQuery\0" \
60 "\x0A" "ForceMulticast\0" \
61 "\x0B" "KnownUnique\0" \
62 "\x0C" "ReturnIntermediates\0" \
63 "\x0D" "NonBrowsable\0" \
64 "\x0E" "ShareConnection\0" \
65 "\x0F" "SuppressUnusable\0" \
66 "\x10" "Timeout\0" \
67 "\x11" "IncludeP2P\0" \
68 "\x12" "WakeOnResolve\0" \
69 "\x13" "BackgroundTrafficClass\0" \
70 "\x14" "IncludeAWDL\0" \
71 "\x15" "Validate\0" \
72 "\x16" "UnicastResponse\0" \
73 "\x17" "ValidateOptional\0" \
74 "\x18" "WakeOnlyService\0" \
75 "\x19" "ThresholdOne\0" \
76 "\x1A" "ThresholdFinder\0" \
77 "\x1B" "DenyCellular\0" \
78 "\x1C" "ServiceIndex\0" \
79 "\x1D" "DenyExpensive\0" \
80 "\x1E" "PathEvaluationDone\0" \
81 "\x00"
82
83 #define kDNSServiceProtocolDescriptors \
84 "\x00" "IPv4\0" \
85 "\x01" "IPv6\0" \
86 "\x00"
87
88 // (m)DNS
89
90 #define kDNSHeaderFlag_Response ( 1 << 15 )
91 #define kDNSHeaderFlag_AuthAnswer ( 1 << 10 )
92 #define kDNSHeaderFlag_Truncation ( 1 << 9 )
93 #define kDNSHeaderFlag_RecursionDesired ( 1 << 8 )
94 #define kDNSHeaderFlag_RecursionAvailable ( 1 << 7 )
95
96 #define kDNSOpCode_Query 0
97 #define kDNSOpCode_InverseQuery 1
98 #define kDNSOpCode_Status 2
99 #define kDNSOpCode_Notify 4
100 #define kDNSOpCode_Update 5
101
102 #define kDNSRCode_NoError 0
103 #define kDNSRCode_FormatError 1
104 #define kDNSRCode_ServerFailure 2
105 #define kDNSRCode_NXDomain 3
106 #define kDNSRCode_NotImplemented 4
107 #define kDNSRCode_Refused 5
108
109 #define kQClassUnicastResponseBit ( 1U << 15 )
110 #define kRRClassCacheFlushBit ( 1U << 15 )
111
112 #define kDomainLabelLengthMax 63
113 #define kDomainNameLengthMax 256
114
115 //===========================================================================================================================
116 // Gerneral Command Options
117 //===========================================================================================================================
118
119 // Command option macros
120
121 #define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON ) \
122 CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None, \
123 (SHORT_HELP), NULL )
124
125 #define kRequiredOptionSuffix " [REQUIRED]"
126
127 #define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
128 CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, \
129 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
130 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
131
132 #define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
133 MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
134
135 #define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
136 CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
137 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
138 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
139
140 #define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \
141 CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL )
142
143 #define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
144 CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
145 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
146 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
147
148 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
149 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
150
151 // DNS-SD API flag options
152
153 static int gDNSSDFlags = 0;
154 static int gDNSSDFlag_BrowseDomains = false;
155 static int gDNSSDFlag_DenyCellular = false;
156 static int gDNSSDFlag_DenyExpensive = false;
157 static int gDNSSDFlag_ForceMulticast = false;
158 static int gDNSSDFlag_IncludeAWDL = false;
159 static int gDNSSDFlag_NoAutoRename = false;
160 static int gDNSSDFlag_PathEvaluationDone = false;
161 static int gDNSSDFlag_RegistrationDomains = false;
162 static int gDNSSDFlag_ReturnIntermediates = false;
163 static int gDNSSDFlag_Shared = false;
164 static int gDNSSDFlag_SuppressUnusable = false;
165 static int gDNSSDFlag_Timeout = false;
166 static int gDNSSDFlag_UnicastResponse = false;
167 static int gDNSSDFlag_Unique = false;
168
169 #define DNSSDFlagsOption() \
170 IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \
171 "DNSServiceFlags to use. This value is bitwise ORed with other single flag options.", false )
172
173 #define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \
174 BooleanOption( SHORT_CHAR, Stringify( FLAG_NAME ), &gDNSSDFlag_ ## FLAG_NAME, \
175 "Use kDNSServiceFlags" Stringify( FLAG_NAME ) "." )
176
177 #define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular )
178 #define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive )
179 #define DNSSDFlagsOption_ForceMulticast() DNSSDFlagOption( 'M', ForceMulticast )
180 #define DNSSDFlagsOption_IncludeAWDL() DNSSDFlagOption( 'A', IncludeAWDL )
181 #define DNSSDFlagsOption_NoAutoRename() DNSSDFlagOption( 'N', NoAutoRename )
182 #define DNSSDFlagsOption_PathEvalDone() DNSSDFlagOption( 'P', PathEvaluationDone )
183 #define DNSSDFlagsOption_ReturnIntermediates() DNSSDFlagOption( 'I', ReturnIntermediates )
184 #define DNSSDFlagsOption_Shared() DNSSDFlagOption( 'S', Shared )
185 #define DNSSDFlagsOption_SuppressUnusable() DNSSDFlagOption( 'S', SuppressUnusable )
186 #define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout )
187 #define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse )
188 #define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique )
189
190 // Interface option
191
192 static const char * gInterface = NULL;
193
194 #define InterfaceOption() \
195 StringOption( 'i', "interface", &gInterface, "interface", \
196 "Network interface by name or index. Use index -1 for local-only.", false )
197
198 // Connection options
199
200 #define kConnectionArg_Normal ""
201 #define kConnectionArgPrefix_PID "pid:"
202 #define kConnectionArgPrefix_UUID "uuid:"
203
204 static const char * gConnectionOpt = kConnectionArg_Normal;
205
206 #define ConnectionOptions() \
207 { kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type", \
208 kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL, \
209 "Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL }
210
211 #define kConnectionSection_Name "Connection Option"
212 #define kConnectionSection_Text \
213 "The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n" \
214 "the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n" \
215 "specifying the connection option without an argument, i.e.,\n" \
216 "\n" \
217 " --connection\n" \
218 "\n" \
219 "To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n" \
220 "\n" \
221 " --connection=pid:<PID>\n" \
222 "\n" \
223 "to specify the delegator by PID, or use\n" \
224 "\n" \
225 " --connection=uuid:<UUID>\n" \
226 "\n" \
227 "to specify the delegator by UUID.\n" \
228 "\n" \
229 "To not use a main connection at all, but instead perform operations on their own connections, use\n" \
230 "\n" \
231 " --no-connection\n"
232
233 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
234
235 // Help text for record data options
236
237 #define kRDataArgPrefix_File "file:"
238 #define kRDataArgPrefix_HexString "hex:"
239 #define kRDataArgPrefix_String "string:"
240 #define kRDataArgPrefix_TXT "txt:"
241
242 #define kRecordDataSection_Name "Record Data Arguments"
243 #define kRecordDataSection_Text \
244 "A record data argument is specified in one of the following formats:\n" \
245 "\n" \
246 "Format Syntax Example\n" \
247 "String string:<string> string:'\\x09color=red'\n" \
248 "Hexadecimal string hex:<hex string> hex:c0a80101 or hex:'C0 A8 01 01'\n" \
249 "TXT record keys and values txt:<comma-delimited keys and values> txt:'key1=x,key2=y\\,z,key3'\n" \
250 "File containing raw record data file:<file path> file:dir/record_data.bin\n"
251
252 #define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text )
253
254 //===========================================================================================================================
255 // Browse Command Options
256 //===========================================================================================================================
257
258 static char ** gBrowse_ServiceTypes = NULL;
259 static size_t gBrowse_ServiceTypesCount = 0;
260 static const char * gBrowse_Domain = NULL;
261 static int gBrowse_DoResolve = false;
262 static int gBrowse_QueryTXT = false;
263 static int gBrowse_TimeLimitSecs = 0;
264
265 static CLIOption kBrowseOpts[] =
266 {
267 InterfaceOption(),
268 MultiStringOption( 't', "type", &gBrowse_ServiceTypes, &gBrowse_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ),
269 StringOption( 'd', "domain", &gBrowse_Domain, "domain", "Domain in which to browse for the service type(s).", false ),
270
271 CLI_OPTION_GROUP( "Flags" ),
272 DNSSDFlagsOption(),
273 DNSSDFlagsOption_IncludeAWDL(),
274
275 CLI_OPTION_GROUP( "Operation" ),
276 ConnectionOptions(),
277 BooleanOption( 0 , "resolve", &gBrowse_DoResolve, "Resolve service instances." ),
278 BooleanOption( 0 , "queryTXT", &gBrowse_QueryTXT, "Query TXT records of service instances." ),
279 IntegerOption( 'l', "timeLimit", &gBrowse_TimeLimitSecs, "seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ),
280
281 ConnectionSection(),
282 CLI_OPTION_END()
283 };
284
285 //===========================================================================================================================
286 // GetAddrInfo Command Options
287 //===========================================================================================================================
288
289 static const char * gGetAddrInfo_Name = NULL;
290 static int gGetAddrInfo_ProtocolIPv4 = false;
291 static int gGetAddrInfo_ProtocolIPv6 = false;
292 static int gGetAddrInfo_OneShot = false;
293 static int gGetAddrInfo_TimeLimitSecs = 0;
294
295 static CLIOption kGetAddrInfoOpts[] =
296 {
297 InterfaceOption(),
298 StringOption( 'n', "name", &gGetAddrInfo_Name, "domain name", "Domain name to resolve.", true ),
299 BooleanOption( 0 , "ipv4", &gGetAddrInfo_ProtocolIPv4, "Use kDNSServiceProtocol_IPv4." ),
300 BooleanOption( 0 , "ipv6", &gGetAddrInfo_ProtocolIPv6, "Use kDNSServiceProtocol_IPv6." ),
301
302 CLI_OPTION_GROUP( "Flags" ),
303 DNSSDFlagsOption(),
304 DNSSDFlagsOption_DenyCellular(),
305 DNSSDFlagsOption_DenyExpensive(),
306 DNSSDFlagsOption_PathEvalDone(),
307 DNSSDFlagsOption_ReturnIntermediates(),
308 DNSSDFlagsOption_SuppressUnusable(),
309 DNSSDFlagsOption_Timeout(),
310
311 CLI_OPTION_GROUP( "Operation" ),
312 ConnectionOptions(),
313 BooleanOption( 'o', "oneshot", &gGetAddrInfo_OneShot, "Finish after first set of results." ),
314 IntegerOption( 'l', "timeLimit", &gGetAddrInfo_TimeLimitSecs, "seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ),
315
316 ConnectionSection(),
317 CLI_OPTION_END()
318 };
319
320 //===========================================================================================================================
321 // QueryRecord Command Options
322 //===========================================================================================================================
323
324 static const char * gQueryRecord_Name = NULL;
325 static const char * gQueryRecord_Type = NULL;
326 static int gQueryRecord_OneShot = false;
327 static int gQueryRecord_TimeLimitSecs = 0;
328 static int gQueryRecord_RawRData = false;
329
330 static CLIOption kQueryRecordOpts[] =
331 {
332 InterfaceOption(),
333 StringOption( 'n', "name", &gQueryRecord_Name, "domain name", "Full domain name of record to query.", true ),
334 StringOption( 't', "type", &gQueryRecord_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
335
336 CLI_OPTION_GROUP( "Flags" ),
337 DNSSDFlagsOption(),
338 DNSSDFlagsOption_IncludeAWDL(),
339 DNSSDFlagsOption_ForceMulticast(),
340 DNSSDFlagsOption_Timeout(),
341 DNSSDFlagsOption_ReturnIntermediates(),
342 DNSSDFlagsOption_SuppressUnusable(),
343 DNSSDFlagsOption_UnicastResponse(),
344 DNSSDFlagsOption_DenyCellular(),
345 DNSSDFlagsOption_DenyExpensive(),
346 DNSSDFlagsOption_PathEvalDone(),
347
348 CLI_OPTION_GROUP( "Operation" ),
349 ConnectionOptions(),
350 BooleanOption( 'o', "oneshot", &gQueryRecord_OneShot, "Finish after first set of results." ),
351 IntegerOption( 'l', "timeLimit", &gQueryRecord_TimeLimitSecs, "seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ),
352 BooleanOption( 0 , "raw", &gQueryRecord_RawRData, "Show record data as a hexdump." ),
353
354 ConnectionSection(),
355 CLI_OPTION_END()
356 };
357
358 //===========================================================================================================================
359 // Register Command Options
360 //===========================================================================================================================
361
362 static const char * gRegister_Name = NULL;
363 static const char * gRegister_Type = NULL;
364 static const char * gRegister_Domain = NULL;
365 static int gRegister_Port = 0;
366 static const char * gRegister_TXT = NULL;
367 static int gRegister_LifetimeMs = -1;
368 static const char ** gAddRecord_Types = NULL;
369 static size_t gAddRecord_TypesCount = 0;
370 static const char ** gAddRecord_Data = NULL;
371 static size_t gAddRecord_DataCount = 0;
372 static const char ** gAddRecord_TTLs = NULL;
373 static size_t gAddRecord_TTLsCount = 0;
374 static const char * gUpdateRecord_Data = NULL;
375 static int gUpdateRecord_DelayMs = 0;
376 static int gUpdateRecord_TTL = 0;
377
378 static CLIOption kRegisterOpts[] =
379 {
380 InterfaceOption(),
381 StringOption( 'n', "name", &gRegister_Name, "service name", "Name of service.", false ),
382 StringOption( 't', "type", &gRegister_Type, "service type", "Service type, e.g., \"_ssh._tcp\".", true ),
383 StringOption( 'd', "domain", &gRegister_Domain, "domain", "Domain in which to advertise the service.", false ),
384 IntegerOption( 'p', "port", &gRegister_Port, "port number", "Service's port number.", true ),
385 StringOption( 0 , "txt", &gRegister_TXT, "record data", "The TXT record data. See " kRecordDataSection_Name " below.", false ),
386
387 CLI_OPTION_GROUP( "Flags" ),
388 DNSSDFlagsOption(),
389 DNSSDFlagsOption_IncludeAWDL(),
390 DNSSDFlagsOption_NoAutoRename(),
391
392 CLI_OPTION_GROUP( "Operation" ),
393 IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
394
395 CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ),
396 StringOption( 0 , "updateData", &gUpdateRecord_Data, "record data", "Record data for the record update. See " kRecordDataSection_Name " below.", false ),
397 IntegerOption( 0 , "updateDelay", &gUpdateRecord_DelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ),
398 IntegerOption( 0 , "updateTTL", &gUpdateRecord_TTL, "seconds", "Time-to-live of the updated record.", false ),
399
400 CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ),
401 MultiStringOption( 0 , "addType", &gAddRecord_Types, &gAddRecord_TypesCount, "record type", "Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ),
402 MultiStringOptionEx( 0 , "addData", &gAddRecord_Data, &gAddRecord_DataCount, "record data", "Additional record's data. See " kRecordDataSection_Name " below.", false, NULL ),
403 MultiStringOption( 0 , "addTTL", &gAddRecord_TTLs, &gAddRecord_TTLsCount, "seconds", "Time-to-live of additional record in seconds. Use '0' for default.", false ),
404
405 RecordDataSection(),
406 CLI_OPTION_END()
407 };
408
409 //===========================================================================================================================
410 // RegisterRecord Command Options
411 //===========================================================================================================================
412
413 static const char * gRegisterRecord_Name = NULL;
414 static const char * gRegisterRecord_Type = NULL;
415 static const char * gRegisterRecord_Data = NULL;
416 static int gRegisterRecord_TTL = 0;
417 static int gRegisterRecord_LifetimeMs = -1;
418 static const char * gRegisterRecord_UpdateData = NULL;
419 static int gRegisterRecord_UpdateDelayMs = 0;
420 static int gRegisterRecord_UpdateTTL = 0;
421
422 static CLIOption kRegisterRecordOpts[] =
423 {
424 InterfaceOption(),
425 StringOption( 'n', "name", &gRegisterRecord_Name, "record name", "Fully qualified domain name of record.", true ),
426 StringOption( 't', "type", &gRegisterRecord_Type, "record type", "Record type by name (e.g., TXT, PTR, A) or number.", true ),
427 StringOption( 'd', "data", &gRegisterRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ),
428 IntegerOption( 0 , "ttl", &gRegisterRecord_TTL, "seconds", "Time-to-live in seconds. Use '0' for default.", false ),
429
430 CLI_OPTION_GROUP( "Flags" ),
431 DNSSDFlagsOption(),
432 DNSSDFlagsOption_IncludeAWDL(),
433 DNSSDFlagsOption_Shared(),
434 DNSSDFlagsOption_Unique(),
435
436 CLI_OPTION_GROUP( "Operation" ),
437 IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
438
439 CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ),
440 StringOption( 0 , "updateData", &gRegisterRecord_UpdateData, "record data", "Record data for the record update.", false ),
441 IntegerOption( 0 , "updateDelay", &gRegisterRecord_UpdateDelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ),
442 IntegerOption( 0 , "updateTTL", &gRegisterRecord_UpdateTTL, "seconds", "Time-to-live of the updated record.", false ),
443
444 RecordDataSection(),
445 CLI_OPTION_END()
446 };
447
448 //===========================================================================================================================
449 // Resolve Command Options
450 //===========================================================================================================================
451
452 static char * gResolve_Name = NULL;
453 static char * gResolve_Type = NULL;
454 static char * gResolve_Domain = NULL;
455 static int gResolve_TimeLimitSecs = 0;
456
457 static CLIOption kResolveOpts[] =
458 {
459 InterfaceOption(),
460 StringOption( 'n', "name", &gResolve_Name, "service name", "Name of the service instance to resolve.", true ),
461 StringOption( 't', "type", &gResolve_Type, "service type", "Type of the service instance to resolve.", true ),
462 StringOption( 'd', "domain", &gResolve_Domain, "domain", "Domain of the service instance to resolve.", true ),
463
464 CLI_OPTION_GROUP( "Flags" ),
465 DNSSDFlagsOption(),
466 DNSSDFlagsOption_ForceMulticast(),
467 DNSSDFlagsOption_IncludeAWDL(),
468 DNSSDFlagsOption_ReturnIntermediates(),
469
470 CLI_OPTION_GROUP( "Operation" ),
471 ConnectionOptions(),
472 IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ),
473
474 ConnectionSection(),
475 CLI_OPTION_END()
476 };
477
478 //===========================================================================================================================
479 // Reconfirm Command Options
480 //===========================================================================================================================
481
482 static const char * gReconfirmRecord_Name = NULL;
483 static const char * gReconfirmRecord_Type = NULL;
484 static const char * gReconfirmRecord_Class = NULL;
485 static const char * gReconfirmRecord_Data = NULL;
486
487 static CLIOption kReconfirmOpts[] =
488 {
489 InterfaceOption(),
490 StringOption( 'n', "name", &gReconfirmRecord_Name, "record name", "Full name of the record to reconfirm.", true ),
491 StringOption( 't', "type", &gReconfirmRecord_Type, "record type", "Type of the record to reconfirm.", true ),
492 StringOption( 'c', "class", &gReconfirmRecord_Class, "record class", "Class of the record to reconfirm. Default class is IN.", false ),
493 StringOption( 'd', "data", &gReconfirmRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ),
494
495 CLI_OPTION_GROUP( "Flags" ),
496 DNSSDFlagsOption(),
497
498 RecordDataSection(),
499 CLI_OPTION_END()
500 };
501
502 //===========================================================================================================================
503 // getaddrinfo-POSIX Command Options
504 //===========================================================================================================================
505
506 static const char * gGAIPOSIX_HostName = NULL;
507 static const char * gGAIPOSIX_ServName = NULL;
508 static const char * gGAIPOSIX_Family = NULL;
509 static int gGAIPOSIXFlag_AddrConfig = false;
510 static int gGAIPOSIXFlag_All = false;
511 static int gGAIPOSIXFlag_CanonName = false;
512 static int gGAIPOSIXFlag_NumericHost = false;
513 static int gGAIPOSIXFlag_NumericServ = false;
514 static int gGAIPOSIXFlag_Passive = false;
515 static int gGAIPOSIXFlag_V4Mapped = false;
516 #if( defined( AI_V4MAPPED_CFG ) )
517 static int gGAIPOSIXFlag_V4MappedCFG = false;
518 #endif
519 #if( defined( AI_DEFAULT ) )
520 static int gGAIPOSIXFlag_Default = false;
521 #endif
522
523 static CLIOption kGetAddrInfoPOSIXOpts[] =
524 {
525 StringOption( 'n', "hostname", &gGAIPOSIX_HostName, "hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ),
526 StringOption( 's', "servname", &gGAIPOSIX_ServName, "servname", "Port number in decimal or service name from services(5).", false ),
527
528 CLI_OPTION_GROUP( "Hints " ),
529 StringOptionEx( 'f', "family", &gGAIPOSIX_Family, "address family", "Address family to use for hints ai_family field.", false,
530 "\n"
531 "Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n"
532 "address family is specified, then AF_UNSPEC is used.\n"
533 "\n" ),
534 BooleanOption( 0 , "flag-addrconfig", &gGAIPOSIXFlag_AddrConfig, "In hints ai_flags field, set AI_ADDRCONFIG." ),
535 BooleanOption( 0 , "flag-all", &gGAIPOSIXFlag_All, "In hints ai_flags field, set AI_ALL." ),
536 BooleanOption( 0 , "flag-canonname", &gGAIPOSIXFlag_CanonName, "In hints ai_flags field, set AI_CANONNAME." ),
537 BooleanOption( 0 , "flag-numerichost", &gGAIPOSIXFlag_NumericHost, "In hints ai_flags field, set AI_NUMERICHOST." ),
538 BooleanOption( 0 , "flag-numericserv", &gGAIPOSIXFlag_NumericServ, "In hints ai_flags field, set AI_NUMERICSERV." ),
539 BooleanOption( 0 , "flag-passive", &gGAIPOSIXFlag_Passive, "In hints ai_flags field, set AI_PASSIVE." ),
540 BooleanOption( 0 , "flag-v4mapped", &gGAIPOSIXFlag_V4Mapped, "In hints ai_flags field, set AI_V4MAPPED." ),
541 #if( defined( AI_V4MAPPED_CFG ) )
542 BooleanOption( 0 , "flag-v4mappedcfg", &gGAIPOSIXFlag_V4MappedCFG, "In hints ai_flags field, set AI_V4MAPPED_CFG." ),
543 #endif
544 #if( defined( AI_DEFAULT ) )
545 BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default, "In hints ai_flags field, set AI_DEFAULT." ),
546 #endif
547
548 CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ),
549 CLI_OPTION_END()
550 };
551
552 //===========================================================================================================================
553 // ReverseLookup Command Options
554 //===========================================================================================================================
555
556 static const char * gReverseLookup_IPAddr = NULL;
557 static int gReverseLookup_OneShot = false;
558 static int gReverseLookup_TimeLimitSecs = 0;
559
560 static CLIOption kReverseLookupOpts[] =
561 {
562 InterfaceOption(),
563 StringOption( 'a', "address", &gReverseLookup_IPAddr, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ),
564
565 CLI_OPTION_GROUP( "Flags" ),
566 DNSSDFlagsOption(),
567 DNSSDFlagsOption_ForceMulticast(),
568 DNSSDFlagsOption_ReturnIntermediates(),
569 DNSSDFlagsOption_SuppressUnusable(),
570
571 CLI_OPTION_GROUP( "Operation" ),
572 ConnectionOptions(),
573 BooleanOption( 'o', "oneshot", &gReverseLookup_OneShot, "Finish after first set of results." ),
574 IntegerOption( 'l', "timeLimit", &gReverseLookup_TimeLimitSecs, "seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ),
575
576 ConnectionSection(),
577 CLI_OPTION_END()
578 };
579
580 //===========================================================================================================================
581 // BrowseAll Command Options
582 //===========================================================================================================================
583
584 static const char * gBrowseAll_Domain = NULL;
585 static char ** gBrowseAll_ServiceTypes = NULL;
586 static size_t gBrowseAll_ServiceTypesCount = 0;
587 static int gBrowseAll_IncludeAWDL = false;
588 static int gBrowseAll_BrowseTimeSecs = 5;
589 static int gBrowseAll_ConnectTimeLimitSecs = 5;
590
591 static CLIOption kBrowseAllOpts[] =
592 {
593 InterfaceOption(),
594 StringOption( 'd', "domain", &gBrowseAll_Domain, "domain", "Domain in which to browse for the service.", false ),
595 MultiStringOption( 't', "type", &gBrowseAll_ServiceTypes, &gBrowseAll_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\".", false ),
596
597 CLI_OPTION_GROUP( "Flags" ),
598 DNSSDFlagsOption_IncludeAWDL(),
599
600 CLI_OPTION_GROUP( "Operation" ),
601 IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs, "seconds", "Specifies the duration of the browse.", false ),
602 IntegerOption( 'c', "connectTimeLimit", &gBrowseAll_ConnectTimeLimitSecs, "seconds", "Specifies the max duration of the connect operations.", false ),
603 CLI_OPTION_END()
604 };
605
606 //===========================================================================================================================
607 // GetAddrInfoStress Command Options
608 //===========================================================================================================================
609
610 static int gGAIStress_TestDurationSecs = 0;
611 static int gGAIStress_ConnectionCount = 0;
612 static int gGAIStress_DurationMinMs = 0;
613 static int gGAIStress_DurationMaxMs = 0;
614 static int gGAIStress_RequestCountMax = 0;
615
616 static CLIOption kGetAddrInfoStressOpts[] =
617 {
618 InterfaceOption(),
619
620 CLI_OPTION_GROUP( "Flags" ),
621 DNSSDFlagsOption_ReturnIntermediates(),
622 DNSSDFlagsOption_SuppressUnusable(),
623
624 CLI_OPTION_GROUP( "Operation" ),
625 IntegerOption( 0, "testDuration", &gGAIStress_TestDurationSecs, "seconds", "Stress test duration in seconds. Use '0' for forever.", false ),
626 IntegerOption( 0, "connectionCount", &gGAIStress_ConnectionCount, "integer", "Number of simultaneous DNS-SD connections.", true ),
627 IntegerOption( 0, "requestDurationMin", &gGAIStress_DurationMinMs, "ms", "Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
628 IntegerOption( 0, "requestDurationMax", &gGAIStress_DurationMaxMs, "ms", "Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
629 IntegerOption( 0, "consecutiveRequestMax", &gGAIStress_RequestCountMax, "integer", "Maximum number of requests on a connection before restarting it.", true ),
630 CLI_OPTION_END()
631 };
632
633 //===========================================================================================================================
634 // DNSQuery Command Options
635 //===========================================================================================================================
636
637 static char * gDNSQuery_Name = NULL;
638 static char * gDNSQuery_Type = "A";
639 static char * gDNSQuery_Server = NULL;
640 static int gDNSQuery_TimeLimitSecs = 5;
641 static int gDNSQuery_UseTCP = false;
642 static int gDNSQuery_Flags = kDNSHeaderFlag_RecursionDesired;
643 static int gDNSQuery_RawRData = false;
644 static int gDNSQuery_Verbose = false;
645
646 #if( TARGET_OS_DARWIN )
647 #define kDNSQueryServerOptionIsRequired false
648 #else
649 #define kDNSQueryServerOptionIsRequired true
650 #endif
651
652 static CLIOption kDNSQueryOpts[] =
653 {
654 StringOption( 'n', "name", &gDNSQuery_Name, "name", "Question name (QNAME) to put in DNS query message.", true ),
655 StringOption( 't', "type", &gDNSQuery_Type, "type", "Question type (QTYPE) to put in DNS query message. Default value is 'A'.", false ),
656 StringOption( 's', "server", &gDNSQuery_Server, "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired ),
657 IntegerOption( 'l', "timeLimit", &gDNSQuery_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ),
658 BooleanOption( 0 , "tcp", &gDNSQuery_UseTCP, "Send the DNS query via TCP instead of UDP." ),
659 IntegerOption( 'f', "flags", &gDNSQuery_Flags, "flags", "16-bit value for DNS header flags/codes field. Default value is 0x0100 (Recursion Desired).", false ),
660 BooleanOption( 0 , "raw", &gDNSQuery_RawRData, "Present record data as a hexdump." ),
661 BooleanOption( 'v', "verbose", &gDNSQuery_Verbose, "Prints the DNS message to be sent to the server." ),
662 CLI_OPTION_END()
663 };
664
665 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
666 //===========================================================================================================================
667 // DNSCrypt Command Options
668 //===========================================================================================================================
669
670 static char * gDNSCrypt_ProviderName = NULL;
671 static char * gDNSCrypt_ProviderKey = NULL;
672 static char * gDNSCrypt_Name = NULL;
673 static char * gDNSCrypt_Type = NULL;
674 static char * gDNSCrypt_Server = NULL;
675 static int gDNSCrypt_TimeLimitSecs = 5;
676 static int gDNSCrypt_RawRData = false;
677 static int gDNSCrypt_Verbose = false;
678
679 static CLIOption kDNSCryptOpts[] =
680 {
681 StringOption( 'p', "providerName", &gDNSCrypt_ProviderName, "name", "The DNSCrypt provider name.", true ),
682 StringOption( 'k', "providerKey", &gDNSCrypt_ProviderKey, "hex string", "The DNSCrypt provider's public signing key.", true ),
683 StringOption( 'n', "name", &gDNSCrypt_Name, "name", "Question name (QNAME) to put in DNS query message.", true ),
684 StringOption( 't', "type", &gDNSCrypt_Type, "type", "Question type (QTYPE) to put in DNS query message.", true ),
685 StringOption( 's', "server", &gDNSCrypt_Server, "IP address", "DNS server's IPv4 or IPv6 address.", true ),
686 IntegerOption( 'l', "timeLimit", &gDNSCrypt_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ),
687 BooleanOption( 0 , "raw", &gDNSCrypt_RawRData, "Present record data as a hexdump." ),
688 BooleanOption( 'v', "verbose", &gDNSCrypt_Verbose, "Prints the DNS message to be sent to the server." ),
689 CLI_OPTION_END()
690 };
691 #endif
692
693 //===========================================================================================================================
694 // MDNSQuery Command Options
695 //===========================================================================================================================
696
697 static char * gMDNSQuery_Name = NULL;
698 static char * gMDNSQuery_Type = NULL;
699 static int gMDNSQuery_SourcePort = 0;
700 static int gMDNSQuery_IsQU = false;
701 static int gMDNSQuery_RawRData = false;
702 static int gMDNSQuery_UseIPv4 = false;
703 static int gMDNSQuery_UseIPv6 = false;
704 static int gMDNSQuery_AllResponses = false;
705 static int gMDNSQuery_ReceiveSecs = 1;
706
707 static CLIOption kMDNSQueryOpts[] =
708 {
709 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
710 StringOption( 'n', "name", &gMDNSQuery_Name, "name", "Question name (QNAME) to put in mDNS message.", true ),
711 StringOption( 't', "type", &gMDNSQuery_Type, "type", "Question type (QTYPE) to put in mDNS message.", true ),
712 IntegerOption( 'p', "sourcePort", &gMDNSQuery_SourcePort, "port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ),
713 BooleanOption( 'u', "QU", &gMDNSQuery_IsQU, "Set the unicast-response bit, i.e., send a QU question." ),
714 BooleanOption( 0 , "raw", &gMDNSQuery_RawRData, "Present record data as a hexdump." ),
715 BooleanOption( 0 , "ipv4", &gMDNSQuery_UseIPv4, "Use IPv4." ),
716 BooleanOption( 0 , "ipv6", &gMDNSQuery_UseIPv6, "Use IPv6." ),
717 BooleanOption( 'a', "allResponses", &gMDNSQuery_AllResponses, "Print all received mDNS messages, not just those containing answers." ),
718 IntegerOption( 'r', "receiveTime", &gMDNSQuery_ReceiveSecs, "seconds", "Amount of time to spend receiving messages after the query is sent. The default is one second. Use -1 for unlimited time.", false ),
719 CLI_OPTION_END()
720 };
721
722 //===========================================================================================================================
723 // PIDToUUID Command Options
724 //===========================================================================================================================
725
726 static int gPIDToUUID_PID = 0;
727
728 static CLIOption kPIDToUUIDOpts[] =
729 {
730 IntegerOption( 'p', "pid", &gPIDToUUID_PID, "PID", "Process ID.", true ),
731 CLI_OPTION_END()
732 };
733
734 //===========================================================================================================================
735 // Command Table
736 //===========================================================================================================================
737
738 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset );
739
740 static void BrowseCmd( void );
741 static void GetAddrInfoCmd( void );
742 static void QueryRecordCmd( void );
743 static void RegisterCmd( void );
744 static void RegisterRecordCmd( void );
745 static void ResolveCmd( void );
746 static void ReconfirmCmd( void );
747 static void GetAddrInfoPOSIXCmd( void );
748 static void ReverseLookupCmd( void );
749 static void BrowseAllCmd( void );
750 static void GetAddrInfoStressCmd( void );
751 static void DNSQueryCmd( void );
752 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
753 static void DNSCryptCmd( void );
754 #endif
755 static void MDNSQueryCmd( void );
756 static void PIDToUUIDCmd( void );
757 static void DaemonVersionCmd( void );
758
759 static CLIOption kGlobalOpts[] =
760 {
761 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback, NULL, NULL,
762 kCLIOptionFlags_NoArgument | kCLIOptionFlags_GlobalOnly, "Displays the version of this tool.", NULL ),
763 CLI_OPTION_HELP(),
764
765 // Common commands.
766
767 Command( "browse", BrowseCmd, kBrowseOpts, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
768 Command( "getAddrInfo", GetAddrInfoCmd, kGetAddrInfoOpts, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
769 Command( "queryRecord", QueryRecordCmd, kQueryRecordOpts, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
770 Command( "register", RegisterCmd, kRegisterOpts, "Uses DNSServiceRegister() to register a service.", false ),
771 Command( "registerRecord", RegisterRecordCmd, kRegisterRecordOpts, "Uses DNSServiceRegisterRecord() to register a record.", false ),
772 Command( "resolve", ResolveCmd, kResolveOpts, "Uses DNSServiceResolve() to resolve a service.", false ),
773 Command( "reconfirm", ReconfirmCmd, kReconfirmOpts, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
774 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd, kGetAddrInfoPOSIXOpts, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
775 Command( "reverseLookup", ReverseLookupCmd, kReverseLookupOpts, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
776 Command( "browseAll", BrowseAllCmd, kBrowseAllOpts, "Browse and resolve all, or just some, services.", false ),
777
778 // Uncommon commands.
779
780 Command( "getAddrInfoStress", GetAddrInfoStressCmd, kGetAddrInfoStressOpts, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
781 Command( "DNSQuery", DNSQueryCmd, kDNSQueryOpts, "Crafts and sends a DNS query.", true ),
782 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
783 Command( "DNSCrypt", DNSCryptCmd, kDNSCryptOpts, "Crafts and sends a DNSCrypt query.", true ),
784 #endif
785 Command( "mDNSQuery", MDNSQueryCmd, kMDNSQueryOpts, "Crafts and sends an mDNS query over the specified interface.", true ),
786 Command( "pid2uuid", PIDToUUIDCmd, kPIDToUUIDOpts, "Prints the UUID of a process.", true ),
787 Command( "daemonVersion", DaemonVersionCmd, NULL, "Prints the version of the DNS-SD daemon.", true ),
788
789 CLI_COMMAND_HELP(),
790 CLI_OPTION_END()
791 };
792
793 //===========================================================================================================================
794 // Helper Prototypes
795 //===========================================================================================================================
796
797 #define kExitReason_OneShotDone "one-shot done"
798 #define kExitReason_ReceivedResponse "received response"
799 #define kExitReason_SIGINT "interrupt signal"
800 #define kExitReason_Timeout "timeout"
801 #define kExitReason_TimeLimit "time limit"
802
803 static void Exit( void *inContext ) ATTRIBUTE_NORETURN;
804
805 #define kTimestampBufLen 27
806
807 static char * GetTimestampStr( char inBuffer[ kTimestampBufLen ] );
808 static DNSServiceFlags GetDNSSDFlagsFromOpts( void );
809
810 typedef enum
811 {
812 kConnectionType_None = 0,
813 kConnectionType_Normal = 1,
814 kConnectionType_DelegatePID = 2,
815 kConnectionType_DelegateUUID = 3
816
817 } ConnectionType;
818
819 typedef struct
820 {
821 ConnectionType type;
822 union
823 {
824 int32_t pid;
825 uint8_t uuid[ 16 ];
826
827 } delegate;
828
829 } ConnectionDesc;
830
831 static OSStatus
832 CreateConnectionFromArgString(
833 const char * inString,
834 dispatch_queue_t inQueue,
835 DNSServiceRef * outSDRef,
836 ConnectionDesc * outDesc );
837 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex );
838 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen );
839 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue );
840 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue );
841
842 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
843
844 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] );
845 static const char * RecordTypeToString( unsigned int inValue );
846
847 // DNS message helpers
848
849 typedef struct
850 {
851 uint8_t id[ 2 ];
852 uint8_t flags[ 2 ];
853 uint8_t questionCount[ 2 ];
854 uint8_t answerCount[ 2 ];
855 uint8_t authorityCount[ 2 ];
856 uint8_t additionalCount[ 2 ];
857
858 } DNSHeader;
859
860 #define kDNSHeaderLength 12
861 check_compile_time( sizeof( DNSHeader ) == kDNSHeaderLength );
862
863 #define DNSHeaderGetID( HDR ) ReadBig16( ( HDR )->id )
864 #define DNSHeaderGetFlags( HDR ) ReadBig16( ( HDR )->flags )
865 #define DNSHeaderGetQuestionCount( HDR ) ReadBig16( ( HDR )->questionCount )
866 #define DNSHeaderGetAnswerCount( HDR ) ReadBig16( ( HDR )->answerCount )
867 #define DNSHeaderGetAuthorityCount( HDR ) ReadBig16( ( HDR )->authorityCount )
868 #define DNSHeaderGetAdditionalCount( HDR ) ReadBig16( ( HDR )->additionalCount )
869
870 static OSStatus
871 DNSMessageExtractDomainName(
872 const uint8_t * inMsgPtr,
873 size_t inMsgLen,
874 const uint8_t * inNamePtr,
875 uint8_t inBuf[ kDomainNameLengthMax ],
876 const uint8_t ** outNextPtr );
877 static OSStatus
878 DNSMessageExtractDomainNameString(
879 const void * inMsgPtr,
880 size_t inMsgLen,
881 const void * inNamePtr,
882 char inBuf[ kDNSServiceMaxDomainName ],
883 const uint8_t ** outNextPtr );
884 static OSStatus
885 DNSMessageExtractRecord(
886 const uint8_t * inMsgPtr,
887 size_t inMsgLen,
888 const uint8_t * inPtr,
889 uint8_t inNameBuf[ kDomainNameLengthMax ],
890 uint16_t * outType,
891 uint16_t * outClass,
892 uint32_t * outTTL,
893 const uint8_t ** outRDataPtr,
894 size_t * outRDataLen,
895 const uint8_t ** outPtr );
896 static OSStatus DNSMessageGetAnswerSection( const uint8_t *inMsgPtr, size_t inMsgLen, const uint8_t **outPtr );
897 static OSStatus
898 DNSRecordDataToString(
899 const void * inRDataPtr,
900 size_t inRDataLen,
901 unsigned int inRDataType,
902 const void * inMsgPtr,
903 size_t inMsgLen,
904 char ** outString );
905 static OSStatus
906 DomainNameAppendString(
907 uint8_t inDomainName[ kDomainNameLengthMax ],
908 const char * inString,
909 uint8_t ** outEndPtr );
910 static Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 );
911 static OSStatus
912 DomainNameToString(
913 const uint8_t * inDomainName,
914 const uint8_t * inEnd,
915 char inBuf[ kDNSServiceMaxDomainName ],
916 const uint8_t ** outNextPtr );
917
918 static OSStatus PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, Boolean inIsMDNS, Boolean inPrintRaw );
919
920 #define PrintMDNSMessage( MSGPTR, MSGLEN, RAW ) PrintDNSMessage( MSGPTR, MSGLEN, true, RAW )
921 #define PrintUDNSMessage( MSGPTR, MSGLEN, RAW ) PrintDNSMessage( MSGPTR, MSGLEN, false, RAW )
922
923 #define kDNSQueryMessageMaxLen ( kDNSHeaderLength + kDomainNameLengthMax + 4 )
924
925 static OSStatus
926 WriteDNSQueryMessage(
927 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
928 uint16_t inMsgID,
929 uint16_t inFlags,
930 const char * inQName,
931 uint16_t inQType,
932 uint16_t inQClass,
933 size_t * outMsgLen );
934
935 // Dispatch helpers
936
937 typedef void ( *DispatchHandler )( void *inContext );
938
939 static OSStatus
940 DispatchSignalSourceCreate(
941 int inSignal,
942 DispatchHandler inEventHandler,
943 void * inContext,
944 dispatch_source_t * outSource );
945 static OSStatus
946 DispatchReadSourceCreate(
947 SocketRef inSock,
948 DispatchHandler inEventHandler,
949 DispatchHandler inCancelHandler,
950 void * inContext,
951 dispatch_source_t * outSource );
952 static OSStatus
953 DispatchTimerCreate(
954 dispatch_time_t inStart,
955 uint64_t inIntervalNs,
956 uint64_t inLeewayNs,
957 DispatchHandler inEventHandler,
958 DispatchHandler inCancelHandler,
959 void * inContext,
960 dispatch_source_t * outTimer );
961
962 static const char * ServiceTypeDescription( const char *inName );
963
964 typedef struct
965 {
966 SocketRef sock;
967 void * context;
968
969 } SocketContext;
970
971 static void SocketContextCancelHandler( void *inContext );
972 static OSStatus StringToInt32( const char *inString, int32_t *outValue );
973 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue );
974 #if( TARGET_OS_DARWIN )
975 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr );
976 #endif
977
978 #define AddRmvString( X ) ( ( (X) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" )
979 #define Unused( X ) (void)(X)
980
981 //===========================================================================================================================
982 // main
983 //===========================================================================================================================
984
985 int main( int argc, const char **argv )
986 {
987 // Route DebugServices logging output to stderr.
988
989 dlog_control( "DebugServices:output=file;stderr" );
990
991 CLIInit( argc, argv );
992 CLIParse( kGlobalOpts, kCLIFlags_None );
993
994 return( gExitCode );
995 }
996
997 //===========================================================================================================================
998 // VersionOptionCallback
999 //===========================================================================================================================
1000
1001 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset )
1002 {
1003 const char * srcVers;
1004 #if( MDNSRESPONDER_PROJECT )
1005 char srcStr[ 16 ];
1006 #endif
1007
1008 Unused( inOption );
1009 Unused( inArg );
1010 Unused( inUnset );
1011
1012 #if( MDNSRESPONDER_PROJECT )
1013 srcVers = SourceVersionToCString( _DNS_SD_H, srcStr );
1014 #else
1015 srcVers = DNSSDUTIL_SOURCE_VERSION;
1016 #endif
1017 FPrintF( stdout, "%s version %v (%s)\n", gProgramName, kDNSSDUtilNumVersion, srcVers );
1018
1019 return( kEndingErr );
1020 }
1021
1022 //===========================================================================================================================
1023 // BrowseCmd
1024 //===========================================================================================================================
1025
1026 typedef struct BrowseResolveOp BrowseResolveOp;
1027
1028 struct BrowseResolveOp
1029 {
1030 BrowseResolveOp * next; // Next resolve operation in list.
1031 DNSServiceRef sdRef; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
1032 char * fullName; // Full name of the service to resolve.
1033 uint32_t interfaceIndex; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
1034 };
1035
1036 typedef struct
1037 {
1038 DNSServiceRef mainRef; // Main sdRef for shared connection.
1039 DNSServiceRef * opRefs; // Array of sdRefs for individual Browse operarions.
1040 size_t opRefsCount; // Count of array of sdRefs for non-shared connections.
1041 const char * domain; // Domain for DNSServiceBrowse operation(s).
1042 DNSServiceFlags flags; // Flags for DNSServiceBrowse operation(s).
1043 char ** serviceTypes; // Array of service types to browse for.
1044 size_t serviceTypesCount; // Count of array of service types to browse for.
1045 int timeLimitSecs; // Time limit of DNSServiceBrowse operation in seconds.
1046 BrowseResolveOp * resolveList; // List of resolve and/or TXT record query operations.
1047 uint32_t ifIndex; // Interface index of DNSServiceBrowse operation(s).
1048 Boolean printedHeader; // True if results header has been printed.
1049 Boolean doResolve; // True if service instances are to be resolved.
1050 Boolean doResolveTXTOnly; // True if TXT records of service instances are to be queried.
1051
1052 } BrowseContext;
1053
1054 static void BrowsePrintPrologue( const BrowseContext *inContext );
1055 static void BrowseContextFree( BrowseContext *inContext );
1056 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp );
1057 static void BrowseResolveOpFree( BrowseResolveOp *inOp );
1058 static void DNSSD_API
1059 BrowseCallback(
1060 DNSServiceRef inSDRef,
1061 DNSServiceFlags inFlags,
1062 uint32_t inInterfaceIndex,
1063 DNSServiceErrorType inError,
1064 const char * inName,
1065 const char * inRegType,
1066 const char * inDomain,
1067 void * inContext );
1068 static void DNSSD_API
1069 BrowseResolveCallback(
1070 DNSServiceRef inSDRef,
1071 DNSServiceFlags inFlags,
1072 uint32_t inInterfaceIndex,
1073 DNSServiceErrorType inError,
1074 const char * inFullName,
1075 const char * inHostname,
1076 uint16_t inPort,
1077 uint16_t inTXTLen,
1078 const unsigned char * inTXTPtr,
1079 void * inContext );
1080 static void DNSSD_API
1081 BrowseQueryRecordCallback(
1082 DNSServiceRef inSDRef,
1083 DNSServiceFlags inFlags,
1084 uint32_t inInterfaceIndex,
1085 DNSServiceErrorType inError,
1086 const char * inFullName,
1087 uint16_t inType,
1088 uint16_t inClass,
1089 uint16_t inRDataLen,
1090 const void * inRDataPtr,
1091 uint32_t inTTL,
1092 void * inContext );
1093
1094 static void BrowseCmd( void )
1095 {
1096 OSStatus err;
1097 size_t i;
1098 BrowseContext * context = NULL;
1099 dispatch_source_t signalSource = NULL;
1100 int useMainConnection;
1101
1102 // Set up SIGINT handler.
1103
1104 signal( SIGINT, SIG_IGN );
1105 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
1106 require_noerr( err, exit );
1107 dispatch_resume( signalSource );
1108
1109 // Create context.
1110
1111 context = (BrowseContext *) calloc( 1, sizeof( *context ) );
1112 require_action( context, exit, err = kNoMemoryErr );
1113
1114 context->opRefs = (DNSServiceRef *) calloc( gBrowse_ServiceTypesCount, sizeof( DNSServiceRef ) );
1115 require_action( context->opRefs, exit, err = kNoMemoryErr );
1116 context->opRefsCount = gBrowse_ServiceTypesCount;
1117
1118 // Check command parameters.
1119
1120 if( gBrowse_TimeLimitSecs < 0 )
1121 {
1122 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs );
1123 err = kParamErr;
1124 goto exit;
1125 }
1126
1127 // Create main connection.
1128
1129 if( gConnectionOpt )
1130 {
1131 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
1132 require_noerr_quiet( err, exit );
1133 useMainConnection = true;
1134 }
1135 else
1136 {
1137 useMainConnection = false;
1138 }
1139
1140 // Get flags.
1141
1142 context->flags = GetDNSSDFlagsFromOpts();
1143 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
1144
1145 // Get interface.
1146
1147 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
1148 require_noerr_quiet( err, exit );
1149
1150 // Set remaining parameters.
1151
1152 context->serviceTypes = gBrowse_ServiceTypes;
1153 context->serviceTypesCount = gBrowse_ServiceTypesCount;
1154 context->domain = gBrowse_Domain;
1155 context->doResolve = gBrowse_DoResolve ? true : false;
1156 context->timeLimitSecs = gBrowse_TimeLimitSecs;
1157 context->doResolveTXTOnly = gBrowse_QueryTXT ? true : false;
1158
1159 // Print prologue.
1160
1161 BrowsePrintPrologue( context );
1162
1163 // Start operation(s).
1164
1165 for( i = 0; i < context->serviceTypesCount; ++i )
1166 {
1167 DNSServiceRef sdRef;
1168
1169 if( useMainConnection ) sdRef = context->mainRef;
1170 err = DNSServiceBrowse( &sdRef, context->flags, context->ifIndex, context->serviceTypes[ i ], context->domain,
1171 BrowseCallback, context );
1172 require_noerr( err, exit );
1173
1174 context->opRefs[ i ] = sdRef;
1175 if( !useMainConnection )
1176 {
1177 err = DNSServiceSetDispatchQueue( context->opRefs[ i ], dispatch_get_main_queue() );
1178 require_noerr( err, exit );
1179 }
1180 }
1181
1182 // Set time limit.
1183
1184 if( context->timeLimitSecs > 0 )
1185 {
1186 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
1187 kExitReason_TimeLimit, Exit );
1188 }
1189 dispatch_main();
1190
1191 exit:
1192 dispatch_source_forget( &signalSource );
1193 if( context ) BrowseContextFree( context );
1194 if( err ) exit( 1 );
1195 }
1196
1197 //===========================================================================================================================
1198 // BrowsePrintPrologue
1199 //===========================================================================================================================
1200
1201 static void BrowsePrintPrologue( const BrowseContext *inContext )
1202 {
1203 const int timeLimitSecs = inContext->timeLimitSecs;
1204 const char * const * serviceType = (const char **) inContext->serviceTypes;
1205 const char * const * const end = (const char **) inContext->serviceTypes + inContext->serviceTypesCount;
1206 char time[ kTimestampBufLen ];
1207 char ifName[ kInterfaceNameBufLen ];
1208
1209 InterfaceIndexToName( inContext->ifIndex, ifName );
1210
1211 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
1212 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
1213 FPrintF( stdout, "Service types: %s", *serviceType++ );
1214 while( serviceType < end ) FPrintF( stdout, ", %s", *serviceType++ );
1215 FPrintF( stdout, "\n" );
1216 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
1217 FPrintF( stdout, "Time limit: " );
1218 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
1219 else FPrintF( stdout, "∞\n" );
1220 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
1221 FPrintF( stdout, "---\n" );
1222 }
1223
1224 //===========================================================================================================================
1225 // BrowseContextFree
1226 //===========================================================================================================================
1227
1228 static void BrowseContextFree( BrowseContext *inContext )
1229 {
1230 size_t i;
1231
1232 for( i = 0; i < inContext->opRefsCount; ++i )
1233 {
1234 DNSServiceForget( &inContext->opRefs[ i ] );
1235 }
1236 if( inContext->serviceTypes )
1237 {
1238 StringArray_Free( inContext->serviceTypes, inContext->serviceTypesCount );
1239 inContext->serviceTypes = NULL;
1240 inContext->serviceTypesCount = 0;
1241 }
1242 DNSServiceForget( &inContext->mainRef );
1243 free( inContext );
1244 }
1245
1246 //===========================================================================================================================
1247 // BrowseResolveOpCreate
1248 //===========================================================================================================================
1249
1250 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp )
1251 {
1252 OSStatus err;
1253 BrowseResolveOp * resolveOp;
1254
1255 resolveOp = (BrowseResolveOp *) calloc( 1, sizeof( *resolveOp ) );
1256 require_action( resolveOp, exit, err = kNoMemoryErr );
1257
1258 resolveOp->fullName = strdup( inFullName );
1259 require_action( resolveOp->fullName, exit, err = kNoMemoryErr );
1260
1261 resolveOp->interfaceIndex = inInterfaceIndex;
1262
1263 *outOp = resolveOp;
1264 resolveOp = NULL;
1265 err = kNoErr;
1266
1267 exit:
1268 if( resolveOp ) BrowseResolveOpFree( resolveOp );
1269 return( err );
1270 }
1271
1272 //===========================================================================================================================
1273 // BrowseResolveOpFree
1274 //===========================================================================================================================
1275
1276 static void BrowseResolveOpFree( BrowseResolveOp *inOp )
1277 {
1278 DNSServiceForget( &inOp->sdRef );
1279 ForgetMem( &inOp->fullName );
1280 free( inOp );
1281 }
1282
1283 //===========================================================================================================================
1284 // BrowseCallback
1285 //===========================================================================================================================
1286
1287 static void DNSSD_API
1288 BrowseCallback(
1289 DNSServiceRef inSDRef,
1290 DNSServiceFlags inFlags,
1291 uint32_t inInterfaceIndex,
1292 DNSServiceErrorType inError,
1293 const char * inName,
1294 const char * inRegType,
1295 const char * inDomain,
1296 void * inContext )
1297 {
1298 BrowseContext * const context = (BrowseContext *) inContext;
1299 OSStatus err;
1300 BrowseResolveOp * newOp = NULL;
1301 BrowseResolveOp ** p;
1302 char fullName[ kDNSServiceMaxDomainName ];
1303 char time[ kTimestampBufLen ];
1304
1305 Unused( inSDRef );
1306
1307 GetTimestampStr( time );
1308
1309 err = inError;
1310 require_noerr( err, exit );
1311
1312 if( !context->printedHeader )
1313 {
1314 FPrintF( stdout, "%-26s A/R Flags IF %-20s %-20s Instance Name\n", "Timestamp", "Domain", "Service Type" );
1315 context->printedHeader = true;
1316 }
1317 FPrintF( stdout, "%-26s %-3s %5X %2d %-20s %-20s %s\n",
1318 time, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inDomain, inRegType, inName );
1319
1320 if( !context->doResolve && !context->doResolveTXTOnly ) goto exit;
1321
1322 err = DNSServiceConstructFullName( fullName, inName, inRegType, inDomain );
1323 require_noerr( err, exit );
1324
1325 if( inFlags & kDNSServiceFlagsAdd )
1326 {
1327 DNSServiceRef sdRef;
1328 DNSServiceFlags flags;
1329
1330 err = BrowseResolveOpCreate( fullName, inInterfaceIndex, &newOp );
1331 require_noerr( err, exit );
1332
1333 if( context->mainRef )
1334 {
1335 sdRef = context->mainRef;
1336 flags = kDNSServiceFlagsShareConnection;
1337 }
1338 else
1339 {
1340 flags = 0;
1341 }
1342 if( context->doResolve )
1343 {
1344 err = DNSServiceResolve( &sdRef, flags, inInterfaceIndex, inName, inRegType, inDomain, BrowseResolveCallback,
1345 NULL );
1346 require_noerr( err, exit );
1347 }
1348 else
1349 {
1350 err = DNSServiceQueryRecord( &sdRef, flags, inInterfaceIndex, fullName, kDNSServiceType_TXT, kDNSServiceClass_IN,
1351 BrowseQueryRecordCallback, NULL );
1352 require_noerr( err, exit );
1353 }
1354
1355 newOp->sdRef = sdRef;
1356 if( !context->mainRef )
1357 {
1358 err = DNSServiceSetDispatchQueue( newOp->sdRef, dispatch_get_main_queue() );
1359 require_noerr( err, exit );
1360 }
1361 for( p = &context->resolveList; *p; p = &( *p )->next ) {}
1362 *p = newOp;
1363 newOp = NULL;
1364 }
1365 else
1366 {
1367 BrowseResolveOp * resolveOp;
1368
1369 for( p = &context->resolveList; ( resolveOp = *p ) != NULL; p = &resolveOp->next )
1370 {
1371 if( ( resolveOp->interfaceIndex == inInterfaceIndex ) && ( strcasecmp( resolveOp->fullName, fullName ) == 0 ) )
1372 {
1373 break;
1374 }
1375 }
1376 if( resolveOp )
1377 {
1378 *p = resolveOp->next;
1379 BrowseResolveOpFree( resolveOp );
1380 }
1381 }
1382
1383 exit:
1384 if( newOp ) BrowseResolveOpFree( newOp );
1385 if( err ) exit( 1 );
1386 }
1387
1388 //===========================================================================================================================
1389 // BrowseQueryRecordCallback
1390 //===========================================================================================================================
1391
1392 static void DNSSD_API
1393 BrowseQueryRecordCallback(
1394 DNSServiceRef inSDRef,
1395 DNSServiceFlags inFlags,
1396 uint32_t inInterfaceIndex,
1397 DNSServiceErrorType inError,
1398 const char * inFullName,
1399 uint16_t inType,
1400 uint16_t inClass,
1401 uint16_t inRDataLen,
1402 const void * inRDataPtr,
1403 uint32_t inTTL,
1404 void * inContext )
1405 {
1406 OSStatus err;
1407 char time[ kTimestampBufLen ];
1408
1409 Unused( inSDRef );
1410 Unused( inClass );
1411 Unused( inTTL );
1412 Unused( inContext );
1413
1414 GetTimestampStr( time );
1415
1416 err = inError;
1417 require_noerr( err, exit );
1418 require_action( inType == kDNSServiceType_TXT, exit, err = kTypeErr );
1419
1420 FPrintF( stdout, "%s %s %s TXT on interface %d\n TXT: %#{txt}\n",
1421 time, AddRmvString( inFlags ), inFullName, (int32_t) inInterfaceIndex, inRDataPtr, (size_t) inRDataLen );
1422
1423 exit:
1424 if( err ) exit( 1 );
1425 }
1426
1427 //===========================================================================================================================
1428 // BrowseResolveCallback
1429 //===========================================================================================================================
1430
1431 static void DNSSD_API
1432 BrowseResolveCallback(
1433 DNSServiceRef inSDRef,
1434 DNSServiceFlags inFlags,
1435 uint32_t inInterfaceIndex,
1436 DNSServiceErrorType inError,
1437 const char * inFullName,
1438 const char * inHostname,
1439 uint16_t inPort,
1440 uint16_t inTXTLen,
1441 const unsigned char * inTXTPtr,
1442 void * inContext )
1443 {
1444 char time[ kTimestampBufLen ];
1445 char errorStr[ 64 ];
1446
1447 Unused( inSDRef );
1448 Unused( inFlags );
1449 Unused( inContext );
1450
1451 GetTimestampStr( time );
1452
1453 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
1454
1455 FPrintF( stdout, "%s %s can be reached at %s:%u (interface %d)%?s\n",
1456 time, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
1457 if( inTXTLen == 1 )
1458 {
1459 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
1460 }
1461 else
1462 {
1463 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
1464 }
1465 }
1466
1467 //===========================================================================================================================
1468 // GetAddrInfoCmd
1469 //===========================================================================================================================
1470
1471 typedef struct
1472 {
1473 DNSServiceRef mainRef; // Main sdRef for shared connection.
1474 DNSServiceRef opRef; // sdRef for the DNSServiceGetAddrInfo operation.
1475 const char * name; // Hostname to resolve.
1476 DNSServiceFlags flags; // Flags argument for DNSServiceGetAddrInfo().
1477 DNSServiceProtocol protocols; // Protocols argument for DNSServiceGetAddrInfo().
1478 uint32_t ifIndex; // Interface index argument for DNSServiceGetAddrInfo().
1479 int timeLimitSecs; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
1480 Boolean printedHeader; // True if the results header has been printed.
1481 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
1482 Boolean needIPv4; // True if in one-shot mode and an IPv4 result is needed.
1483 Boolean needIPv6; // True if in one-shot mode and an IPv6 result is needed.
1484
1485 } GetAddrInfoContext;
1486
1487 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext );
1488 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext );
1489 static void DNSSD_API
1490 GetAddrInfoCallback(
1491 DNSServiceRef inSDRef,
1492 DNSServiceFlags inFlags,
1493 uint32_t inInterfaceIndex,
1494 DNSServiceErrorType inError,
1495 const char * inHostname,
1496 const struct sockaddr * inSockAddr,
1497 uint32_t inTTL,
1498 void * inContext );
1499
1500 static void GetAddrInfoCmd( void )
1501 {
1502 OSStatus err;
1503 DNSServiceRef sdRef;
1504 GetAddrInfoContext * context = NULL;
1505 dispatch_source_t signalSource = NULL;
1506 int useMainConnection;
1507
1508 // Set up SIGINT handler.
1509
1510 signal( SIGINT, SIG_IGN );
1511 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
1512 require_noerr( err, exit );
1513 dispatch_resume( signalSource );
1514
1515 // Check command parameters.
1516
1517 if( gGetAddrInfo_TimeLimitSecs < 0 )
1518 {
1519 FPrintF( stderr, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs );
1520 err = kParamErr;
1521 goto exit;
1522 }
1523
1524 // Create context.
1525
1526 context = (GetAddrInfoContext *) calloc( 1, sizeof( *context ) );
1527 require_action( context, exit, err = kNoMemoryErr );
1528
1529 // Create main connection.
1530
1531 if( gConnectionOpt )
1532 {
1533 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
1534 require_noerr_quiet( err, exit );
1535 useMainConnection = true;
1536 }
1537 else
1538 {
1539 useMainConnection = false;
1540 }
1541
1542 // Get flags.
1543
1544 context->flags = GetDNSSDFlagsFromOpts();
1545 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
1546
1547 // Get interface.
1548
1549 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
1550 require_noerr_quiet( err, exit );
1551
1552 // Set remaining parameters.
1553
1554 context->name = gGetAddrInfo_Name;
1555 context->timeLimitSecs = gGetAddrInfo_TimeLimitSecs;
1556 if( gGetAddrInfo_ProtocolIPv4 ) context->protocols |= kDNSServiceProtocol_IPv4;
1557 if( gGetAddrInfo_ProtocolIPv6 ) context->protocols |= kDNSServiceProtocol_IPv6;
1558 if( gGetAddrInfo_OneShot )
1559 {
1560 context->oneShotMode = true;
1561 context->needIPv4 = ( gGetAddrInfo_ProtocolIPv4 || !gGetAddrInfo_ProtocolIPv6 ) ? true : false;
1562 context->needIPv6 = ( gGetAddrInfo_ProtocolIPv6 || !gGetAddrInfo_ProtocolIPv4 ) ? true : false;
1563 }
1564
1565 // Print prologue.
1566
1567 GetAddrInfoPrintPrologue( context );
1568
1569 // Start operation.
1570
1571 if( useMainConnection ) sdRef = context->mainRef;
1572 err = DNSServiceGetAddrInfo( &sdRef, context->flags, context->ifIndex, context->protocols, context->name,
1573 GetAddrInfoCallback, context );
1574 require_noerr( err, exit );
1575
1576 context->opRef = sdRef;
1577 if( !useMainConnection )
1578 {
1579 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
1580 require_noerr( err, exit );
1581 }
1582
1583 // Set time limit.
1584
1585 if( context->timeLimitSecs > 0 )
1586 {
1587 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
1588 kExitReason_TimeLimit, Exit );
1589 }
1590 dispatch_main();
1591
1592 exit:
1593 dispatch_source_forget( &signalSource );
1594 if( context ) GetAddrInfoContextFree( context );
1595 if( err ) exit( 1 );
1596 }
1597
1598 //===========================================================================================================================
1599 // GetAddrInfoPrintPrologue
1600 //===========================================================================================================================
1601
1602 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext )
1603 {
1604 const int timeLimitSecs = inContext->timeLimitSecs;
1605 char ifName[ kInterfaceNameBufLen ];
1606 char time[ kTimestampBufLen ];
1607
1608 InterfaceIndexToName( inContext->ifIndex, ifName );
1609
1610 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
1611 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
1612 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
1613 FPrintF( stdout, "Name: %s\n", inContext->name );
1614 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
1615 FPrintF( stdout, "Time limit: " );
1616 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
1617 else FPrintF( stdout, "∞\n" );
1618 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
1619 FPrintF( stdout, "---\n" );
1620 }
1621
1622 //===========================================================================================================================
1623 // GetAddrInfoContextFree
1624 //===========================================================================================================================
1625
1626 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext )
1627 {
1628 DNSServiceForget( &inContext->opRef );
1629 DNSServiceForget( &inContext->mainRef );
1630 free( inContext );
1631 }
1632
1633 //===========================================================================================================================
1634 // GetAddrInfoCallback
1635 //===========================================================================================================================
1636
1637 static void DNSSD_API
1638 GetAddrInfoCallback(
1639 DNSServiceRef inSDRef,
1640 DNSServiceFlags inFlags,
1641 uint32_t inInterfaceIndex,
1642 DNSServiceErrorType inError,
1643 const char * inHostname,
1644 const struct sockaddr * inSockAddr,
1645 uint32_t inTTL,
1646 void * inContext )
1647 {
1648 GetAddrInfoContext * const context = (GetAddrInfoContext *) inContext;
1649 OSStatus err;
1650 const char * addrStr;
1651 char addrStrBuf[ kSockAddrStringMaxSize ];
1652 char time[ kTimestampBufLen ];
1653
1654 Unused( inSDRef );
1655
1656 GetTimestampStr( time );
1657
1658 switch( inError )
1659 {
1660 case kDNSServiceErr_NoError:
1661 case kDNSServiceErr_NoSuchRecord:
1662 err = kNoErr;
1663 break;
1664
1665 case kDNSServiceErr_Timeout:
1666 Exit( kExitReason_Timeout );
1667
1668 default:
1669 err = inError;
1670 goto exit;
1671 }
1672
1673 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
1674 {
1675 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
1676 err = kTypeErr;
1677 goto exit;
1678 }
1679
1680 if( !inError )
1681 {
1682 err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf );
1683 require_noerr( err, exit );
1684 addrStr = addrStrBuf;
1685 }
1686 else
1687 {
1688 addrStr = ( inSockAddr->sa_family == AF_INET ) ? "No Such Record (A)" : "No Such Record (AAAA)";
1689 }
1690
1691 if( !context->printedHeader )
1692 {
1693 FPrintF( stdout, "%-26s A/R Flags IF %-32s %-38s %6s\n", "Timestamp", "Hostname", "Address", "TTL" );
1694 context->printedHeader = true;
1695 }
1696 FPrintF( stdout, "%-26s %s %5X %2d %-32s %-38s %6u\n",
1697 time, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inHostname, addrStr, inTTL );
1698
1699 if( context->oneShotMode )
1700 {
1701 if( inFlags & kDNSServiceFlagsAdd )
1702 {
1703 if( inSockAddr->sa_family == AF_INET ) context->needIPv4 = false;
1704 else context->needIPv6 = false;
1705 }
1706 if( !( inFlags & kDNSServiceFlagsMoreComing ) && !context->needIPv4 && !context->needIPv6 )
1707 {
1708 Exit( kExitReason_OneShotDone );
1709 }
1710 }
1711
1712 exit:
1713 if( err ) exit( 1 );
1714 }
1715
1716 //===========================================================================================================================
1717 // QueryRecordCmd
1718 //===========================================================================================================================
1719
1720 typedef struct
1721 {
1722 DNSServiceRef mainRef; // Main sdRef for shared connection.
1723 DNSServiceRef opRef; // sdRef for the DNSServiceQueryRecord operation.
1724 const char * recordName; // Resource record name argument for DNSServiceQueryRecord().
1725 DNSServiceFlags flags; // Flags argument for DNSServiceQueryRecord().
1726 uint32_t ifIndex; // Interface index argument for DNSServiceQueryRecord().
1727 int timeLimitSecs; // Time limit for the DNSServiceQueryRecord() operation in seconds.
1728 uint16_t recordType; // Resource record type argument for DNSServiceQueryRecord().
1729 Boolean printedHeader; // True if the results header was printed.
1730 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
1731 Boolean gotRecord; // True if in one-shot mode and received at least one record of the desired type.
1732 Boolean printRawRData; // True if RDATA results are not to be formatted when printed.
1733
1734 } QueryRecordContext;
1735
1736 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext );
1737 static void QueryRecordContextFree( QueryRecordContext *inContext );
1738 static void DNSSD_API
1739 QueryRecordCallback(
1740 DNSServiceRef inSDRef,
1741 DNSServiceFlags inFlags,
1742 uint32_t inInterfaceIndex,
1743 DNSServiceErrorType inError,
1744 const char * inFullName,
1745 uint16_t inType,
1746 uint16_t inClass,
1747 uint16_t inRDataLen,
1748 const void * inRDataPtr,
1749 uint32_t inTTL,
1750 void * inContext );
1751
1752 static void QueryRecordCmd( void )
1753 {
1754 OSStatus err;
1755 DNSServiceRef sdRef;
1756 QueryRecordContext * context = NULL;
1757 dispatch_source_t signalSource = NULL;
1758 int useMainConnection;
1759
1760 // Set up SIGINT handler.
1761
1762 signal( SIGINT, SIG_IGN );
1763 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
1764 require_noerr( err, exit );
1765 dispatch_resume( signalSource );
1766
1767 // Create context.
1768
1769 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
1770 require_action( context, exit, err = kNoMemoryErr );
1771
1772 // Check command parameters.
1773
1774 if( gQueryRecord_TimeLimitSecs < 0 )
1775 {
1776 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs );
1777 err = kParamErr;
1778 goto exit;
1779 }
1780
1781 // Create main connection.
1782
1783 if( gConnectionOpt )
1784 {
1785 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
1786 require_noerr_quiet( err, exit );
1787 useMainConnection = true;
1788 }
1789 else
1790 {
1791 useMainConnection = false;
1792 }
1793
1794 // Get flags.
1795
1796 context->flags = GetDNSSDFlagsFromOpts();
1797 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
1798
1799 // Get interface.
1800
1801 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
1802 require_noerr_quiet( err, exit );
1803
1804 // Get record type.
1805
1806 err = RecordTypeFromArgString( gQueryRecord_Type, &context->recordType );
1807 require_noerr( err, exit );
1808
1809 // Set remaining parameters.
1810
1811 context->recordName = gQueryRecord_Name;
1812 context->timeLimitSecs = gQueryRecord_TimeLimitSecs;
1813 context->oneShotMode = gQueryRecord_OneShot ? true : false;
1814 context->printRawRData = gQueryRecord_RawRData ? true : false;
1815
1816 // Print prologue.
1817
1818 QueryRecordPrintPrologue( context );
1819
1820 // Start operation.
1821
1822 if( useMainConnection ) sdRef = context->mainRef;
1823 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
1824 kDNSServiceClass_IN, QueryRecordCallback, context );
1825 require_noerr( err, exit );
1826
1827 context->opRef = sdRef;
1828 if( !useMainConnection )
1829 {
1830 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
1831 require_noerr( err, exit );
1832 }
1833
1834 // Set time limit.
1835
1836 if( context->timeLimitSecs > 0 )
1837 {
1838 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_TimeLimit,
1839 Exit );
1840 }
1841 dispatch_main();
1842
1843 exit:
1844 dispatch_source_forget( &signalSource );
1845 if( context ) QueryRecordContextFree( context );
1846 if( err ) exit( 1 );
1847 }
1848
1849 //===========================================================================================================================
1850 // QueryRecordContextFree
1851 //===========================================================================================================================
1852
1853 static void QueryRecordContextFree( QueryRecordContext *inContext )
1854 {
1855 DNSServiceForget( &inContext->opRef );
1856 DNSServiceForget( &inContext->mainRef );
1857 free( inContext );
1858 }
1859
1860 //===========================================================================================================================
1861 // QueryRecordPrintPrologue
1862 //===========================================================================================================================
1863
1864 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext )
1865 {
1866 const int timeLimitSecs = inContext->timeLimitSecs;
1867 char ifName[ kInterfaceNameBufLen ];
1868 char time[ kTimestampBufLen ];
1869
1870 InterfaceIndexToName( inContext->ifIndex, ifName );
1871
1872 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
1873 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
1874 FPrintF( stdout, "Name: %s\n", inContext->recordName );
1875 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
1876 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
1877 FPrintF( stdout, "Time limit: " );
1878 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
1879 else FPrintF( stdout, "∞\n" );
1880 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
1881 FPrintF( stdout, "---\n" );
1882
1883 }
1884
1885 //===========================================================================================================================
1886 // QueryRecordCallback
1887 //===========================================================================================================================
1888
1889 static void DNSSD_API
1890 QueryRecordCallback(
1891 DNSServiceRef inSDRef,
1892 DNSServiceFlags inFlags,
1893 uint32_t inInterfaceIndex,
1894 DNSServiceErrorType inError,
1895 const char * inFullName,
1896 uint16_t inType,
1897 uint16_t inClass,
1898 uint16_t inRDataLen,
1899 const void * inRDataPtr,
1900 uint32_t inTTL,
1901 void * inContext )
1902 {
1903 QueryRecordContext * const context = (QueryRecordContext *) inContext;
1904 OSStatus err;
1905 char * rdataStr = NULL;
1906 char time[ kTimestampBufLen ];
1907
1908 Unused( inSDRef );
1909
1910 GetTimestampStr( time );
1911
1912 switch( inError )
1913 {
1914 case kDNSServiceErr_NoError:
1915 case kDNSServiceErr_NoSuchRecord:
1916 err = kNoErr;
1917 break;
1918
1919 case kDNSServiceErr_Timeout:
1920 Exit( kExitReason_Timeout );
1921
1922 default:
1923 err = inError;
1924 goto exit;
1925 }
1926
1927 if( inError == kDNSServiceErr_NoSuchRecord )
1928 {
1929 ASPrintF( &rdataStr, "No Such Record" );
1930 }
1931 else
1932 {
1933 if( !context->printRawRData ) DNSRecordDataToString( inRDataPtr, inRDataLen, inType, NULL, 0, &rdataStr );
1934 if( !rdataStr )
1935 {
1936 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, INT_MAX );
1937 require_action( rdataStr, exit, err = kNoMemoryErr );
1938 }
1939 }
1940
1941 if( !context->printedHeader )
1942 {
1943 FPrintF( stdout, "%-26s A/R Flags IF %-32s %-5s %-5s %6s RData\n", "Timestamp", "Name", "Type", "Class", "TTL" );
1944 context->printedHeader = true;
1945 }
1946 FPrintF( stdout, "%-26s %-3s %5X %2d %-32s %-5s %?-5s%?5u %6u %s\n",
1947 time, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inFullName, RecordTypeToString( inType ),
1948 ( inClass == kDNSServiceClass_IN ), "IN", ( inClass != kDNSServiceClass_IN ), inClass, inTTL, rdataStr );
1949
1950 if( context->oneShotMode )
1951 {
1952 if( ( inFlags & kDNSServiceFlagsAdd ) &&
1953 ( ( context->recordType == kDNSServiceType_ANY ) || ( context->recordType == inType ) ) )
1954 {
1955 context->gotRecord = true;
1956 }
1957 if( !( inFlags & kDNSServiceFlagsMoreComing ) && context->gotRecord ) Exit( kExitReason_OneShotDone );
1958 }
1959
1960 exit:
1961 FreeNullSafe( rdataStr );
1962 if( err ) exit( 1 );
1963 }
1964
1965 //===========================================================================================================================
1966 // RegisterCmd
1967 //===========================================================================================================================
1968
1969 typedef struct
1970 {
1971 DNSRecordRef recordRef; // Reference returned by DNSServiceAddRecord().
1972 uint8_t * dataPtr; // Record data.
1973 size_t dataLen; // Record data length.
1974 uint32_t ttl; // Record TTL value.
1975 uint16_t type; // Record type.
1976
1977 } ExtraRecord;
1978
1979 typedef struct
1980 {
1981 DNSServiceRef opRef; // sdRef for DNSServiceRegister operation.
1982 const char * name; // Service name argument for DNSServiceRegister().
1983 const char * type; // Service type argument for DNSServiceRegister().
1984 const char * domain; // Domain in which advertise the service.
1985 uint8_t * txtPtr; // Service TXT record data. (malloc'd)
1986 size_t txtLen; // Service TXT record data len.
1987 ExtraRecord * extraRecords; // Array of extra records to add to registered service.
1988 size_t extraRecordsCount; // Number of extra records.
1989 uint8_t * updateTXTPtr; // Pointer to record data for TXT record update. (malloc'd)
1990 size_t updateTXTLen; // Length of record data for TXT record update.
1991 uint32_t updateTTL; // TTL of updated TXT record.
1992 int updateDelayMs; // Post-registration TXT record update delay in milliseconds.
1993 DNSServiceFlags flags; // Flags argument for DNSServiceRegister().
1994 uint32_t ifIndex; // Interface index argument for DNSServiceRegister().
1995 int lifetimeMs; // Lifetime of the record registration in milliseconds.
1996 uint16_t port; // Service instance's port number.
1997 Boolean printedHeader; // True if results header was printed.
1998 Boolean didRegister; // True if service was registered.
1999
2000 } RegisterContext;
2001
2002 static void RegisterPrintPrologue( const RegisterContext *inContext );
2003 static void RegisterContextFree( RegisterContext *inContext );
2004 static void DNSSD_API
2005 RegisterCallback(
2006 DNSServiceRef inSDRef,
2007 DNSServiceFlags inFlags,
2008 DNSServiceErrorType inError,
2009 const char * inName,
2010 const char * inType,
2011 const char * inDomain,
2012 void * inContext );
2013 static void RegisterUpdate( void *inContext );
2014
2015 static void RegisterCmd( void )
2016 {
2017 OSStatus err;
2018 RegisterContext * context = NULL;
2019 dispatch_source_t signalSource = NULL;
2020
2021 // Set up SIGINT handler.
2022
2023 signal( SIGINT, SIG_IGN );
2024 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
2025 require_noerr( err, exit );
2026 dispatch_resume( signalSource );
2027
2028 // Create context.
2029
2030 context = (RegisterContext *) calloc( 1, sizeof( *context ) );
2031 require_action( context, exit, err = kNoMemoryErr );
2032
2033 // Check command parameters.
2034
2035 if( ( gRegister_Port < 0 ) || ( gRegister_Port > UINT16_MAX ) )
2036 {
2037 FPrintF( stderr, "Port number %d is out-of-range.\n", gRegister_Port );
2038 err = kParamErr;
2039 goto exit;
2040 }
2041
2042 if( ( gAddRecord_DataCount != gAddRecord_TypesCount ) || ( gAddRecord_TTLsCount != gAddRecord_TypesCount ) )
2043 {
2044 FPrintF( stderr, "There are missing additional record parameters.\n" );
2045 err = kParamErr;
2046 goto exit;
2047 }
2048
2049 // Get flags.
2050
2051 context->flags = GetDNSSDFlagsFromOpts();
2052
2053 // Get interface index.
2054
2055 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
2056 require_noerr_quiet( err, exit );
2057
2058 // Get TXT record data.
2059
2060 if( gRegister_TXT )
2061 {
2062 err = RecordDataFromArgString( gRegister_TXT, &context->txtPtr, &context->txtLen );
2063 require_noerr_quiet( err, exit );
2064 }
2065
2066 // Set remaining parameters.
2067
2068 context->name = gRegister_Name;
2069 context->type = gRegister_Type;
2070 context->domain = gRegister_Domain;
2071 context->port = (uint16_t) gRegister_Port;
2072 context->lifetimeMs = gRegister_LifetimeMs;
2073
2074 if( gAddRecord_TypesCount > 0 )
2075 {
2076 size_t i;
2077
2078 context->extraRecords = (ExtraRecord *) calloc( gAddRecord_TypesCount, sizeof( ExtraRecord ) );
2079 require_action( context, exit, err = kNoMemoryErr );
2080 context->extraRecordsCount = gAddRecord_TypesCount;
2081
2082 for( i = 0; i < gAddRecord_TypesCount; ++i )
2083 {
2084 ExtraRecord * const extraRecord = &context->extraRecords[ i ];
2085
2086 err = RecordTypeFromArgString( gAddRecord_Types[ i ], &extraRecord->type );
2087 require_noerr( err, exit );
2088
2089 err = StringToUInt32( gAddRecord_TTLs[ i ], &extraRecord->ttl );
2090 if( err )
2091 {
2092 FPrintF( stderr, "Invalid TTL value: %s\n", gAddRecord_TTLs[ i ] );
2093 err = kParamErr;
2094 goto exit;
2095 }
2096
2097 err = RecordDataFromArgString( gAddRecord_Data[ i ], &extraRecord->dataPtr, &extraRecord->dataLen );
2098 require_noerr_quiet( err, exit );
2099 }
2100 }
2101
2102 if( gUpdateRecord_Data )
2103 {
2104 err = RecordDataFromArgString( gUpdateRecord_Data, &context->updateTXTPtr, &context->updateTXTLen );
2105 require_noerr_quiet( err, exit );
2106
2107 context->updateTTL = (uint32_t) gUpdateRecord_TTL;
2108 context->updateDelayMs = gUpdateRecord_DelayMs;
2109 }
2110
2111 // Print prologue.
2112
2113 RegisterPrintPrologue( context );
2114
2115 // Start operation.
2116
2117 err = DNSServiceRegister( &context->opRef, context->flags, context->ifIndex, context->name, context->type,
2118 context->domain, NULL, ntohs( context->port ), (uint16_t) context->txtLen, context->txtPtr,
2119 RegisterCallback, context );
2120 ForgetMem( &context->txtPtr );
2121 require_noerr( err, exit );
2122
2123 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
2124 require_noerr( err, exit );
2125
2126 dispatch_main();
2127
2128 exit:
2129 dispatch_source_forget( &signalSource );
2130 if( context ) RegisterContextFree( context );
2131 if( err ) exit( 1 );
2132 }
2133
2134 //===========================================================================================================================
2135 // RegisterPrintPrologue
2136 //===========================================================================================================================
2137
2138 static void RegisterPrintPrologue( const RegisterContext *inContext )
2139 {
2140 size_t i;
2141 int infinite;
2142 char ifName[ kInterfaceNameBufLen ];
2143 char time[ kTimestampBufLen ];
2144
2145 InterfaceIndexToName( inContext->ifIndex, ifName );
2146
2147 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
2148 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
2149 FPrintF( stdout, "Name: %s\n", inContext->name ? inContext->name : "<NULL>" );
2150 FPrintF( stdout, "Type: %s\n", inContext->type );
2151 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
2152 FPrintF( stdout, "Port: %u\n", inContext->port );
2153 FPrintF( stdout, "TXT data: %#{txt}\n", inContext->txtPtr, inContext->txtLen );
2154 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
2155 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
2156 if( inContext->updateTXTPtr )
2157 {
2158 FPrintF( stdout, "\nUpdate record:\n" );
2159 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs > 0 ) ? inContext->updateDelayMs : 0 );
2160 FPrintF( stdout, " TTL: %u%?s\n",
2161 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
2162 FPrintF( stdout, " TXT data: %#{txt}\n", inContext->updateTXTPtr, inContext->updateTXTLen );
2163 }
2164 if( inContext->extraRecordsCount > 0 ) FPrintF( stdout, "\n" );
2165 for( i = 0; i < inContext->extraRecordsCount; ++i )
2166 {
2167 const ExtraRecord * record = &inContext->extraRecords[ i ];
2168
2169 FPrintF( stdout, "Extra record %zu:\n", i + 1 );
2170 FPrintF( stdout, " Type: %s (%u)\n", RecordTypeToString( record->type ), record->type );
2171 FPrintF( stdout, " TTL: %u%?s\n", record->ttl, record->ttl == 0, " (system will use a default value.)" );
2172 FPrintF( stdout, " RData: %#H\n\n", record->dataPtr, (int) record->dataLen, INT_MAX );
2173 }
2174 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
2175 FPrintF( stdout, "---\n" );
2176 }
2177
2178 //===========================================================================================================================
2179 // RegisterContextFree
2180 //===========================================================================================================================
2181
2182 static void RegisterContextFree( RegisterContext *inContext )
2183 {
2184 ExtraRecord * record;
2185 const ExtraRecord * const end = inContext->extraRecords + inContext->extraRecordsCount;
2186
2187 DNSServiceForget( &inContext->opRef );
2188 ForgetMem( &inContext->txtPtr );
2189 for( record = inContext->extraRecords; record < end; ++record )
2190 {
2191 check( !record->recordRef );
2192 ForgetMem( &record->dataPtr );
2193 }
2194 ForgetMem( &inContext->extraRecords );
2195 ForgetMem( &inContext->updateTXTPtr );
2196 free( inContext );
2197 }
2198
2199 //===========================================================================================================================
2200 // RegisterCallback
2201 //===========================================================================================================================
2202
2203 static void DNSSD_API
2204 RegisterCallback(
2205 DNSServiceRef inSDRef,
2206 DNSServiceFlags inFlags,
2207 DNSServiceErrorType inError,
2208 const char * inName,
2209 const char * inType,
2210 const char * inDomain,
2211 void * inContext )
2212 {
2213 RegisterContext * const context = (RegisterContext *) inContext;
2214 OSStatus err;
2215 char time[ kTimestampBufLen ];
2216
2217 Unused( inSDRef );
2218
2219 GetTimestampStr( time );
2220
2221 if( !context->printedHeader )
2222 {
2223 FPrintF( stdout, "%-26s A/R Flags Service\n", "Timestamp" );
2224 context->printedHeader = true;
2225 }
2226 FPrintF( stdout, "%-26s %-3s %5X %s.%s%s %?#m\n",
2227 time, AddRmvString( inFlags ), inFlags, inName, inType, inDomain, inError, inError );
2228
2229 require_noerr_action_quiet( inError, exit, err = inError );
2230
2231 if( !context->didRegister && ( inFlags & kDNSServiceFlagsAdd ) )
2232 {
2233 context->didRegister = true;
2234 if( context->updateTXTPtr )
2235 {
2236 if( context->updateDelayMs > 0 )
2237 {
2238 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
2239 context, RegisterUpdate );
2240 }
2241 else
2242 {
2243 RegisterUpdate( context );
2244 }
2245 }
2246 if( context->extraRecordsCount > 0 )
2247 {
2248 ExtraRecord * record;
2249 const ExtraRecord * const end = context->extraRecords + context->extraRecordsCount;
2250
2251 for( record = context->extraRecords; record < end; ++record )
2252 {
2253 err = DNSServiceAddRecord( context->opRef, &record->recordRef, 0, record->type,
2254 (uint16_t) record->dataLen, record->dataPtr, record->ttl );
2255 require_noerr( err, exit );
2256 }
2257 }
2258 if( context->lifetimeMs == 0 )
2259 {
2260 Exit( kExitReason_TimeLimit );
2261 }
2262 else if( context->lifetimeMs > 0 )
2263 {
2264 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
2265 kExitReason_TimeLimit, Exit );
2266 }
2267 }
2268 err = kNoErr;
2269
2270 exit:
2271 if( err ) exit( 1 );
2272 }
2273
2274 //===========================================================================================================================
2275 // RegisterUpdate
2276 //===========================================================================================================================
2277
2278 static void RegisterUpdate( void *inContext )
2279 {
2280 OSStatus err;
2281 RegisterContext * const context = (RegisterContext *) inContext;
2282
2283 err = DNSServiceUpdateRecord( context->opRef, NULL, 0, (uint16_t) context->updateTXTLen, context->updateTXTPtr,
2284 context->updateTTL );
2285 require_noerr( err, exit );
2286
2287 exit:
2288 if( err ) exit( 1 );
2289 }
2290
2291 //===========================================================================================================================
2292 // RegisterRecordCmd
2293 //===========================================================================================================================
2294
2295 typedef struct
2296 {
2297 DNSServiceRef conRef; // sdRef to be initialized by DNSServiceCreateConnection().
2298 DNSRecordRef recordRef; // Registered record reference.
2299 const char * recordName; // Name of resource record.
2300 uint8_t * dataPtr; // Pointer to resource record data.
2301 size_t dataLen; // Length of resource record data.
2302 uint32_t ttl; // TTL value of resource record in seconds.
2303 uint32_t ifIndex; // Interface index argument for DNSServiceRegisterRecord().
2304 DNSServiceFlags flags; // Flags argument for DNSServiceRegisterRecord().
2305 int lifetimeMs; // Lifetime of the record registration in milliseconds.
2306 uint16_t recordType; // Resource record type.
2307 uint8_t * updateDataPtr; // Pointer to data for record update. (malloc'd)
2308 size_t updateDataLen; // Length of data for record update.
2309 uint32_t updateTTL; // TTL for updated record.
2310 int updateDelayMs; // Post-registration record update delay in milliseconds.
2311 Boolean didRegister; // True if the record was registered.
2312
2313 } RegisterRecordContext;
2314
2315 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext );
2316 static void RegisterRecordContextFree( RegisterRecordContext *inContext );
2317 static void DNSSD_API
2318 RegisterRecordCallback(
2319 DNSServiceRef inSDRef,
2320 DNSRecordRef inRecordRef,
2321 DNSServiceFlags inFlags,
2322 DNSServiceErrorType inError,
2323 void * inContext );
2324 static void RegisterRecordUpdate( void *inContext );
2325
2326 static void RegisterRecordCmd( void )
2327 {
2328 OSStatus err;
2329 RegisterRecordContext * context = NULL;
2330 dispatch_source_t signalSource = NULL;
2331
2332 // Set up SIGINT handler.
2333
2334 signal( SIGINT, SIG_IGN );
2335 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
2336 require_noerr( err, exit );
2337 dispatch_resume( signalSource );
2338
2339 // Create context.
2340
2341 context = (RegisterRecordContext *) calloc( 1, sizeof( *context ) );
2342 require_action( context, exit, err = kNoMemoryErr );
2343
2344 // Create connection.
2345
2346 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->conRef, NULL );
2347 require_noerr_quiet( err, exit );
2348
2349 // Get flags.
2350
2351 context->flags = GetDNSSDFlagsFromOpts();
2352
2353 // Get interface.
2354
2355 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
2356 require_noerr_quiet( err, exit );
2357
2358 // Get record type.
2359
2360 err = RecordTypeFromArgString( gRegisterRecord_Type, &context->recordType );
2361 require_noerr( err, exit );
2362
2363 // Get record data.
2364
2365 if( gRegisterRecord_Data )
2366 {
2367 err = RecordDataFromArgString( gRegisterRecord_Data, &context->dataPtr, &context->dataLen );
2368 require_noerr_quiet( err, exit );
2369 }
2370
2371 // Set remaining parameters.
2372
2373 context->recordName = gRegisterRecord_Name;
2374 context->ttl = (uint32_t) gRegisterRecord_TTL;
2375 context->lifetimeMs = gRegisterRecord_LifetimeMs;
2376
2377 // Get update data.
2378
2379 if( gRegisterRecord_UpdateData )
2380 {
2381 err = RecordDataFromArgString( gRegisterRecord_UpdateData, &context->updateDataPtr, &context->updateDataLen );
2382 require_noerr_quiet( err, exit );
2383
2384 context->updateTTL = (uint32_t) gRegisterRecord_UpdateTTL;
2385 context->updateDelayMs = gRegisterRecord_UpdateDelayMs;
2386 }
2387
2388 // Print prologue.
2389
2390 RegisterRecordPrintPrologue( context );
2391
2392 // Start operation.
2393
2394 err = DNSServiceRegisterRecord( context->conRef, &context->recordRef, context->flags, context->ifIndex,
2395 context->recordName, context->recordType, kDNSServiceClass_IN, (uint16_t) context->dataLen, context->dataPtr,
2396 context->ttl, RegisterRecordCallback, context );
2397 if( err )
2398 {
2399 FPrintF( stderr, "DNSServiceRegisterRecord() returned %#m\n", err );
2400 goto exit;
2401 }
2402
2403 dispatch_main();
2404
2405 exit:
2406 dispatch_source_forget( &signalSource );
2407 if( context ) RegisterRecordContextFree( context );
2408 if( err ) exit( 1 );
2409 }
2410
2411 //===========================================================================================================================
2412 // RegisterRecordPrintPrologue
2413 //===========================================================================================================================
2414
2415 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext )
2416 {
2417 int infinite;
2418 char time[ kTimestampBufLen ];
2419 char ifName[ kInterfaceNameBufLen ];
2420
2421 InterfaceIndexToName( inContext->ifIndex, ifName );
2422
2423 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
2424 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
2425 FPrintF( stdout, "Name: %s\n", inContext->recordName );
2426 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
2427 FPrintF( stdout, "TTL: %u\n", inContext->ttl );
2428 FPrintF( stdout, "Data: %#H\n", inContext->dataPtr, (int) inContext->dataLen, INT_MAX );
2429 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
2430 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
2431 if( inContext->updateDataPtr )
2432 {
2433 FPrintF( stdout, "\nUpdate record:\n" );
2434 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs >= 0 ) ? inContext->updateDelayMs : 0 );
2435 FPrintF( stdout, " TTL: %u%?s\n",
2436 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
2437 FPrintF( stdout, " RData: %#H\n", inContext->updateDataPtr, (int) inContext->updateDataLen, INT_MAX );
2438 }
2439 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
2440 FPrintF( stdout, "---\n" );
2441 }
2442
2443 //===========================================================================================================================
2444 // RegisterRecordContextFree
2445 //===========================================================================================================================
2446
2447 static void RegisterRecordContextFree( RegisterRecordContext *inContext )
2448 {
2449 DNSServiceForget( &inContext->conRef );
2450 ForgetMem( &inContext->dataPtr );
2451 ForgetMem( &inContext->updateDataPtr );
2452 free( inContext );
2453 }
2454
2455 //===========================================================================================================================
2456 // RegisterRecordCallback
2457 //===========================================================================================================================
2458
2459 static void
2460 RegisterRecordCallback(
2461 DNSServiceRef inSDRef,
2462 DNSRecordRef inRecordRef,
2463 DNSServiceFlags inFlags,
2464 DNSServiceErrorType inError,
2465 void * inContext )
2466 {
2467 RegisterRecordContext * context = (RegisterRecordContext *) inContext;
2468 char time[ kTimestampBufLen ];
2469
2470 Unused( inSDRef );
2471 Unused( inRecordRef );
2472 Unused( inFlags );
2473 Unused( context );
2474
2475 GetTimestampStr( time );
2476 FPrintF( stdout, "%s Record registration result (error %#m)\n", time, inError );
2477
2478 if( !context->didRegister && !inError )
2479 {
2480 context->didRegister = true;
2481 if( context->updateDataPtr )
2482 {
2483 if( context->updateDelayMs > 0 )
2484 {
2485 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
2486 context, RegisterRecordUpdate );
2487 }
2488 else
2489 {
2490 RegisterRecordUpdate( context );
2491 }
2492 }
2493 if( context->lifetimeMs == 0 )
2494 {
2495 Exit( kExitReason_TimeLimit );
2496 }
2497 else if( context->lifetimeMs > 0 )
2498 {
2499 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
2500 kExitReason_TimeLimit, Exit );
2501 }
2502 }
2503 }
2504
2505 //===========================================================================================================================
2506 // RegisterRecordUpdate
2507 //===========================================================================================================================
2508
2509 static void RegisterRecordUpdate( void *inContext )
2510 {
2511 OSStatus err;
2512 RegisterRecordContext * const context = (RegisterRecordContext *) inContext;
2513
2514 err = DNSServiceUpdateRecord( context->conRef, context->recordRef, 0, (uint16_t) context->updateDataLen,
2515 context->updateDataPtr, context->updateTTL );
2516 require_noerr( err, exit );
2517
2518 exit:
2519 if( err ) exit( 1 );
2520 }
2521
2522 //===========================================================================================================================
2523 // ResolveCmd
2524 //===========================================================================================================================
2525
2526 typedef struct
2527 {
2528 DNSServiceRef mainRef; // Main sdRef for shared connections.
2529 DNSServiceRef opRef; // sdRef for the DNSServiceResolve operation.
2530 DNSServiceFlags flags; // Flags argument for DNSServiceResolve().
2531 const char * name; // Service name argument for DNSServiceResolve().
2532 const char * type; // Service type argument for DNSServiceResolve().
2533 const char * domain; // Domain argument for DNSServiceResolve().
2534 uint32_t ifIndex; // Interface index argument for DNSServiceResolve().
2535 int timeLimitSecs; // Time limit for the DNSServiceResolve operation in seconds.
2536
2537 } ResolveContext;
2538
2539 static void ResolvePrintPrologue( const ResolveContext *inContext );
2540 static void ResolveContextFree( ResolveContext *inContext );
2541 static void DNSSD_API
2542 ResolveCallback(
2543 DNSServiceRef inSDRef,
2544 DNSServiceFlags inFlags,
2545 uint32_t inInterfaceIndex,
2546 DNSServiceErrorType inError,
2547 const char * inFullName,
2548 const char * inHostname,
2549 uint16_t inPort,
2550 uint16_t inTXTLen,
2551 const unsigned char * inTXTPtr,
2552 void * inContext );
2553
2554 static void ResolveCmd( void )
2555 {
2556 OSStatus err;
2557 DNSServiceRef sdRef;
2558 ResolveContext * context = NULL;
2559 dispatch_source_t signalSource = NULL;
2560 int useMainConnection;
2561
2562 // Set up SIGINT handler.
2563
2564 signal( SIGINT, SIG_IGN );
2565 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
2566 require_noerr( err, exit );
2567 dispatch_resume( signalSource );
2568
2569 // Create context.
2570
2571 context = (ResolveContext *) calloc( 1, sizeof( *context ) );
2572 require_action( context, exit, err = kNoMemoryErr );
2573
2574 // Check command parameters.
2575
2576 if( gResolve_TimeLimitSecs < 0 )
2577 {
2578 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs );
2579 err = kParamErr;
2580 goto exit;
2581 }
2582
2583 // Create main connection.
2584
2585 if( gConnectionOpt )
2586 {
2587 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
2588 require_noerr_quiet( err, exit );
2589 useMainConnection = true;
2590 }
2591 else
2592 {
2593 useMainConnection = false;
2594 }
2595
2596 // Get flags.
2597
2598 context->flags = GetDNSSDFlagsFromOpts();
2599 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
2600
2601 // Get interface index.
2602
2603 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
2604 require_noerr_quiet( err, exit );
2605
2606 // Set remaining parameters.
2607
2608 context->name = gResolve_Name;
2609 context->type = gResolve_Type;
2610 context->domain = gResolve_Domain;
2611 context->timeLimitSecs = gResolve_TimeLimitSecs;
2612
2613 // Print prologue.
2614
2615 ResolvePrintPrologue( context );
2616
2617 // Start operation.
2618
2619 if( useMainConnection ) sdRef = context->mainRef;
2620 err = DNSServiceResolve( &sdRef, context->flags, context->ifIndex, context->name, context->type, context->domain,
2621 ResolveCallback, NULL );
2622 require_noerr( err, exit );
2623
2624 context->opRef = sdRef;
2625 if( !useMainConnection )
2626 {
2627 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
2628 require_noerr( err, exit );
2629 }
2630
2631 // Set time limit.
2632
2633 if( context->timeLimitSecs > 0 )
2634 {
2635 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
2636 kExitReason_TimeLimit, Exit );
2637 }
2638 dispatch_main();
2639
2640 exit:
2641 dispatch_source_forget( &signalSource );
2642 if( context ) ResolveContextFree( context );
2643 if( err ) exit( 1 );
2644 }
2645
2646 //===========================================================================================================================
2647 // ReconfirmCmd
2648 //===========================================================================================================================
2649
2650 static void ReconfirmCmd( void )
2651 {
2652 OSStatus err;
2653 uint8_t * rdataPtr = NULL;
2654 size_t rdataLen = 0;
2655 DNSServiceFlags flags;
2656 uint32_t ifIndex;
2657 uint16_t type, class;
2658 char ifName[ kInterfaceNameBufLen ];
2659
2660 // Get flags.
2661
2662 flags = GetDNSSDFlagsFromOpts();
2663
2664 // Get interface index.
2665
2666 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
2667 require_noerr_quiet( err, exit );
2668
2669 // Get record type.
2670
2671 err = RecordTypeFromArgString( gReconfirmRecord_Type, &type );
2672 require_noerr( err, exit );
2673
2674 // Get record data.
2675
2676 if( gReconfirmRecord_Data )
2677 {
2678 err = RecordDataFromArgString( gReconfirmRecord_Data, &rdataPtr, &rdataLen );
2679 require_noerr_quiet( err, exit );
2680 }
2681
2682 // Get record data.
2683
2684 if( gReconfirmRecord_Class )
2685 {
2686 err = RecordClassFromArgString( gReconfirmRecord_Class, &class );
2687 require_noerr( err, exit );
2688 }
2689 else
2690 {
2691 class = kDNSServiceClass_IN;
2692 }
2693
2694 // Print prologue.
2695
2696 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
2697 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
2698 FPrintF( stdout, "Name: %s\n", gReconfirmRecord_Name );
2699 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
2700 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
2701 FPrintF( stdout, "Data: %#H\n", rdataPtr, (int) rdataLen, INT_MAX );
2702 FPrintF( stdout, "---\n" );
2703
2704 err = DNSServiceReconfirmRecord( flags, ifIndex, gReconfirmRecord_Name, type, class, (uint16_t) rdataLen, rdataPtr );
2705 FPrintF( stdout, "Error: %#m\n", err );
2706
2707 exit:
2708 FreeNullSafe( rdataPtr );
2709 if( err ) exit( 1 );
2710 }
2711
2712 //===========================================================================================================================
2713 // ResolvePrintPrologue
2714 //===========================================================================================================================
2715
2716 static void ResolvePrintPrologue( const ResolveContext *inContext )
2717 {
2718 const int timeLimitSecs = inContext->timeLimitSecs;
2719 char ifName[ kInterfaceNameBufLen ];
2720 char time[ kTimestampBufLen ];
2721
2722 InterfaceIndexToName( inContext->ifIndex, ifName );
2723
2724 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
2725 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
2726 FPrintF( stdout, "Name: %s\n", inContext->name );
2727 FPrintF( stdout, "Type: %s\n", inContext->type );
2728 FPrintF( stdout, "Domain: %s\n", inContext->domain );
2729 FPrintF( stdout, "Time limit: " );
2730 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
2731 else FPrintF( stdout, "∞\n" );
2732 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
2733 FPrintF( stdout, "---\n" );
2734 }
2735
2736 //===========================================================================================================================
2737 // ResolveContextFree
2738 //===========================================================================================================================
2739
2740 static void ResolveContextFree( ResolveContext *inContext )
2741 {
2742 DNSServiceForget( &inContext->opRef );
2743 DNSServiceForget( &inContext->mainRef );
2744 free( inContext );
2745 }
2746
2747 //===========================================================================================================================
2748 // ResolveCallback
2749 //===========================================================================================================================
2750
2751 static void DNSSD_API
2752 ResolveCallback(
2753 DNSServiceRef inSDRef,
2754 DNSServiceFlags inFlags,
2755 uint32_t inInterfaceIndex,
2756 DNSServiceErrorType inError,
2757 const char * inFullName,
2758 const char * inHostname,
2759 uint16_t inPort,
2760 uint16_t inTXTLen,
2761 const unsigned char * inTXTPtr,
2762 void * inContext )
2763 {
2764 char time[ kTimestampBufLen ];
2765 char errorStr[ 64 ];
2766
2767 Unused( inSDRef );
2768 Unused( inFlags );
2769 Unused( inContext );
2770
2771 GetTimestampStr( time );
2772
2773 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
2774
2775 FPrintF( stdout, "%s: %s can be reached at %s:%u (interface %d)%?s\n",
2776 time, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
2777 if( inTXTLen == 1 )
2778 {
2779 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
2780 }
2781 else
2782 {
2783 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
2784 }
2785 }
2786
2787 //===========================================================================================================================
2788 // GetAddrInfoPOSIXCmd
2789 //===========================================================================================================================
2790
2791 #define AddressFamilyStr( X ) ( \
2792 ( (X) == AF_INET ) ? "inet" : \
2793 ( (X) == AF_INET6 ) ? "inet6" : \
2794 ( (X) == AF_UNSPEC ) ? "unspec" : \
2795 "???" )
2796
2797 static void GetAddrInfoPOSIXCmd( void )
2798 {
2799 OSStatus err;
2800 struct addrinfo hints;
2801 const struct addrinfo * addrInfo;
2802 struct addrinfo * addrInfoList = NULL;
2803 char time[ kTimestampBufLen ];
2804
2805 memset( &hints, 0, sizeof( hints ) );
2806 hints.ai_socktype = SOCK_STREAM;
2807
2808 // Set hints address family.
2809
2810 if( !gGAIPOSIX_Family ) hints.ai_family = AF_UNSPEC;
2811 else if( strcasecmp( gGAIPOSIX_Family, "inet" ) == 0 ) hints.ai_family = AF_INET;
2812 else if( strcasecmp( gGAIPOSIX_Family, "inet6" ) == 0 ) hints.ai_family = AF_INET6;
2813 else if( strcasecmp( gGAIPOSIX_Family, "unspec" ) == 0 ) hints.ai_family = AF_UNSPEC;
2814 else
2815 {
2816 FPrintF( stderr, "Invalid address family: %s.\n", gGAIPOSIX_Family );
2817 err = kParamErr;
2818 goto exit;
2819 }
2820
2821 // Set hints flags.
2822
2823 if( gGAIPOSIXFlag_AddrConfig ) hints.ai_flags |= AI_ADDRCONFIG;
2824 if( gGAIPOSIXFlag_All ) hints.ai_flags |= AI_ALL;
2825 if( gGAIPOSIXFlag_CanonName ) hints.ai_flags |= AI_CANONNAME;
2826 if( gGAIPOSIXFlag_NumericHost ) hints.ai_flags |= AI_NUMERICHOST;
2827 if( gGAIPOSIXFlag_NumericServ ) hints.ai_flags |= AI_NUMERICSERV;
2828 if( gGAIPOSIXFlag_Passive ) hints.ai_flags |= AI_PASSIVE;
2829 if( gGAIPOSIXFlag_V4Mapped ) hints.ai_flags |= AI_V4MAPPED;
2830 #if( defined( AI_V4MAPPED_CFG ) )
2831 if( gGAIPOSIXFlag_V4MappedCFG ) hints.ai_flags |= AI_V4MAPPED_CFG;
2832 #endif
2833 #if( defined( AI_DEFAULT ) )
2834 if( gGAIPOSIXFlag_Default ) hints.ai_flags |= AI_DEFAULT;
2835 #endif
2836
2837 // Print prologue.
2838
2839 FPrintF( stdout, "Hostname: %s\n", gGAIPOSIX_HostName );
2840 FPrintF( stdout, "Servname: %s\n", gGAIPOSIX_ServName );
2841 FPrintF( stdout, "Address family: %s\n", AddressFamilyStr( hints.ai_family ) );
2842 FPrintF( stdout, "Flags: 0x%X < ", hints.ai_flags );
2843 if( hints.ai_flags & AI_NUMERICSERV ) FPrintF( stdout, "AI_NUMERICSERV " );
2844 if( hints.ai_flags & AI_V4MAPPED ) FPrintF( stdout, "AI_V4MAPPED " );
2845 if( hints.ai_flags & AI_ADDRCONFIG ) FPrintF( stdout, "AI_ADDRCONFIG " );
2846 #if( defined( AI_V4MAPPED_CFG ) )
2847 if( hints.ai_flags & AI_V4MAPPED_CFG ) FPrintF( stdout, "AI_V4MAPPED_CFG " );
2848 #endif
2849 if( hints.ai_flags & AI_ALL ) FPrintF( stdout, "AI_ALL " );
2850 if( hints.ai_flags & AI_NUMERICHOST ) FPrintF( stdout, "AI_NUMERICHOST " );
2851 if( hints.ai_flags & AI_CANONNAME ) FPrintF( stdout, "AI_CANONNAME " );
2852 if( hints.ai_flags & AI_PASSIVE ) FPrintF( stdout, "AI_PASSIVE " );
2853 FPrintF( stdout, ">\n" );
2854 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
2855 FPrintF( stdout, "---\n" );
2856
2857 // Call getaddrinfo().
2858
2859 err = getaddrinfo( gGAIPOSIX_HostName, gGAIPOSIX_ServName, &hints, &addrInfoList );
2860 GetTimestampStr( time );
2861 if( err )
2862 {
2863 FPrintF( stderr, "Error %#m: %s.\n", err, gai_strerror( err ) );
2864 }
2865 else
2866 {
2867 int addrCount = 0;
2868
2869 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next ) { ++addrCount; }
2870
2871 FPrintF( stdout, "Addresses (%d total):\n", addrCount );
2872 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next )
2873 {
2874 FPrintF( stdout, "%##a\n", addrInfo->ai_addr );
2875 }
2876 }
2877 FPrintF( stdout, "---\n" );
2878 FPrintF( stdout, "End time: %s\n", time );
2879
2880 exit:
2881 if( addrInfoList ) freeaddrinfo( addrInfoList );
2882 if( err ) exit( 1 );
2883 }
2884
2885 //===========================================================================================================================
2886 // ReverseLookupCmd
2887 //===========================================================================================================================
2888
2889 static void ReverseLookupCmd( void )
2890 {
2891 OSStatus err;
2892 QueryRecordContext * context = NULL;
2893 DNSServiceRef sdRef;
2894 dispatch_source_t signalSource = NULL;
2895 uint32_t ipv4Addr;
2896 uint8_t ipv6Addr[ 16 ];
2897 char recordName[ ( 16 * 4 ) + 9 + 1 ];
2898 int useMainConnection;
2899
2900 // Set up SIGINT handler.
2901
2902 signal( SIGINT, SIG_IGN );
2903 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
2904 require_noerr( err, exit );
2905 dispatch_resume( signalSource );
2906
2907 // Create context.
2908
2909 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
2910 require_action( context, exit, err = kNoMemoryErr );
2911
2912 // Check command parameters.
2913
2914 if( gReverseLookup_TimeLimitSecs < 0 )
2915 {
2916 FPrintF( stderr, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs );
2917 err = kParamErr;
2918 goto exit;
2919 }
2920
2921 // Create main connection.
2922
2923 if( gConnectionOpt )
2924 {
2925 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
2926 require_noerr_quiet( err, exit );
2927 useMainConnection = true;
2928 }
2929 else
2930 {
2931 useMainConnection = false;
2932 }
2933
2934 // Get flags.
2935
2936 context->flags = GetDNSSDFlagsFromOpts();
2937 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
2938
2939 // Get interface index.
2940
2941 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
2942 require_noerr_quiet( err, exit );
2943
2944 // Create reverse lookup record name.
2945
2946 err = StringToIPv4Address( gReverseLookup_IPAddr, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix,
2947 &ipv4Addr, NULL, NULL, NULL, NULL );
2948 if( err )
2949 {
2950 char * dst;
2951 int i;
2952
2953 err = StringToIPv6Address( gReverseLookup_IPAddr,
2954 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
2955 ipv6Addr, NULL, NULL, NULL, NULL );
2956 if( err )
2957 {
2958 FPrintF( stderr, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr );
2959 err = kParamErr;
2960 goto exit;
2961 }
2962 dst = recordName;
2963 for( i = 15; i >= 0; --i )
2964 {
2965 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] & 0x0F ];
2966 *dst++ = '.';
2967 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] >> 4 ];
2968 *dst++ = '.';
2969 }
2970 strcpy( dst, "ip6.arpa." );
2971 check( ( strlen( recordName ) + 1 ) <= sizeof( recordName ) );
2972 }
2973 else
2974 {
2975 SNPrintF( recordName, sizeof( recordName ), "%u.%u.%u.%u.in-addr.arpa.",
2976 ipv4Addr & 0xFF,
2977 ( ipv4Addr >> 8 ) & 0xFF,
2978 ( ipv4Addr >> 16 ) & 0xFF,
2979 ( ipv4Addr >> 24 ) & 0xFF );
2980 }
2981
2982 // Set remaining parameters.
2983
2984 context->recordName = recordName;
2985 context->recordType = kDNSServiceType_PTR;
2986 context->timeLimitSecs = gReverseLookup_TimeLimitSecs;
2987 context->oneShotMode = gReverseLookup_OneShot ? true : false;
2988
2989 // Print prologue.
2990
2991 QueryRecordPrintPrologue( context );
2992
2993 // Start operation.
2994
2995 if( useMainConnection ) sdRef = context->mainRef;
2996 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
2997 kDNSServiceClass_IN, QueryRecordCallback, context );
2998 require_noerr( err, exit );
2999
3000 context->opRef = sdRef;
3001 if( !useMainConnection )
3002 {
3003 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3004 require_noerr( err, exit );
3005 }
3006
3007 // Set time limit.
3008
3009 if( context->timeLimitSecs > 0 )
3010 {
3011 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
3012 kExitReason_TimeLimit, Exit );
3013 }
3014 dispatch_main();
3015
3016 exit:
3017 dispatch_source_forget( &signalSource );
3018 if( context ) QueryRecordContextFree( context );
3019 if( err ) exit( 1 );
3020 }
3021
3022 //===========================================================================================================================
3023 // BrowseAllCmd
3024 //===========================================================================================================================
3025
3026 typedef struct BrowseDomain BrowseDomain;
3027 typedef struct BrowseType BrowseType;
3028 typedef struct BrowseOp BrowseOp;
3029 typedef struct BrowseInstance BrowseInstance;
3030 typedef struct BrowseIPAddr BrowseIPAddr;
3031
3032 typedef struct
3033 {
3034 int refCount;
3035 DNSServiceRef mainRef;
3036 DNSServiceRef domainsQuery;
3037 const char * domain;
3038 BrowseDomain * domainList;
3039 char ** serviceTypes;
3040 size_t serviceTypesCount;
3041 dispatch_source_t exitTimer;
3042 uint32_t ifIndex;
3043 int pendingConnectCount;
3044 int browseTimeSecs;
3045 int connectTimeLimitSecs;
3046 Boolean includeAWDL;
3047 Boolean useColoredText;
3048
3049 } BrowseAllContext;
3050
3051 struct BrowseDomain
3052 {
3053 BrowseDomain * next;
3054 char * name;
3055 DNSServiceRef servicesQuery;
3056 BrowseAllContext * context;
3057 BrowseType * typeList;
3058 };
3059
3060 struct BrowseType
3061 {
3062 BrowseType * next;
3063 char * name;
3064 BrowseOp * browseList;
3065 };
3066
3067 struct BrowseOp
3068 {
3069 BrowseOp * next;
3070 BrowseAllContext * context;
3071 DNSServiceRef browse;
3072 uint64_t startTicks;
3073 BrowseInstance * instanceList;
3074 uint32_t ifIndex;
3075 Boolean isTCP;
3076 };
3077
3078 struct BrowseInstance
3079 {
3080 BrowseInstance * next;
3081 BrowseAllContext * context;
3082 char * name;
3083 uint64_t foundTicks;
3084 DNSServiceRef resolve;
3085 uint64_t resolveStartTicks;
3086 uint64_t resolveDoneTicks;
3087 DNSServiceRef getAddr;
3088 uint64_t getAddrStartTicks;
3089 BrowseIPAddr * addrList;
3090 uint8_t * txtPtr;
3091 size_t txtLen;
3092 char * hostname;
3093 uint32_t ifIndex;
3094 uint16_t port;
3095 Boolean isTCP;
3096 };
3097
3098 typedef enum
3099 {
3100 kConnectStatus_None = 0,
3101 kConnectStatus_Pending = 1,
3102 kConnectStatus_Succeeded = 2,
3103 kConnectStatus_Failed = 3
3104
3105 } ConnectStatus;
3106
3107 struct BrowseIPAddr
3108 {
3109 BrowseIPAddr * next;
3110 sockaddr_ip sip;
3111 int refCount;
3112 BrowseAllContext * context;
3113 uint64_t foundTicks;
3114 AsyncConnectionRef connection;
3115 ConnectStatus connectStatus;
3116 CFTimeInterval connectTimeSecs;
3117 OSStatus connectError;
3118 };
3119
3120 static void BrowseAllPrintPrologue( const BrowseAllContext *inContext );
3121 static void DNSSD_API
3122 BrowseAllQueryDomainsCallback(
3123 DNSServiceRef inSDRef,
3124 DNSServiceFlags inFlags,
3125 uint32_t inInterfaceIndex,
3126 DNSServiceErrorType inError,
3127 const char * inFullName,
3128 uint16_t inType,
3129 uint16_t inClass,
3130 uint16_t inRDataLen,
3131 const void * inRDataPtr,
3132 uint32_t inTTL,
3133 void * inContext );
3134 static void DNSSD_API
3135 BrowseAllQueryCallback(
3136 DNSServiceRef inSDRef,
3137 DNSServiceFlags inFlags,
3138 uint32_t inInterfaceIndex,
3139 DNSServiceErrorType inError,
3140 const char * inFullName,
3141 uint16_t inType,
3142 uint16_t inClass,
3143 uint16_t inRDataLen,
3144 const void * inRDataPtr,
3145 uint32_t inTTL,
3146 void * inContext );
3147 static void DNSSD_API
3148 BrowseAllBrowseCallback(
3149 DNSServiceRef inSDRef,
3150 DNSServiceFlags inFlags,
3151 uint32_t inInterfaceIndex,
3152 DNSServiceErrorType inError,
3153 const char * inName,
3154 const char * inRegType,
3155 const char * inDomain,
3156 void * inContext );
3157 static void DNSSD_API
3158 BrowseAllResolveCallback(
3159 DNSServiceRef inSDRef,
3160 DNSServiceFlags inFlags,
3161 uint32_t inInterfaceIndex,
3162 DNSServiceErrorType inError,
3163 const char * inFullName,
3164 const char * inHostname,
3165 uint16_t inPort,
3166 uint16_t inTXTLen,
3167 const unsigned char * inTXTPtr,
3168 void * inContext );
3169 static void DNSSD_API
3170 BrowseAllGAICallback(
3171 DNSServiceRef inSDRef,
3172 DNSServiceFlags inFlags,
3173 uint32_t inInterfaceIndex,
3174 DNSServiceErrorType inError,
3175 const char * inHostname,
3176 const struct sockaddr * inSockAddr,
3177 uint32_t inTTL,
3178 void * inContext );
3179 static void BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg );
3180 static void BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg );
3181 static void BrowseAllStop( void *inContext );
3182 static void BrowseAllExit( void *inContext );
3183 static OSStatus BrowseAllAddDomain( BrowseAllContext *inContext, const char *inName );
3184 static OSStatus BrowseAllRemoveDomain( BrowseAllContext *inContext, const char *inName );
3185 static void BrowseAllContextRelease( BrowseAllContext *inContext );
3186 static OSStatus
3187 BrowseAllAddServiceType(
3188 BrowseAllContext * inContext,
3189 BrowseDomain * inDomain,
3190 const char * inName,
3191 uint32_t inIfIndex,
3192 Boolean inIncludeAWDL );
3193 static OSStatus
3194 BrowseAllRemoveServiceType(
3195 BrowseAllContext * inContext,
3196 BrowseDomain * inDomain,
3197 const char * inName,
3198 uint32_t inIfIndex );
3199 static OSStatus
3200 BrowseAllAddServiceInstance(
3201 BrowseAllContext * inContext,
3202 BrowseOp * inBrowse,
3203 const char * inName,
3204 const char * inRegType,
3205 const char * inDomain,
3206 uint32_t inIfIndex );
3207 static OSStatus
3208 BrowseAllRemoveServiceInstance(
3209 BrowseAllContext * inContext,
3210 BrowseOp * inBrowse,
3211 const char * inName,
3212 uint32_t inIfIndex );
3213 static OSStatus
3214 BrowseAllAddIPAddress(
3215 BrowseAllContext * inContext,
3216 BrowseInstance * inInstance,
3217 const struct sockaddr * inSockAddr );
3218 static OSStatus
3219 BrowseAllRemoveIPAddress(
3220 BrowseAllContext * inContext,
3221 BrowseInstance * inInstance,
3222 const struct sockaddr * inSockAddr );
3223 static void BrowseDomainFree( BrowseDomain *inDomain );
3224 static void BrowseTypeFree( BrowseType *inType );
3225 static void BrowseOpFree( BrowseOp *inBrowse );
3226 static void BrowseInstanceFree( BrowseInstance *inInstance );
3227 static void BrowseIPAddrRelease( BrowseIPAddr *inAddr );
3228 static void BrowseIPAddrReleaseList( BrowseIPAddr *inList );
3229
3230 #define ForgetIPAddressList( X ) ForgetCustom( X, BrowseIPAddrReleaseList )
3231 #define ForgetBrowseAllContext( X ) ForgetCustom( X, BrowseAllContextRelease )
3232
3233 #define kBrowseAllOpenFileMin 4096
3234
3235 static void BrowseAllCmd( void )
3236 {
3237 OSStatus err;
3238 BrowseAllContext * context = NULL;
3239
3240 // Check command parameters.
3241
3242 if( gBrowseAll_BrowseTimeSecs <= 0 )
3243 {
3244 FPrintF( stdout, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs );
3245 err = kParamErr;
3246 goto exit;
3247 }
3248
3249 #if( TARGET_OS_POSIX )
3250 // Set open file minimum.
3251
3252 {
3253 struct rlimit fdLimits;
3254
3255 err = getrlimit( RLIMIT_NOFILE, &fdLimits );
3256 err = map_global_noerr_errno( err );
3257 require_noerr( err, exit );
3258
3259 if( fdLimits.rlim_cur < kBrowseAllOpenFileMin )
3260 {
3261 fdLimits.rlim_cur = kBrowseAllOpenFileMin;
3262 err = setrlimit( RLIMIT_NOFILE, &fdLimits );
3263 err = map_global_noerr_errno( err );
3264 require_noerr( err, exit );
3265 }
3266 }
3267 #endif
3268
3269 context = (BrowseAllContext *) calloc( 1, sizeof( *context ) );
3270 require_action( context, exit, err = kNoMemoryErr );
3271
3272 context->refCount = 1;
3273 context->domain = gBrowseAll_Domain;
3274 context->serviceTypes = gBrowseAll_ServiceTypes;
3275 context->serviceTypesCount = gBrowseAll_ServiceTypesCount;
3276 gBrowseAll_ServiceTypes = NULL;
3277 gBrowseAll_ServiceTypesCount = 0;
3278 context->browseTimeSecs = gBrowseAll_BrowseTimeSecs;
3279 context->connectTimeLimitSecs = gBrowseAll_ConnectTimeLimitSecs;
3280 context->includeAWDL = gBrowseAll_IncludeAWDL ? true : false;
3281 #if( TARGET_OS_POSIX )
3282 context->useColoredText = isatty( STDOUT_FILENO ) ? true : false;
3283 #endif
3284
3285 err = DNSServiceCreateConnection( &context->mainRef );
3286 require_noerr( err, exit );
3287
3288 err = DNSServiceSetDispatchQueue( context->mainRef, dispatch_get_main_queue() );
3289 require_noerr( err, exit );
3290
3291 // Set interface index.
3292
3293 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3294 require_noerr_quiet( err, exit );
3295
3296 BrowseAllPrintPrologue( context );
3297
3298 if( context->domain )
3299 {
3300 err = BrowseAllAddDomain( context, context->domain );
3301 require_noerr( err, exit );
3302 }
3303 else
3304 {
3305 DNSServiceRef sdRef;
3306
3307 sdRef = context->mainRef;
3308 err = DNSServiceQueryRecord( &sdRef, kDNSServiceFlagsShareConnection, kDNSServiceInterfaceIndexLocalOnly,
3309 "b._dns-sd._udp.local.", kDNSServiceType_PTR, kDNSServiceClass_IN, BrowseAllQueryDomainsCallback, context );
3310 require_noerr( err, exit );
3311
3312 context->domainsQuery = sdRef;
3313 }
3314
3315 dispatch_after_f( dispatch_time_seconds( context->browseTimeSecs ), dispatch_get_main_queue(), context, BrowseAllStop );
3316 dispatch_main();
3317
3318 exit:
3319 if( context ) BrowseAllContextRelease( context );
3320 if( err ) exit( 1 );
3321 }
3322
3323 //===========================================================================================================================
3324 // BrowseAllPrintPrologue
3325 //===========================================================================================================================
3326
3327 static void BrowseAllPrintPrologue( const BrowseAllContext *inContext )
3328 {
3329 size_t i;
3330 char ifName[ kInterfaceNameBufLen ];
3331 char time[ kTimestampBufLen ];
3332
3333 InterfaceIndexToName( inContext->ifIndex, ifName );
3334
3335 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3336 FPrintF( stdout, "Service types: ");
3337 if( inContext->serviceTypesCount > 0 )
3338 {
3339 FPrintF( stdout, "%s", inContext->serviceTypes[ 0 ] );
3340 for( i = 1; i < inContext->serviceTypesCount; ++i ) FPrintF( stdout, ", %s", inContext->serviceTypes[ i ] );
3341 FPrintF( stdout, "\n" );
3342 }
3343 else
3344 {
3345 FPrintF( stdout, "all services\n" );
3346 }
3347 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "default domains" );
3348 FPrintF( stdout, "Browse time: %d second%?c\n", inContext->browseTimeSecs, inContext->browseTimeSecs != 1, 's' );
3349 FPrintF( stdout, "Connect time limit: %d second%?c\n",
3350 inContext->connectTimeLimitSecs, inContext->connectTimeLimitSecs != 1, 's' );
3351 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
3352 FPrintF( stdout, "---\n" );
3353 }
3354
3355 //===========================================================================================================================
3356 // BrowseAllQueryDomainsCallback
3357 //===========================================================================================================================
3358
3359 static void DNSSD_API
3360 BrowseAllQueryDomainsCallback(
3361 DNSServiceRef inSDRef,
3362 DNSServiceFlags inFlags,
3363 uint32_t inInterfaceIndex,
3364 DNSServiceErrorType inError,
3365 const char * inFullName,
3366 uint16_t inType,
3367 uint16_t inClass,
3368 uint16_t inRDataLen,
3369 const void * inRDataPtr,
3370 uint32_t inTTL,
3371 void * inContext )
3372 {
3373 OSStatus err;
3374 BrowseAllContext * const context = (BrowseAllContext *) inContext;
3375 char domainStr[ kDNSServiceMaxDomainName ];
3376
3377 Unused( inSDRef );
3378 Unused( inInterfaceIndex );
3379 Unused( inFullName );
3380 Unused( inType );
3381 Unused( inClass );
3382 Unused( inTTL );
3383
3384 err = inError;
3385 require_noerr( err, exit );
3386
3387 err = DomainNameToString( inRDataPtr, ( (const uint8_t *) inRDataPtr ) + inRDataLen, domainStr, NULL );
3388 require_noerr( err, exit );
3389
3390 if( inFlags & kDNSServiceFlagsAdd )
3391 {
3392 err = BrowseAllAddDomain( context, domainStr );
3393 if( err == kDuplicateErr ) err = kNoErr;
3394 require_noerr( err, exit );
3395 }
3396 else
3397 {
3398 err = BrowseAllRemoveDomain( context, domainStr );
3399 if( err == kNotFoundErr ) err = kNoErr;
3400 require_noerr( err, exit );
3401 }
3402
3403 exit:
3404 if( err ) exit( 1 );
3405 }
3406
3407 //===========================================================================================================================
3408 // BrowseAllQueryCallback
3409 //===========================================================================================================================
3410
3411 static void DNSSD_API
3412 BrowseAllQueryCallback(
3413 DNSServiceRef inSDRef,
3414 DNSServiceFlags inFlags,
3415 uint32_t inInterfaceIndex,
3416 DNSServiceErrorType inError,
3417 const char * inFullName,
3418 uint16_t inType,
3419 uint16_t inClass,
3420 uint16_t inRDataLen,
3421 const void * inRDataPtr,
3422 uint32_t inTTL,
3423 void * inContext )
3424 {
3425 OSStatus err;
3426 BrowseDomain * const domain = (BrowseDomain *) inContext;
3427 const uint8_t * firstLabel;
3428 const uint8_t * secondLabel;
3429 char * serviceTypeStr = NULL;
3430 const uint8_t * const end = ( (uint8_t * ) inRDataPtr ) + inRDataLen;
3431
3432 Unused( inSDRef );
3433 Unused( inFullName );
3434 Unused( inTTL );
3435 Unused( inType );
3436 Unused( inClass );
3437
3438 err = inError;
3439 require_noerr( err, exit );
3440
3441 check( inType == kDNSServiceType_PTR );
3442 check( inClass == kDNSServiceClass_IN );
3443 require_action( inRDataLen > 0, exit, err = kSizeErr );
3444
3445 firstLabel = inRDataPtr;
3446 require_action_quiet( ( firstLabel + 1 + firstLabel[ 0 ] ) < end , exit, err = kUnderrunErr );
3447
3448 secondLabel = firstLabel + 1 + firstLabel[ 0 ];
3449 require_action_quiet( ( secondLabel + 1 + secondLabel[ 0 ] ) < end , exit, err = kUnderrunErr );
3450
3451 ASPrintF( &serviceTypeStr, "%#s.%#s", firstLabel, secondLabel );
3452 require_action( serviceTypeStr, exit, err = kNoMemoryErr );
3453
3454 if( inFlags & kDNSServiceFlagsAdd )
3455 {
3456 err = BrowseAllAddServiceType( domain->context, domain, serviceTypeStr, inInterfaceIndex, false );
3457 if( err == kDuplicateErr ) err = kNoErr;
3458 require_noerr( err, exit );
3459 }
3460 else
3461 {
3462 err = BrowseAllRemoveServiceType( domain->context, domain, serviceTypeStr, inInterfaceIndex );
3463 if( err == kNotFoundErr ) err = kNoErr;
3464 require_noerr( err, exit );
3465 }
3466
3467 exit:
3468 FreeNullSafe( serviceTypeStr );
3469 }
3470
3471 //===========================================================================================================================
3472 // BrowseAllBrowseCallback
3473 //===========================================================================================================================
3474
3475 static void DNSSD_API
3476 BrowseAllBrowseCallback(
3477 DNSServiceRef inSDRef,
3478 DNSServiceFlags inFlags,
3479 uint32_t inInterfaceIndex,
3480 DNSServiceErrorType inError,
3481 const char * inName,
3482 const char * inRegType,
3483 const char * inDomain,
3484 void * inContext )
3485 {
3486 OSStatus err;
3487 BrowseOp * const browse = (BrowseOp *) inContext;
3488
3489 Unused( inSDRef );
3490
3491 err = inError;
3492 require_noerr( err, exit );
3493
3494 if( inFlags & kDNSServiceFlagsAdd )
3495 {
3496 err = BrowseAllAddServiceInstance( browse->context, browse, inName, inRegType, inDomain, inInterfaceIndex );
3497 if( err == kDuplicateErr ) err = kNoErr;
3498 require_noerr( err, exit );
3499 }
3500 else
3501 {
3502 err = BrowseAllRemoveServiceInstance( browse->context, browse, inName, inInterfaceIndex );
3503 if( err == kNotFoundErr ) err = kNoErr;
3504 require_noerr( err, exit );
3505 }
3506
3507 exit:
3508 return;
3509 }
3510
3511 //===========================================================================================================================
3512 // BrowseAllResolveCallback
3513 //===========================================================================================================================
3514
3515 static void DNSSD_API
3516 BrowseAllResolveCallback(
3517 DNSServiceRef inSDRef,
3518 DNSServiceFlags inFlags,
3519 uint32_t inInterfaceIndex,
3520 DNSServiceErrorType inError,
3521 const char * inFullName,
3522 const char * inHostname,
3523 uint16_t inPort,
3524 uint16_t inTXTLen,
3525 const unsigned char * inTXTPtr,
3526 void * inContext )
3527 {
3528 OSStatus err;
3529 const uint64_t nowTicks = UpTicks();
3530 BrowseInstance * const instance = (BrowseInstance *) inContext;
3531
3532 Unused( inSDRef );
3533 Unused( inFlags );
3534 Unused( inInterfaceIndex );
3535 Unused( inFullName );
3536
3537 err = inError;
3538 require_noerr( err, exit );
3539
3540 if( !MemEqual( instance->txtPtr, instance->txtLen, inTXTPtr, inTXTLen ) )
3541 {
3542 FreeNullSafe( instance->txtPtr );
3543 instance->txtPtr = malloc( inTXTLen );
3544 require_action( instance->txtPtr, exit, err = kNoMemoryErr );
3545
3546 memcpy( instance->txtPtr, inTXTPtr, inTXTLen );
3547 instance->txtLen = inTXTLen;
3548 }
3549
3550 instance->port = ntohs( inPort );
3551
3552 if( !instance->hostname || ( strcasecmp( instance->hostname, inHostname ) != 0 ) )
3553 {
3554 DNSServiceRef sdRef;
3555
3556 if( !instance->hostname ) instance->resolveDoneTicks = nowTicks;
3557 FreeNullSafe( instance->hostname );
3558 instance->hostname = strdup( inHostname );
3559 require_action( instance->hostname, exit, err = kNoMemoryErr );
3560
3561 DNSServiceForget( &instance->getAddr );
3562 ForgetIPAddressList( &instance->addrList );
3563
3564 sdRef = instance->context->mainRef;
3565 instance->getAddrStartTicks = UpTicks();
3566 err = DNSServiceGetAddrInfo( &sdRef, kDNSServiceFlagsShareConnection, instance->ifIndex,
3567 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, instance->hostname, BrowseAllGAICallback, instance );
3568 require_noerr( err, exit );
3569
3570 instance->getAddr = sdRef;
3571 }
3572
3573 exit:
3574 if( err ) exit( 1 );
3575 }
3576
3577 //===========================================================================================================================
3578 // BrowseAllGAICallback
3579 //===========================================================================================================================
3580
3581 static void DNSSD_API
3582 BrowseAllGAICallback(
3583 DNSServiceRef inSDRef,
3584 DNSServiceFlags inFlags,
3585 uint32_t inInterfaceIndex,
3586 DNSServiceErrorType inError,
3587 const char * inHostname,
3588 const struct sockaddr * inSockAddr,
3589 uint32_t inTTL,
3590 void * inContext )
3591 {
3592 OSStatus err;
3593 BrowseInstance * const instance = (BrowseInstance *) inContext;
3594
3595 Unused( inSDRef );
3596 Unused( inInterfaceIndex );
3597 Unused( inHostname );
3598 Unused( inTTL );
3599
3600 err = inError;
3601 require_noerr( err, exit );
3602
3603 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
3604 {
3605 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
3606 goto exit;
3607 }
3608
3609 if( inFlags & kDNSServiceFlagsAdd )
3610 {
3611 err = BrowseAllAddIPAddress( instance->context, instance, inSockAddr );
3612 if( err == kDuplicateErr ) err = kNoErr;
3613 require_noerr( err, exit );
3614 }
3615 else
3616 {
3617 err = BrowseAllRemoveIPAddress( instance->context, instance, inSockAddr );
3618 if( err == kNotFoundErr ) err = kNoErr;
3619 require_noerr( err, exit );
3620 }
3621
3622 exit:
3623 return;
3624 }
3625
3626 //===========================================================================================================================
3627 // BrowseAllConnectionProgress
3628 //===========================================================================================================================
3629
3630 static void BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg )
3631 {
3632 BrowseIPAddr * const addr = (BrowseIPAddr *) inArg;
3633
3634 if( inPhase == kAsyncConnectionPhase_Connected )
3635 {
3636 const AsyncConnectedInfo * const info = (AsyncConnectedInfo *) inDetails;
3637
3638 addr->connectTimeSecs = info->connectSecs;
3639 }
3640 }
3641
3642 //===========================================================================================================================
3643 // BrowseAllConnectionHandler
3644 //===========================================================================================================================
3645
3646 static void BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg )
3647 {
3648 BrowseIPAddr * const addr = (BrowseIPAddr *) inArg;
3649 BrowseAllContext * const context = addr->context;
3650
3651 if( inError )
3652 {
3653 addr->connectStatus = kConnectStatus_Failed;
3654 addr->connectError = inError;
3655 }
3656 else
3657 {
3658 addr->connectStatus = kConnectStatus_Succeeded;
3659 }
3660
3661 check( context->pendingConnectCount > 0 );
3662 if( --context->pendingConnectCount == 0 )
3663 {
3664 if( context->exitTimer )
3665 {
3666 dispatch_source_forget( &context->exitTimer );
3667 dispatch_async_f( dispatch_get_main_queue(), context, BrowseAllExit );
3668 }
3669 }
3670
3671 ForgetSocket( &inSock );
3672 BrowseIPAddrRelease( addr );
3673 }
3674
3675 //===========================================================================================================================
3676 // BrowseAllStop
3677 //===========================================================================================================================
3678
3679 static void BrowseAllStop( void *inContext )
3680 {
3681 OSStatus err;
3682 BrowseAllContext * const context = (BrowseAllContext *) inContext;
3683 BrowseDomain * domain;
3684 BrowseType * type;
3685 BrowseOp * browse;
3686 BrowseInstance * instance;
3687
3688 DNSServiceForget( &context->domainsQuery );
3689 for( domain = context->domainList; domain; domain = domain->next )
3690 {
3691 DNSServiceForget( &domain->servicesQuery );
3692 for( type = domain->typeList; type; type = type->next )
3693 {
3694 for( browse = type->browseList; browse; browse = browse->next )
3695 {
3696 DNSServiceForget( &browse->browse );
3697 for( instance = browse->instanceList; instance; instance = instance->next )
3698 {
3699 DNSServiceForget( &instance->resolve );
3700 DNSServiceForget( &instance->getAddr );
3701 }
3702 }
3703 }
3704 }
3705 DNSServiceForget( &context->mainRef );
3706
3707 if( ( context->pendingConnectCount > 0 ) && ( context->connectTimeLimitSecs > 0 ) )
3708 {
3709 check( !context->exitTimer );
3710 err = DispatchTimerCreate( dispatch_time_seconds( context->connectTimeLimitSecs ), DISPATCH_TIME_FOREVER,
3711 100 * kNanosecondsPerMillisecond, BrowseAllExit, NULL, context, &context->exitTimer );
3712 require_noerr( err, exit );
3713 dispatch_resume( context->exitTimer );
3714 }
3715 else
3716 {
3717 dispatch_async_f( dispatch_get_main_queue(), context, BrowseAllExit );
3718 }
3719 err = kNoErr;
3720
3721 exit:
3722 if( err ) exit( 1 );
3723 }
3724
3725 //===========================================================================================================================
3726 // BrowseAllExit
3727 //===========================================================================================================================
3728
3729 #define kStatusStr_CouldConnect "connected"
3730 #define kStatusStr_CouldConnectColored kANSIGreen kStatusStr_CouldConnect kANSINormal
3731 #define kStatusStr_CouldNotConnect "could not connect"
3732 #define kStatusStr_CouldNotConnectColored kANSIRed kStatusStr_CouldNotConnect kANSINormal
3733 #define kStatusStr_NoConnectionAttempted "no connection attempted"
3734 #define kStatusStr_Unknown "unknown"
3735
3736 #define Indent( X ) ( (X) * 4 ), ""
3737
3738 static void BrowseAllExit( void *inContext )
3739 {
3740 BrowseAllContext * const context = (BrowseAllContext *) inContext;
3741 BrowseDomain * domain;
3742 BrowseType * type;
3743 BrowseOp * browse;
3744 BrowseInstance * instance;
3745 BrowseIPAddr * addr;
3746
3747 dispatch_source_forget( &context->exitTimer );
3748
3749 for( domain = context->domainList; domain; domain = domain->next )
3750 {
3751 FPrintF( stdout, "%s\n\n", domain->name );
3752
3753 for( type = domain->typeList; type; type = type->next )
3754 {
3755 const char * desc;
3756
3757 desc = ServiceTypeDescription( type->name );
3758 if( desc ) FPrintF( stdout, "%*s" "%s (%s)\n\n", Indent( 1 ), desc, type->name );
3759 else FPrintF( stdout, "%*s" "%s\n\n", Indent( 1 ), type->name );
3760
3761 for( browse = type->browseList; browse; browse = browse->next )
3762 {
3763 for( instance = browse->instanceList; instance; instance = instance->next )
3764 {
3765 char ifname[ IF_NAMESIZE + 1 ];
3766
3767 FPrintF( stdout, "%*s" "%s via ", Indent( 2 ), instance->name );
3768 FPrintF( stdout, "%*s" "%s via ", Indent( 2 ), instance->name );
3769 if( instance->ifIndex == 0 )
3770 {
3771 FPrintF( stdout, "the Internet" );
3772 }
3773 else if( if_indextoname( instance->ifIndex, ifname ) )
3774 {
3775 NetTransportType netType;
3776
3777 SocketGetInterfaceInfo( kInvalidSocketRef, ifname, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3778 &netType );
3779 FPrintF( stdout, "%s (%s)",
3780 ( netType == kNetTransportType_Ethernet ) ? "ethernet" : NetTransportTypeToString( netType ),
3781 ifname );
3782 }
3783 else
3784 {
3785 FPrintF( stdout, "interface index %u", instance->ifIndex );
3786 }
3787 FPrintF( stdout, "\n\n" );
3788
3789 if( instance->hostname )
3790 {
3791 char buffer[ 256 ];
3792
3793 SNPrintF( buffer, sizeof( buffer ), "%s:%u", instance->hostname, instance->port );
3794 FPrintF( stdout, "%*s" "%-51s %4llu ms\n", Indent( 3 ), buffer,
3795 UpTicksToMilliseconds( instance->resolveDoneTicks - instance->resolveStartTicks ) );
3796 }
3797 else
3798 {
3799 FPrintF( stdout, "%*s" "%s:%u\n", Indent( 3 ), instance->hostname, instance->port );
3800 }
3801
3802 for( addr = instance->addrList; addr; addr = addr->next )
3803 {
3804 AsyncConnection_Forget( &addr->connection );
3805
3806 if( addr->connectStatus == kConnectStatus_Pending )
3807 {
3808 addr->connectStatus = kConnectStatus_Failed;
3809 addr->connectError = kTimeoutErr;
3810 }
3811
3812 FPrintF( stdout, "%*s" "%-##47a %4llu ms (", Indent( 4 ),
3813 &addr->sip.sa, UpTicksToMilliseconds( addr->foundTicks - instance->getAddrStartTicks ) );
3814 switch( addr->connectStatus )
3815 {
3816 case kConnectStatus_None:
3817 FPrintF( stdout, "%s", kStatusStr_NoConnectionAttempted );
3818 break;
3819
3820 case kConnectStatus_Succeeded:
3821 FPrintF( stdout, "%s in %.2f ms",
3822 context->useColoredText ? kStatusStr_CouldConnectColored : kStatusStr_CouldConnect,
3823 addr->connectTimeSecs * 1000 );
3824 break;
3825
3826 case kConnectStatus_Failed:
3827 FPrintF( stdout, "%s: %m",
3828 context->useColoredText ? kStatusStr_CouldNotConnectColored : kStatusStr_CouldNotConnect,
3829 addr->connectError );
3830 break;
3831
3832 default:
3833 FPrintF( stdout, "%s", kStatusStr_Unknown );
3834 break;
3835 }
3836 FPrintF( stdout, ")\n" );
3837 }
3838
3839 FPrintF( stdout, "\n" );
3840 if( instance->txtLen == 0 ) continue;
3841
3842 FPrintF( stdout, "%*s" "TXT record:\n", Indent( 3 ) );
3843 if( instance->txtLen > 1 )
3844 {
3845 FPrintF( stdout, "%3{txt}", instance->txtPtr, instance->txtLen );
3846 }
3847 else
3848 {
3849 FPrintF( stdout, "%*s" "%#H\n", Indent( 3 ), instance->txtPtr, (int) instance->txtLen, INT_MAX );
3850 }
3851 FPrintF( stdout, "\n" );
3852 }
3853 }
3854 FPrintF( stdout, "\n" );
3855 }
3856 }
3857
3858 while( ( domain = context->domainList ) != NULL )
3859 {
3860 context->domainList = domain->next;
3861 BrowseDomainFree( domain );
3862 }
3863
3864 BrowseAllContextRelease( context );
3865 Exit( NULL );
3866 }
3867
3868 //===========================================================================================================================
3869 // BrowseAllAddDomain
3870 //===========================================================================================================================
3871
3872 static OSStatus BrowseAllAddDomain( BrowseAllContext *inContext, const char *inName )
3873 {
3874 OSStatus err;
3875 BrowseDomain * domain;
3876 BrowseDomain ** p;
3877 BrowseDomain * newDomain = NULL;
3878
3879 for( p = &inContext->domainList; ( domain = *p ) != NULL; p = &domain->next )
3880 {
3881 if( strcasecmp( domain->name, inName ) == 0 ) break;
3882 }
3883 require_action_quiet( !domain, exit, err = kDuplicateErr );
3884
3885 newDomain = (BrowseDomain *) calloc( 1, sizeof( *newDomain ) );
3886 require_action( newDomain, exit, err = kNoMemoryErr );
3887
3888 ++inContext->refCount;
3889 newDomain->context = inContext;
3890
3891 newDomain->name = strdup( inName );
3892 require_action( newDomain->name, exit, err = kNoMemoryErr );
3893
3894 if( inContext->serviceTypesCount > 0 )
3895 {
3896 size_t i;
3897
3898 for( i = 0; i < inContext->serviceTypesCount; ++i )
3899 {
3900 err = BrowseAllAddServiceType( inContext, newDomain, inContext->serviceTypes[ i ], inContext->ifIndex,
3901 inContext->includeAWDL );
3902 if( err == kDuplicateErr ) err = kNoErr;
3903 require_noerr( err, exit );
3904 }
3905 }
3906 else
3907 {
3908 char * recordName;
3909 DNSServiceFlags flags;
3910 DNSServiceRef sdRef;
3911
3912 ASPrintF( &recordName, "_services._dns-sd._udp.%s", newDomain->name );
3913 require_action( recordName, exit, err = kNoMemoryErr );
3914
3915 flags = kDNSServiceFlagsShareConnection;
3916 if( inContext->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
3917
3918 sdRef = newDomain->context->mainRef;
3919 err = DNSServiceQueryRecord( &sdRef, flags, inContext->ifIndex, recordName, kDNSServiceType_PTR, kDNSServiceClass_IN,
3920 BrowseAllQueryCallback, newDomain );
3921 free( recordName );
3922 require_noerr( err, exit );
3923
3924 newDomain->servicesQuery = sdRef;
3925 }
3926
3927 *p = newDomain;
3928 newDomain = NULL;
3929 err = kNoErr;
3930
3931 exit:
3932 if( newDomain ) BrowseDomainFree( newDomain );
3933 return( err );
3934 }
3935
3936 //===========================================================================================================================
3937 // BrowseAllRemoveDomain
3938 //===========================================================================================================================
3939
3940 static OSStatus BrowseAllRemoveDomain( BrowseAllContext *inContext, const char *inName )
3941 {
3942 OSStatus err;
3943 BrowseDomain * domain;
3944 BrowseDomain ** p;
3945
3946 for( p = &inContext->domainList; ( domain = *p ) != NULL; p = &domain->next )
3947 {
3948 if( strcasecmp( domain->name, inName ) == 0 ) break;
3949 }
3950
3951 if( domain )
3952 {
3953 *p = domain->next;
3954 BrowseDomainFree( domain );
3955 err = kNoErr;
3956 }
3957 else
3958 {
3959 err = kNotFoundErr;
3960 }
3961
3962 return( err );
3963 }
3964
3965 //===========================================================================================================================
3966 // BrowseAllContextRelease
3967 //===========================================================================================================================
3968
3969 static void BrowseAllContextRelease( BrowseAllContext *inContext )
3970 {
3971 if( --inContext->refCount == 0 )
3972 {
3973 check( !inContext->domainsQuery );
3974 check( !inContext->domainList );
3975 check( !inContext->exitTimer );
3976 check( !inContext->pendingConnectCount );
3977 DNSServiceForget( &inContext->mainRef );
3978 if( inContext->serviceTypes )
3979 {
3980 StringArray_Free( inContext->serviceTypes, inContext->serviceTypesCount );
3981 inContext->serviceTypes = NULL;
3982 inContext->serviceTypesCount = 0;
3983 }
3984 free( inContext );
3985 }
3986 }
3987
3988 //===========================================================================================================================
3989 // BrowseAllAddServiceType
3990 //===========================================================================================================================
3991
3992 static OSStatus
3993 BrowseAllAddServiceType(
3994 BrowseAllContext * inContext,
3995 BrowseDomain * inDomain,
3996 const char * inName,
3997 uint32_t inIfIndex,
3998 Boolean inIncludeAWDL )
3999 {
4000 OSStatus err;
4001 DNSServiceRef sdRef;
4002 DNSServiceFlags flags;
4003 BrowseType * type;
4004 BrowseType ** typePtr;
4005 BrowseType * newType = NULL;
4006 BrowseOp * browse;
4007 BrowseOp ** browsePtr;
4008 BrowseOp * newBrowse = NULL;
4009
4010 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
4011 {
4012 if( strcasecmp( type->name, inName ) == 0 ) break;
4013 }
4014 if( !type )
4015 {
4016 newType = (BrowseType *) calloc( 1, sizeof( *newType ) );
4017 require_action( newType, exit, err = kNoMemoryErr );
4018
4019 newType->name = strdup( inName );
4020 require_action( newType->name, exit, err = kNoMemoryErr );
4021
4022 type = newType;
4023 }
4024
4025 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
4026 {
4027 if( browse->ifIndex == inIfIndex ) break;
4028 }
4029 require_action_quiet( !browse, exit, err = kDuplicateErr );
4030
4031 newBrowse = (BrowseOp *) calloc( 1, sizeof( *newBrowse ) );
4032 require_action( newBrowse, exit, err = kNoMemoryErr );
4033
4034 ++inContext->refCount;
4035 newBrowse->context = inContext;
4036 newBrowse->ifIndex = inIfIndex;
4037 if( stricmp_suffix( inName, "._tcp" ) == 0 ) newBrowse->isTCP = true;
4038
4039 flags = kDNSServiceFlagsShareConnection;
4040 if( inIncludeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
4041
4042 newBrowse->startTicks = UpTicks();
4043
4044 sdRef = inContext->mainRef;
4045 err = DNSServiceBrowse( &sdRef, flags, newBrowse->ifIndex, type->name, inDomain->name, BrowseAllBrowseCallback,
4046 newBrowse );
4047 require_noerr( err, exit );
4048
4049 newBrowse->browse = sdRef;
4050 *browsePtr = newBrowse;
4051 newBrowse = NULL;
4052
4053 if( newType )
4054 {
4055 *typePtr = newType;
4056 newType = NULL;
4057 }
4058
4059 exit:
4060 if( newBrowse ) BrowseOpFree( newBrowse );
4061 if( newType ) BrowseTypeFree( newType );
4062 return( err );
4063 }
4064
4065 //===========================================================================================================================
4066 // BrowseAllRemoveServiceType
4067 //===========================================================================================================================
4068
4069 static OSStatus
4070 BrowseAllRemoveServiceType(
4071 BrowseAllContext * inContext,
4072 BrowseDomain * inDomain,
4073 const char * inName,
4074 uint32_t inIfIndex )
4075 {
4076 OSStatus err;
4077 BrowseType * type;
4078 BrowseType ** typePtr;
4079 BrowseOp * browse;
4080 BrowseOp ** browsePtr;
4081
4082 Unused( inContext );
4083
4084 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
4085 {
4086 if( strcasecmp( type->name, inName ) == 0 ) break;
4087 }
4088 require_action_quiet( type, exit, err = kNotFoundErr );
4089
4090 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
4091 {
4092 if( browse->ifIndex == inIfIndex ) break;
4093 }
4094 require_action_quiet( browse, exit, err = kNotFoundErr );
4095
4096 *browsePtr = browse->next;
4097 BrowseOpFree( browse );
4098 if( !type->browseList )
4099 {
4100 *typePtr = type->next;
4101 BrowseTypeFree( type );
4102 }
4103 err = kNoErr;
4104
4105 exit:
4106 return( err );
4107 }
4108
4109 //===========================================================================================================================
4110 // BrowseAllAddServiceInstance
4111 //===========================================================================================================================
4112
4113 static OSStatus
4114 BrowseAllAddServiceInstance(
4115 BrowseAllContext * inContext,
4116 BrowseOp * inBrowse,
4117 const char * inName,
4118 const char * inRegType,
4119 const char * inDomain,
4120 uint32_t inIfIndex )
4121 {
4122 OSStatus err;
4123 DNSServiceRef sdRef;
4124 BrowseInstance * instance;
4125 BrowseInstance ** p;
4126 const uint64_t nowTicks = UpTicks();
4127 BrowseInstance * newInstance = NULL;
4128
4129 for( p = &inBrowse->instanceList; ( instance = *p ) != NULL; p = &instance->next )
4130 {
4131 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
4132 }
4133 require_action_quiet( !instance, exit, err = kDuplicateErr );
4134
4135 newInstance = (BrowseInstance *) calloc( 1, sizeof( *newInstance ) );
4136 require_action( newInstance, exit, err = kNoMemoryErr );
4137
4138 ++inContext->refCount;
4139 newInstance->context = inContext;
4140 newInstance->foundTicks = nowTicks;
4141 newInstance->ifIndex = inIfIndex;
4142 newInstance->isTCP = inBrowse->isTCP;
4143
4144 newInstance->name = strdup( inName );
4145 require_action( newInstance->name, exit, err = kNoMemoryErr );
4146
4147 sdRef = inContext->mainRef;
4148 newInstance->resolveStartTicks = UpTicks();
4149 err = DNSServiceResolve( &sdRef, kDNSServiceFlagsShareConnection, newInstance->ifIndex, inName, inRegType, inDomain,
4150 BrowseAllResolveCallback, newInstance );
4151 require_noerr( err, exit );
4152
4153 newInstance->resolve = sdRef;
4154 *p = newInstance;
4155 newInstance = NULL;
4156
4157 exit:
4158 if( newInstance ) BrowseInstanceFree( newInstance );
4159 return( err );
4160 }
4161
4162 //===========================================================================================================================
4163 // BrowseAllRemoveServiceInstance
4164 //===========================================================================================================================
4165
4166 static OSStatus
4167 BrowseAllRemoveServiceInstance(
4168 BrowseAllContext * inContext,
4169 BrowseOp * inBrowse,
4170 const char * inName,
4171 uint32_t inIfIndex )
4172 {
4173 OSStatus err;
4174 BrowseInstance * instance;
4175 BrowseInstance ** p;
4176
4177 Unused( inContext );
4178
4179 for( p = &inBrowse->instanceList; ( instance = *p ) != NULL; p = &instance->next )
4180 {
4181 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
4182 }
4183 require_action_quiet( instance, exit, err = kNotFoundErr );
4184
4185 *p = instance->next;
4186 BrowseInstanceFree( instance );
4187 err = kNoErr;
4188
4189 exit:
4190 return( err );
4191 }
4192
4193 //===========================================================================================================================
4194 // BrowseAllAddIPAddress
4195 //===========================================================================================================================
4196
4197 #define kDiscardProtocolPort 9
4198
4199 static OSStatus
4200 BrowseAllAddIPAddress(
4201 BrowseAllContext * inContext,
4202 BrowseInstance * inInstance,
4203 const struct sockaddr * inSockAddr )
4204 {
4205 OSStatus err;
4206 BrowseIPAddr * addr;
4207 BrowseIPAddr ** p;
4208 const uint64_t nowTicks = UpTicks();
4209 BrowseIPAddr * newAddr = NULL;
4210
4211 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
4212 {
4213 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
4214 err = kTypeErr;
4215 goto exit;
4216 }
4217
4218 for( p = &inInstance->addrList; ( addr = *p ) != NULL; p = &addr->next )
4219 {
4220 if( SockAddrCompareAddr( &addr->sip, inSockAddr ) == 0 ) break;
4221 }
4222 require_action_quiet( !addr, exit, err = kDuplicateErr );
4223
4224 newAddr = (BrowseIPAddr *) calloc( 1, sizeof( *newAddr ) );
4225 require_action( newAddr, exit, err = kNoMemoryErr );
4226
4227 ++inContext->refCount;
4228 newAddr->refCount = 1;
4229 newAddr->context = inContext;
4230 newAddr->foundTicks = nowTicks;
4231 SockAddrCopy( inSockAddr, &newAddr->sip.sa );
4232
4233 if( inInstance->isTCP && ( inInstance->port != kDiscardProtocolPort ) )
4234 {
4235 char destination[ kSockAddrStringMaxSize ];
4236
4237 err = SockAddrToString( &newAddr->sip, kSockAddrStringFlagsNoPort, destination );
4238 require_noerr( err, exit );
4239
4240 err = AsyncConnection_Connect( &newAddr->connection, destination, -inInstance->port, kAsyncConnectionFlag_P2P,
4241 kAsyncConnectionNoTimeout, kSocketBufferSize_DontSet, kSocketBufferSize_DontSet,
4242 BrowseAllConnectionProgress, newAddr, BrowseAllConnectionHandler, newAddr, dispatch_get_main_queue() );
4243 require_noerr( err, exit );
4244
4245 ++newAddr->refCount;
4246 newAddr->connectStatus = kConnectStatus_Pending;
4247 ++inContext->pendingConnectCount;
4248 }
4249
4250 *p = newAddr;
4251 newAddr = NULL;
4252 err = kNoErr;
4253
4254 exit:
4255 if( newAddr ) BrowseIPAddrRelease( newAddr );
4256 return( err );
4257 }
4258
4259 //===========================================================================================================================
4260 // BrowseAllRemoveIPAddress
4261 //===========================================================================================================================
4262
4263 static OSStatus
4264 BrowseAllRemoveIPAddress(
4265 BrowseAllContext * inContext,
4266 BrowseInstance * inInstance,
4267 const struct sockaddr * inSockAddr )
4268 {
4269 OSStatus err;
4270 BrowseIPAddr * addr;
4271 BrowseIPAddr ** p;
4272
4273 Unused( inContext );
4274
4275 for( p = &inInstance->addrList; ( addr = *p ) != NULL; p = &addr->next )
4276 {
4277 if( SockAddrCompareAddr( &addr->sip.sa, inSockAddr ) == 0 ) break;
4278 }
4279 require_action_quiet( addr, exit, err = kNotFoundErr );
4280
4281 *p = addr->next;
4282 BrowseIPAddrRelease( addr );
4283 err = kNoErr;
4284
4285 exit:
4286 return( err );
4287 }
4288
4289 //===========================================================================================================================
4290 // BrowseDomainFree
4291 //===========================================================================================================================
4292
4293 static void BrowseDomainFree( BrowseDomain *inDomain )
4294 {
4295 BrowseType * type;
4296
4297 ForgetBrowseAllContext( &inDomain->context );
4298 ForgetMem( &inDomain->name );
4299 DNSServiceForget( &inDomain->servicesQuery );
4300 while( ( type = inDomain->typeList ) != NULL )
4301 {
4302 inDomain->typeList = type->next;
4303 BrowseTypeFree( type );
4304 }
4305 free( inDomain );
4306 }
4307
4308 //===========================================================================================================================
4309 // BrowseTypeFree
4310 //===========================================================================================================================
4311
4312 static void BrowseTypeFree( BrowseType *inType )
4313 {
4314 BrowseOp * browse;
4315
4316 ForgetMem( &inType->name );
4317 while( ( browse = inType->browseList ) != NULL )
4318 {
4319 inType->browseList = browse->next;
4320 BrowseOpFree( browse );
4321 }
4322 free( inType );
4323 }
4324
4325 //===========================================================================================================================
4326 // BrowseOpFree
4327 //===========================================================================================================================
4328
4329 static void BrowseOpFree( BrowseOp *inBrowse )
4330 {
4331 BrowseInstance * instance;
4332
4333 ForgetBrowseAllContext( &inBrowse->context );
4334 DNSServiceForget( &inBrowse->browse );
4335 while( ( instance = inBrowse->instanceList ) != NULL )
4336 {
4337 inBrowse->instanceList = instance->next;
4338 BrowseInstanceFree( instance );
4339 }
4340 free( inBrowse );
4341 }
4342
4343 //===========================================================================================================================
4344 // BrowseInstanceFree
4345 //===========================================================================================================================
4346
4347 static void BrowseInstanceFree( BrowseInstance *inInstance )
4348 {
4349 ForgetBrowseAllContext( &inInstance->context );
4350 ForgetMem( &inInstance->name );
4351 DNSServiceForget( &inInstance->resolve );
4352 DNSServiceForget( &inInstance->getAddr );
4353 ForgetMem( &inInstance->txtPtr );
4354 ForgetMem( &inInstance->hostname );
4355 ForgetIPAddressList( &inInstance->addrList );
4356 free( inInstance );
4357 }
4358
4359 //===========================================================================================================================
4360 // BrowseIPAddrRelease
4361 //===========================================================================================================================
4362
4363 static void BrowseIPAddrRelease( BrowseIPAddr *inAddr )
4364 {
4365 AsyncConnection_Forget( &inAddr->connection );
4366 if( --inAddr->refCount == 0 )
4367 {
4368 ForgetBrowseAllContext( &inAddr->context );
4369 free( inAddr );
4370 }
4371 }
4372
4373 //===========================================================================================================================
4374 // BrowseIPAddrReleaseList
4375 //===========================================================================================================================
4376
4377 static void BrowseIPAddrReleaseList( BrowseIPAddr *inList )
4378 {
4379 BrowseIPAddr * addr;
4380
4381 while( ( addr = inList ) != NULL )
4382 {
4383 inList = addr->next;
4384 BrowseIPAddrRelease( addr );
4385 }
4386 }
4387
4388 //===========================================================================================================================
4389 // GetAddrInfoStressCmd
4390 //===========================================================================================================================
4391
4392 typedef struct
4393 {
4394 DNSServiceRef mainRef;
4395 DNSServiceRef sdRef;
4396 DNSServiceFlags flags;
4397 unsigned int interfaceIndex;
4398 unsigned int connectionNumber;
4399 unsigned int requestCount;
4400 unsigned int requestCountMax;
4401 unsigned int requestCountLimit;
4402 unsigned int durationMinMs;
4403 unsigned int durationMaxMs;
4404
4405 } GAIStressContext;
4406
4407 static void GetAddrInfoStressEvent( void *inContext );
4408 static void DNSSD_API
4409 GetAddrInfoStressCallback(
4410 DNSServiceRef inSDRef,
4411 DNSServiceFlags inFlags,
4412 uint32_t inInterfaceIndex,
4413 DNSServiceErrorType inError,
4414 const char * inHostname,
4415 const struct sockaddr * inSockAddr,
4416 uint32_t inTTL,
4417 void * inContext );
4418
4419 static void GetAddrInfoStressCmd( void )
4420 {
4421 OSStatus err;
4422 GAIStressContext * context = NULL;
4423 int i;
4424 DNSServiceFlags flags;
4425 uint32_t ifIndex;
4426 char ifName[ kInterfaceNameBufLen ];
4427 char time[ kTimestampBufLen ];
4428
4429 if( gGAIStress_TestDurationSecs < 0 )
4430 {
4431 FPrintF( stdout, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs );
4432 err = kParamErr;
4433 goto exit;
4434 }
4435 if( gGAIStress_ConnectionCount <= 0 )
4436 {
4437 FPrintF( stdout, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount );
4438 err = kParamErr;
4439 goto exit;
4440 }
4441 if( gGAIStress_DurationMinMs <= 0 )
4442 {
4443 FPrintF( stdout, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs );
4444 err = kParamErr;
4445 goto exit;
4446 }
4447 if( gGAIStress_DurationMaxMs <= 0 )
4448 {
4449 FPrintF( stdout, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs );
4450 err = kParamErr;
4451 goto exit;
4452 }
4453 if( gGAIStress_DurationMinMs > gGAIStress_DurationMaxMs )
4454 {
4455 FPrintF( stdout, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
4456 gGAIStress_DurationMinMs, gGAIStress_DurationMaxMs );
4457 err = kParamErr;
4458 goto exit;
4459 }
4460 if( gGAIStress_RequestCountMax <= 0 )
4461 {
4462 FPrintF( stdout, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax );
4463 err = kParamErr;
4464 goto exit;
4465 }
4466
4467 // Set flags.
4468
4469 flags = GetDNSSDFlagsFromOpts();
4470
4471 // Set interface index.
4472
4473 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
4474 require_noerr_quiet( err, exit );
4475
4476 for( i = 0; i < gGAIStress_ConnectionCount; ++i )
4477 {
4478 context = (GAIStressContext *) calloc( 1, sizeof( *context ) );
4479 require_action( context, exit, err = kNoMemoryErr );
4480
4481 context->flags = flags;
4482 context->interfaceIndex = ifIndex;
4483 context->connectionNumber = (unsigned int)( i + 1 );
4484 context->requestCountMax = (unsigned int) gGAIStress_RequestCountMax;
4485 context->durationMinMs = (unsigned int) gGAIStress_DurationMinMs;
4486 context->durationMaxMs = (unsigned int) gGAIStress_DurationMaxMs;
4487
4488 dispatch_async_f( dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
4489 context = NULL;
4490 }
4491
4492 if( gGAIStress_TestDurationSecs > 0 )
4493 {
4494 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs ), dispatch_get_main_queue(), NULL, Exit );
4495 }
4496
4497 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
4498 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
4499 FPrintF( stdout, "Test duration: " );
4500 if( gGAIStress_TestDurationSecs == 0 )
4501 {
4502 FPrintF( stdout, "∞\n" );
4503 }
4504 else
4505 {
4506 FPrintF( stdout, "%d s\n", gGAIStress_TestDurationSecs );
4507 }
4508 FPrintF( stdout, "Connection count: %d\n", gGAIStress_ConnectionCount );
4509 FPrintF( stdout, "Request duration min: %d ms\n", gGAIStress_DurationMinMs );
4510 FPrintF( stdout, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs );
4511 FPrintF( stdout, "Request count max: %d\n", gGAIStress_RequestCountMax );
4512 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
4513 FPrintF( stdout, "---\n" );
4514
4515 dispatch_main();
4516
4517 exit:
4518 FreeNullSafe( context );
4519 if( err ) exit( 1 );
4520 }
4521
4522 //===========================================================================================================================
4523 // GetAddrInfoStressEvent
4524 //===========================================================================================================================
4525
4526 #define kStressRandStrLen 5
4527
4528 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
4529
4530 static void GetAddrInfoStressEvent( void *inContext )
4531 {
4532 GAIStressContext * const context = (GAIStressContext *) inContext;
4533 OSStatus err;
4534 DNSServiceRef sdRef;
4535 unsigned int nextMs;
4536 char randomStr[ kStressRandStrLen + 1 ];
4537 char hostname[ kStressRandStrLen + 4 + 1 ];
4538 char time[ kTimestampBufLen ];
4539 Boolean isConnectionNew = false;
4540 static Boolean printedHeader = false;
4541
4542 if( !context->mainRef || ( context->requestCount >= context->requestCountLimit ) )
4543 {
4544 DNSServiceForget( &context->mainRef );
4545 context->sdRef = NULL;
4546 context->requestCount = 0;
4547 context->requestCountLimit = RandomRange( 1, context->requestCountMax );
4548
4549 err = DNSServiceCreateConnection( &context->mainRef );
4550 require_noerr( err, exit );
4551
4552 err = DNSServiceSetDispatchQueue( context->mainRef, dispatch_get_main_queue() );
4553 require_noerr( err, exit );
4554
4555 isConnectionNew = true;
4556 }
4557
4558 RandomString( kLowercaseAlphaCharSet, sizeof_string( kLowercaseAlphaCharSet ), 2, kStressRandStrLen, randomStr );
4559 SNPrintF( hostname, sizeof( hostname ), "%s.com", randomStr );
4560
4561 nextMs = RandomRange( context->durationMinMs, context->durationMaxMs );
4562
4563 if( !printedHeader )
4564 {
4565 FPrintF( stdout, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
4566 printedHeader = true;
4567 }
4568 FPrintF( stdout, "%-26s %3u%c %9s %8u\n",
4569 GetTimestampStr( time ), context->connectionNumber, isConnectionNew ? '*': ' ', hostname, nextMs );
4570
4571 DNSServiceForget( &context->sdRef );
4572 sdRef = context->mainRef;
4573 err = DNSServiceGetAddrInfo( &sdRef, context->flags | kDNSServiceFlagsShareConnection, context->interfaceIndex,
4574 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, hostname, GetAddrInfoStressCallback, NULL );
4575 require_noerr( err, exit );
4576 context->sdRef = sdRef;
4577
4578 context->requestCount++;
4579
4580 dispatch_after_f( dispatch_time_milliseconds( nextMs ), dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
4581
4582 exit:
4583 if( err ) exit( 1 );
4584 }
4585
4586 //===========================================================================================================================
4587 // GetAddrInfoStressCallback
4588 //===========================================================================================================================
4589
4590 static void DNSSD_API
4591 GetAddrInfoStressCallback(
4592 DNSServiceRef inSDRef,
4593 DNSServiceFlags inFlags,
4594 uint32_t inInterfaceIndex,
4595 DNSServiceErrorType inError,
4596 const char * inHostname,
4597 const struct sockaddr * inSockAddr,
4598 uint32_t inTTL,
4599 void * inContext )
4600 {
4601 Unused( inSDRef );
4602 Unused( inFlags );
4603 Unused( inInterfaceIndex );
4604 Unused( inError );
4605 Unused( inHostname );
4606 Unused( inSockAddr );
4607 Unused( inTTL );
4608 Unused( inContext );
4609 }
4610
4611 //===========================================================================================================================
4612 // DNSQueryCmd
4613 //===========================================================================================================================
4614
4615 #define kDNSPort 53
4616
4617 typedef struct
4618 {
4619 sockaddr_ip serverAddr;
4620 uint64_t sendTicks;
4621 uint8_t * msgPtr;
4622 size_t msgLen;
4623 size_t msgOffset;
4624 const char * name;
4625 dispatch_source_t readSource;
4626 SocketRef sock;
4627 int timeLimitSecs;
4628 uint16_t queryID;
4629 uint16_t type;
4630 Boolean haveTCPLen;
4631 Boolean useTCP;
4632 Boolean printRawRData; // True if RDATA results are not to be formatted.
4633 uint8_t msgBuf[ 512 ];
4634
4635 } DNSQueryContext;
4636
4637 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext );
4638 static void DNSQueryReadHandler( void *inContext );
4639 static void DNSQueryCancelHandler( void *inContext );
4640
4641 static void DNSQueryCmd( void )
4642 {
4643 OSStatus err;
4644 DNSQueryContext * context = NULL;
4645 uint8_t * msgPtr;
4646 size_t msgLen, sendLen;
4647
4648 // Check command parameters.
4649
4650 if( gDNSQuery_TimeLimitSecs < -1 )
4651 {
4652 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs );
4653 err = kParamErr;
4654 goto exit;
4655 }
4656 if( ( gDNSQuery_Flags < INT16_MIN ) || ( gDNSQuery_Flags > UINT16_MAX ) )
4657 {
4658 FPrintF( stdout, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags );
4659 err = kParamErr;
4660 goto exit;
4661 }
4662
4663 // Create context.
4664
4665 context = (DNSQueryContext *) calloc( 1, sizeof( *context ) );
4666 require_action( context, exit, err = kNoMemoryErr );
4667
4668 context->name = gDNSQuery_Name;
4669 context->sock = kInvalidSocketRef;
4670 context->timeLimitSecs = gDNSQuery_TimeLimitSecs;
4671 context->queryID = (uint16_t) Random32();
4672 context->useTCP = gDNSQuery_UseTCP ? true : false;
4673 context->printRawRData = gDNSQuery_RawRData ? true : false;
4674
4675 #if( TARGET_OS_DARWIN )
4676 if( gDNSQuery_Server )
4677 #endif
4678 {
4679 err = StringToSockAddr( gDNSQuery_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
4680 require_noerr( err, exit );
4681 }
4682 #if( TARGET_OS_DARWIN )
4683 else
4684 {
4685 err = GetDefaultDNSServer( &context->serverAddr );
4686 require_noerr( err, exit );
4687 }
4688 #endif
4689 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSPort );
4690
4691 err = RecordTypeFromArgString( gDNSQuery_Type, &context->type );
4692 require_noerr( err, exit );
4693
4694 // Write query message.
4695
4696 check_compile_time_code( sizeof( context->msgBuf ) >= ( kDNSQueryMessageMaxLen + 2 ) );
4697
4698 msgPtr = context->useTCP ? &context->msgBuf[ 2 ] : context->msgBuf;
4699 err = WriteDNSQueryMessage( msgPtr, context->queryID, (uint16_t) gDNSQuery_Flags, context->name, context->type,
4700 kDNSServiceClass_IN, &msgLen );
4701 require_noerr( err, exit );
4702 check( msgLen <= UINT16_MAX );
4703
4704 if( context->useTCP )
4705 {
4706 WriteBig16( context->msgBuf, msgLen );
4707 sendLen = 2 + msgLen;
4708 }
4709 else
4710 {
4711 sendLen = msgLen;
4712 }
4713
4714 DNSQueryPrintPrologue( context );
4715
4716 if( gDNSQuery_Verbose )
4717 {
4718 FPrintF( stdout, "DNS message to send:\n\n" );
4719 PrintUDNSMessage( msgPtr, msgLen, false );
4720 FPrintF( stdout, "---\n" );
4721 }
4722
4723 if( context->useTCP )
4724 {
4725 // Create TCP socket.
4726
4727 context->sock = socket( context->serverAddr.sa.sa_family, SOCK_STREAM, IPPROTO_TCP );
4728 err = map_socket_creation_errno( context->sock );
4729 require_noerr( err, exit );
4730
4731 err = SocketConnect( context->sock, &context->serverAddr, 5 );
4732 require_noerr( err, exit );
4733 }
4734 else
4735 {
4736 // Create UDP socket.
4737
4738 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &context->sock );
4739 require_noerr( err, exit );
4740 }
4741
4742 context->sendTicks = UpTicks();
4743 err = SocketWriteAll( context->sock, context->msgBuf, sendLen, 5 );
4744 require_noerr( err, exit );
4745
4746 if( context->timeLimitSecs == 0 ) goto exit;
4747
4748 err = DispatchReadSourceCreate( context->sock, DNSQueryReadHandler, DNSQueryCancelHandler, context,
4749 &context->readSource );
4750 require_noerr( err, exit );
4751 dispatch_resume( context->readSource );
4752
4753 if( context->timeLimitSecs > 0 )
4754 {
4755 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
4756 Exit );
4757 }
4758 dispatch_main();
4759
4760 exit:
4761 if( context )
4762 {
4763 dispatch_source_forget( &context->readSource );
4764 ForgetSocket( &context->sock );
4765 free( context );
4766 }
4767 if( err ) exit( 1 );
4768 }
4769
4770 //===========================================================================================================================
4771 // DNSQueryPrintPrologue
4772 //===========================================================================================================================
4773
4774 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext )
4775 {
4776 const int timeLimitSecs = inContext->timeLimitSecs;
4777 char time[ kTimestampBufLen ];
4778
4779 FPrintF( stdout, "Name: %s\n", inContext->name );
4780 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->type ), inContext->type );
4781 FPrintF( stdout, "Server: %##a\n", &inContext->serverAddr );
4782 FPrintF( stdout, "Transport: %s\n", inContext->useTCP ? "TCP" : "UDP" );
4783 FPrintF( stdout, "Time limit: " );
4784 if( timeLimitSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
4785 else FPrintF( stdout, "∞\n" );
4786 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
4787 FPrintF( stdout, "---\n" );
4788 }
4789
4790 //===========================================================================================================================
4791 // DNSQueryReadHandler
4792 //===========================================================================================================================
4793
4794 static void DNSQueryReadHandler( void *inContext )
4795 {
4796 OSStatus err;
4797 const uint64_t nowTicks = UpTicks();
4798 DNSQueryContext * const context = (DNSQueryContext *) inContext;
4799 char time[ kTimestampBufLen ];
4800
4801 GetTimestampStr( time );
4802
4803 if( context->useTCP )
4804 {
4805 if( !context->haveTCPLen )
4806 {
4807 err = SocketReadData( context->sock, &context->msgBuf, 2, &context->msgOffset );
4808 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
4809 require_noerr( err, exit );
4810
4811 context->msgOffset = 0;
4812 context->msgLen = ReadBig16( context->msgBuf );
4813 context->haveTCPLen = true;
4814 if( context->msgLen <= sizeof( context->msgBuf ) )
4815 {
4816 context->msgPtr = context->msgBuf;
4817 }
4818 else
4819 {
4820 context->msgPtr = (uint8_t *) malloc( context->msgLen );
4821 require_action( context->msgPtr, exit, err = kNoMemoryErr );
4822 }
4823 }
4824
4825 err = SocketReadData( context->sock, context->msgPtr, context->msgLen, &context->msgOffset );
4826 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
4827 require_noerr( err, exit );
4828 context->msgOffset = 0;
4829 context->haveTCPLen = false;
4830 }
4831 else
4832 {
4833 sockaddr_ip fromAddr;
4834
4835 context->msgPtr = context->msgBuf;
4836 err = SocketRecvFrom( context->sock, context->msgPtr, sizeof( context->msgBuf ), &context->msgLen, &fromAddr,
4837 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
4838 require_noerr( err, exit );
4839
4840 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
4841 }
4842
4843 FPrintF( stdout, "Receive time: %s\n", time );
4844 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
4845 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
4846 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
4847 PrintUDNSMessage( context->msgPtr, context->msgLen, context->printRawRData );
4848
4849 if( ( context->msgLen >= kDNSHeaderLength ) && ( DNSHeaderGetID( (DNSHeader *) context->msgPtr ) == context->queryID ) )
4850 {
4851 Exit( kExitReason_ReceivedResponse );
4852 }
4853
4854 exit:
4855 if( err ) dispatch_source_forget( &context->readSource );
4856 }
4857
4858 //===========================================================================================================================
4859 // DNSQueryCancelHandler
4860 //===========================================================================================================================
4861
4862 static void DNSQueryCancelHandler( void *inContext )
4863 {
4864 DNSQueryContext * const context = (DNSQueryContext *) inContext;
4865
4866 check( !context->readSource );
4867 ForgetSocket( &context->sock );
4868 if( context->msgPtr != context->msgBuf ) ForgetMem( &context->msgPtr );
4869 free( context );
4870 dispatch_async_f( dispatch_get_main_queue(), NULL, Exit );
4871 }
4872
4873 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
4874 //===========================================================================================================================
4875 // DNSCryptCmd
4876 //===========================================================================================================================
4877
4878 #define kDNSCryptPort 443
4879
4880 #define kDNSCryptMinPadLength 8
4881 #define kDNSCryptMaxPadLength 256
4882 #define kDNSCryptBlockSize 64
4883 #define kDNSCryptCertMinimumLength 124
4884 #define kDNSCryptClientMagicLength 8
4885 #define kDNSCryptResolverMagicLength 8
4886 #define kDNSCryptHalfNonceLength 12
4887 #define kDNSCryptCertMagicLength 4
4888
4889 check_compile_time( ( kDNSCryptHalfNonceLength * 2 ) == crypto_box_NONCEBYTES );
4890
4891 static const uint8_t kDNSCryptCertMagic[ kDNSCryptCertMagicLength ] = { 'D', 'N', 'S', 'C' };
4892 static const uint8_t kDNSCryptResolverMagic[ kDNSCryptResolverMagicLength ] =
4893 {
4894 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
4895 };
4896
4897 typedef struct
4898 {
4899 uint8_t certMagic[ kDNSCryptCertMagicLength ];
4900 uint8_t esVersion[ 2 ];
4901 uint8_t minorVersion[ 2 ];
4902 uint8_t signature[ crypto_sign_BYTES ];
4903 uint8_t publicKey[ crypto_box_PUBLICKEYBYTES ];
4904 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
4905 uint8_t serial[ 4 ];
4906 uint8_t startTime[ 4 ];
4907 uint8_t endTime[ 4 ];
4908 uint8_t extensions[ 1 ]; // Variably-sized extension data.
4909
4910 } DNSCryptCert;
4911
4912 check_compile_time( offsetof( DNSCryptCert, extensions ) == kDNSCryptCertMinimumLength );
4913
4914 typedef struct
4915 {
4916 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
4917 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
4918 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
4919 uint8_t poly1305MAC[ 16 ];
4920
4921 } DNSCryptQueryHeader;
4922
4923 check_compile_time( sizeof( DNSCryptQueryHeader ) == 68 );
4924 check_compile_time( sizeof( DNSCryptQueryHeader ) >= crypto_box_ZEROBYTES );
4925 check_compile_time( ( sizeof( DNSCryptQueryHeader ) - crypto_box_ZEROBYTES + crypto_box_BOXZEROBYTES ) ==
4926 offsetof( DNSCryptQueryHeader, poly1305MAC ) );
4927
4928 typedef struct
4929 {
4930 uint8_t resolverMagic[ kDNSCryptResolverMagicLength ];
4931 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
4932 uint8_t resolverNonce[ kDNSCryptHalfNonceLength ];
4933 uint8_t poly1305MAC[ 16 ];
4934
4935 } DNSCryptResponseHeader;
4936
4937 check_compile_time( sizeof( DNSCryptResponseHeader ) == 48 );
4938 check_compile_time( offsetof( DNSCryptResponseHeader, poly1305MAC ) >= crypto_box_BOXZEROBYTES );
4939 check_compile_time( ( offsetof( DNSCryptResponseHeader, poly1305MAC ) - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES ) ==
4940 sizeof( DNSCryptResponseHeader ) );
4941
4942 typedef struct
4943 {
4944 sockaddr_ip serverAddr;
4945 uint64_t sendTicks;
4946 const char * providerName;
4947 const char * qname;
4948 const uint8_t * certPtr;
4949 size_t certLen;
4950 dispatch_source_t readSource;
4951 size_t msgLen;
4952 int timeLimitSecs;
4953 uint16_t queryID;
4954 uint16_t qtype;
4955 Boolean printRawRData;
4956 uint8_t serverPublicSignKey[ crypto_sign_PUBLICKEYBYTES ];
4957 uint8_t serverPublicKey[ crypto_box_PUBLICKEYBYTES ];
4958 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
4959 uint8_t clientSecretKey[ crypto_box_SECRETKEYBYTES ];
4960 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
4961 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
4962 uint8_t nmKey[ crypto_box_BEFORENMBYTES ];
4963 uint8_t msgBuf[ 512 ];
4964
4965 } DNSCryptContext;
4966
4967 static void DNSCryptReceiveCertHandler( void *inContext );
4968 static void DNSCryptReceiveResponseHandler( void *inContext );
4969 static void DNSCryptProceed( void *inContext );
4970 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext );
4971 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext );
4972 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext );
4973 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen );
4974
4975 static void DNSCryptCmd( void )
4976 {
4977 OSStatus err;
4978 DNSCryptContext * context = NULL;
4979 size_t writtenBytes;
4980 size_t totalBytes;
4981 SocketContext * sockContext;
4982 SocketRef sock = kInvalidSocketRef;
4983 const char * ptr;
4984
4985 // Check command parameters.
4986
4987 if( gDNSCrypt_TimeLimitSecs < -1 )
4988 {
4989 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs );
4990 err = kParamErr;
4991 goto exit;
4992 }
4993
4994 // Create context.
4995
4996 context = (DNSCryptContext *) calloc( 1, sizeof( *context ) );
4997 require_action( context, exit, err = kNoMemoryErr );
4998
4999 context->providerName = gDNSCrypt_ProviderName;
5000 context->qname = gDNSCrypt_Name;
5001 context->timeLimitSecs = gDNSCrypt_TimeLimitSecs;
5002 context->printRawRData = gDNSCrypt_RawRData ? true : false;
5003
5004 err = crypto_box_keypair( context->clientPublicKey, context->clientSecretKey );
5005 require_noerr( err, exit );
5006
5007 err = HexToData( gDNSCrypt_ProviderKey, kSizeCString, kHexToData_DefaultFlags,
5008 context->serverPublicSignKey, sizeof( context->serverPublicSignKey ), &writtenBytes, &totalBytes, &ptr );
5009 if( err || ( *ptr != '\0' ) )
5010 {
5011 FPrintF( stderr, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey );
5012 goto exit;
5013 }
5014 else if( totalBytes != sizeof( context->serverPublicSignKey ) )
5015 {
5016 FPrintF( stderr, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
5017 totalBytes, sizeof( context->serverPublicSignKey ) );
5018 err = kSizeErr;
5019 goto exit;
5020 }
5021 check( writtenBytes == totalBytes );
5022
5023 err = StringToSockAddr( gDNSCrypt_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
5024 require_noerr( err, exit );
5025 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSCryptPort );
5026
5027 err = RecordTypeFromArgString( gDNSCrypt_Type, &context->qtype );
5028 require_noerr( err, exit );
5029
5030 // Write query message.
5031
5032 context->queryID = (uint16_t) Random32();
5033 err = WriteDNSQueryMessage( context->msgBuf, context->queryID, kDNSHeaderFlag_RecursionDesired, context->providerName,
5034 kDNSServiceType_TXT, kDNSServiceClass_IN, &context->msgLen );
5035 require_noerr( err, exit );
5036
5037 // Create UDP socket.
5038
5039 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &sock );
5040 require_noerr( err, exit );
5041
5042 // Send DNS query.
5043
5044 context->sendTicks = UpTicks();
5045 err = SocketWriteAll( sock, context->msgBuf, context->msgLen, 5 );
5046 require_noerr( err, exit );
5047
5048 sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) );
5049 require_action( sockContext, exit, err = kNoMemoryErr );
5050
5051 err = DispatchReadSourceCreate( sock, DNSCryptReceiveCertHandler, SocketContextCancelHandler, sockContext,
5052 &context->readSource );
5053 if( err ) ForgetMem( &sockContext );
5054 require_noerr( err, exit );
5055
5056 sockContext->context = context;
5057 sockContext->sock = sock;
5058 sock = kInvalidSocketRef;
5059 dispatch_resume( context->readSource );
5060
5061 if( context->timeLimitSecs > 0 )
5062 {
5063 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
5064 Exit );
5065 }
5066 dispatch_main();
5067
5068 exit:
5069 if( context ) free( context );
5070 ForgetSocket( &sock );
5071 if( err ) exit( 1 );
5072 }
5073
5074 //===========================================================================================================================
5075 // DNSCryptReceiveCertHandler
5076 //===========================================================================================================================
5077
5078 static void DNSCryptReceiveCertHandler( void *inContext )
5079 {
5080 OSStatus err;
5081 const uint64_t nowTicks = UpTicks();
5082 SocketContext * const sockContext = (SocketContext *) inContext;
5083 DNSCryptContext * const context = (DNSCryptContext *) sockContext->context;
5084 const DNSHeader * hdr;
5085 sockaddr_ip fromAddr;
5086 const uint8_t * ptr;
5087 const uint8_t * txtPtr;
5088 size_t txtLen;
5089 unsigned int answerCount, i;
5090 uint8_t targetName[ kDomainNameLengthMax ];
5091 char time[ kTimestampBufLen ];
5092
5093 GetTimestampStr( time );
5094
5095 dispatch_source_forget( &context->readSource );
5096
5097 err = SocketRecvFrom( sockContext->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
5098 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
5099 require_noerr( err, exit );
5100 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
5101
5102 FPrintF( stdout, "Receive time: %s\n", time );
5103 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
5104 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
5105 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
5106
5107 PrintUDNSMessage( context->msgBuf, context->msgLen, context->printRawRData );
5108
5109 require_action_quiet( context->msgLen >= kDNSHeaderLength, exit, err = kSizeErr );
5110
5111 hdr = (DNSHeader *) context->msgBuf;
5112 require_action_quiet( DNSHeaderGetID( hdr ) == context->queryID, exit, err = kMismatchErr );
5113
5114 err = DNSMessageGetAnswerSection( context->msgBuf, context->msgLen, &ptr );
5115 require_noerr( err, exit );
5116
5117 targetName[ 0 ] = 0;
5118 err = DomainNameAppendString( targetName, context->providerName, NULL );
5119 require_noerr( err, exit );
5120
5121 answerCount = DNSHeaderGetAnswerCount( hdr );
5122 for( i = 0; i < answerCount; ++i )
5123 {
5124 uint16_t type;
5125 uint16_t class;
5126 uint8_t name[ kDomainNameLengthMax ];
5127
5128 err = DNSMessageExtractRecord( context->msgBuf, context->msgLen, ptr, name, &type, &class, NULL, &txtPtr, &txtLen,
5129 &ptr );
5130 require_noerr( err, exit );
5131
5132 if( ( type == kDNSServiceType_TXT ) && ( class == kDNSServiceClass_IN ) && DomainNameEqual( name, targetName ) )
5133 {
5134 break;
5135 }
5136 }
5137
5138 if( txtLen < ( 1 + kDNSCryptCertMinimumLength ) )
5139 {
5140 FPrintF( stderr, "TXT record length is too short (%u < %u)\n", txtLen, kDNSCryptCertMinimumLength + 1 );
5141 err = kSizeErr;
5142 goto exit;
5143 }
5144 if( txtPtr[ 0 ] < kDNSCryptCertMinimumLength )
5145 {
5146 FPrintF( stderr, "TXT record value length is too short (%u < %u)\n", txtPtr[ 0 ], kDNSCryptCertMinimumLength );
5147 err = kSizeErr;
5148 goto exit;
5149 }
5150
5151 context->certLen = txtPtr[ 0 ];
5152 context->certPtr = &txtPtr[ 1 ];
5153
5154 dispatch_async_f( dispatch_get_main_queue(), context, DNSCryptProceed );
5155
5156 exit:
5157 if( err ) Exit( NULL );
5158 }
5159
5160 //===========================================================================================================================
5161 // DNSCryptReceiveResponseHandler
5162 //===========================================================================================================================
5163
5164 static void DNSCryptReceiveResponseHandler( void *inContext )
5165 {
5166 OSStatus err;
5167 const uint64_t nowTicks = UpTicks();
5168 SocketContext * const sockContext = (SocketContext *) inContext;
5169 DNSCryptContext * const context = (DNSCryptContext *) sockContext->context;
5170 sockaddr_ip fromAddr;
5171 DNSCryptResponseHeader * hdr;
5172 const uint8_t * end;
5173 uint8_t * ciphertext;
5174 uint8_t * plaintext;
5175 const uint8_t * response;
5176 char time[ kTimestampBufLen ];
5177 uint8_t nonce[ crypto_box_NONCEBYTES ];
5178
5179 GetTimestampStr( time );
5180
5181 dispatch_source_forget( &context->readSource );
5182
5183 err = SocketRecvFrom( sockContext->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
5184 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
5185 require_noerr( err, exit );
5186 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
5187
5188 FPrintF( stdout, "Receive time: %s\n", time );
5189 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
5190 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
5191 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
5192
5193 if( context->msgLen < sizeof( DNSCryptResponseHeader ) )
5194 {
5195 FPrintF( stderr, "DNSCrypt response is too short.\n" );
5196 err = kSizeErr;
5197 goto exit;
5198 }
5199
5200 hdr = (DNSCryptResponseHeader *) context->msgBuf;
5201
5202 if( memcmp( hdr->resolverMagic, kDNSCryptResolverMagic, kDNSCryptResolverMagicLength ) != 0 )
5203 {
5204 FPrintF( stderr, "DNSCrypt response resolver magic %#H != %#H\n",
5205 hdr->resolverMagic, kDNSCryptResolverMagicLength, INT_MAX,
5206 kDNSCryptResolverMagic, kDNSCryptResolverMagicLength, INT_MAX );
5207 err = kValueErr;
5208 goto exit;
5209 }
5210
5211 if( memcmp( hdr->clientNonce, context->clientNonce, kDNSCryptHalfNonceLength ) != 0 )
5212 {
5213 FPrintF( stderr, "DNSCrypt response client nonce mismatch.\n" );
5214 err = kValueErr;
5215 goto exit;
5216 }
5217
5218 memcpy( nonce, hdr->clientNonce, crypto_box_NONCEBYTES );
5219
5220 ciphertext = hdr->poly1305MAC - crypto_box_BOXZEROBYTES;
5221 memset( ciphertext, 0, crypto_box_BOXZEROBYTES );
5222
5223 plaintext = (uint8_t *)( hdr + 1 ) - crypto_box_ZEROBYTES;
5224 check( plaintext == ciphertext );
5225
5226 end = context->msgBuf + context->msgLen;
5227
5228 err = crypto_box_open_afternm( plaintext, ciphertext, (size_t)( end - ciphertext ), nonce, context->nmKey );
5229 require_noerr( err, exit );
5230
5231 response = plaintext + crypto_box_ZEROBYTES;
5232 PrintUDNSMessage( response, (size_t)( end - response ), context->printRawRData );
5233 Exit( kExitReason_ReceivedResponse );
5234
5235 exit:
5236 if( err ) Exit( NULL );
5237 }
5238
5239 //===========================================================================================================================
5240 // DNSCryptProceed
5241 //===========================================================================================================================
5242
5243 static void DNSCryptProceed( void *inContext )
5244 {
5245 OSStatus err;
5246 DNSCryptContext * const context = (DNSCryptContext *) inContext;
5247
5248 err = DNSCryptProcessCert( context );
5249 require_noerr_quiet( err, exit );
5250
5251 err = DNSCryptBuildQuery( context );
5252 require_noerr_quiet( err, exit );
5253
5254 err = DNSCryptSendQuery( context );
5255 require_noerr_quiet( err, exit );
5256
5257 exit:
5258 if( err ) Exit( NULL );
5259 }
5260
5261 //===========================================================================================================================
5262 // DNSCryptProcessCert
5263 //===========================================================================================================================
5264
5265 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext )
5266 {
5267 OSStatus err;
5268 const DNSCryptCert * const cert = (DNSCryptCert *) inContext->certPtr;
5269 const uint8_t * const certEnd = inContext->certPtr + inContext->certLen;
5270 struct timeval now;
5271 time_t startTimeSecs, endTimeSecs;
5272 size_t signedLen;
5273 uint8_t * tempBuf;
5274 unsigned long long tempLen;
5275
5276 DNSCryptPrintCertificate( cert, inContext->certLen );
5277
5278 if( memcmp( cert->certMagic, kDNSCryptCertMagic, kDNSCryptCertMagicLength ) != 0 )
5279 {
5280 FPrintF( stderr, "DNSCrypt certificate magic %#H != %#H\n",
5281 cert->certMagic, kDNSCryptCertMagicLength, INT_MAX,
5282 kDNSCryptCertMagic, kDNSCryptCertMagicLength, INT_MAX );
5283 err = kValueErr;
5284 goto exit;
5285 }
5286
5287 startTimeSecs = (time_t) ReadBig32( cert->startTime );
5288 endTimeSecs = (time_t) ReadBig32( cert->endTime );
5289
5290 gettimeofday( &now, NULL );
5291 if( now.tv_sec < startTimeSecs )
5292 {
5293 FPrintF( stderr, "DNSCrypt certificate start time is in the future.\n" );
5294 err = kDateErr;
5295 goto exit;
5296 }
5297 if( now.tv_sec >= endTimeSecs )
5298 {
5299 FPrintF( stderr, "DNSCrypt certificate has expired.\n" );
5300 err = kDateErr;
5301 goto exit;
5302 }
5303
5304 signedLen = (size_t)( certEnd - cert->signature );
5305 tempBuf = (uint8_t *) malloc( signedLen );
5306 require_action( tempBuf, exit, err = kNoMemoryErr );
5307 err = crypto_sign_open( tempBuf, &tempLen, cert->signature, signedLen, inContext->serverPublicSignKey );
5308 free( tempBuf );
5309 if( err )
5310 {
5311 FPrintF( stderr, "DNSCrypt certificate failed verification.\n" );
5312 err = kAuthenticationErr;
5313 goto exit;
5314 }
5315
5316 memcpy( inContext->serverPublicKey, cert->publicKey, crypto_box_PUBLICKEYBYTES );
5317 memcpy( inContext->clientMagic, cert->clientMagic, kDNSCryptClientMagicLength );
5318
5319 err = crypto_box_beforenm( inContext->nmKey, inContext->serverPublicKey, inContext->clientSecretKey );
5320 require_noerr( err, exit );
5321
5322 inContext->certPtr = NULL;
5323 inContext->certLen = 0;
5324 inContext->msgLen = 0;
5325
5326 exit:
5327 return( err );
5328 }
5329
5330 //===========================================================================================================================
5331 // DNSCryptBuildQuery
5332 //===========================================================================================================================
5333
5334 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen );
5335
5336 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext )
5337 {
5338 OSStatus err;
5339 DNSCryptQueryHeader * const hdr = (DNSCryptQueryHeader *) inContext->msgBuf;
5340 uint8_t * const queryPtr = (uint8_t *)( hdr + 1 );
5341 size_t queryLen;
5342 size_t paddedQueryLen;
5343 const uint8_t * const msgLimit = inContext->msgBuf + sizeof( inContext->msgBuf );
5344 const uint8_t * padLimit;
5345 uint8_t nonce[ crypto_box_NONCEBYTES ];
5346
5347 check_compile_time_code( sizeof( inContext->msgBuf ) >= ( sizeof( DNSCryptQueryHeader ) + kDNSQueryMessageMaxLen ) );
5348
5349 inContext->queryID = (uint16_t) Random32();
5350 err = WriteDNSQueryMessage( queryPtr, inContext->queryID, kDNSHeaderFlag_RecursionDesired, inContext->qname,
5351 inContext->qtype, kDNSServiceClass_IN, &queryLen );
5352 require_noerr( err, exit );
5353
5354 padLimit = &queryPtr[ queryLen + kDNSCryptMaxPadLength ];
5355 if( padLimit > msgLimit ) padLimit = msgLimit;
5356
5357 err = DNSCryptPadQuery( queryPtr, queryLen, (size_t)( padLimit - queryPtr ), &paddedQueryLen );
5358 require_noerr( err, exit );
5359
5360 memset( queryPtr - crypto_box_ZEROBYTES, 0, crypto_box_ZEROBYTES );
5361 RandomBytes( inContext->clientNonce, kDNSCryptHalfNonceLength );
5362 memcpy( nonce, inContext->clientNonce, kDNSCryptHalfNonceLength );
5363 memset( &nonce[ kDNSCryptHalfNonceLength ], 0, kDNSCryptHalfNonceLength );
5364
5365 err = crypto_box_afternm( queryPtr - crypto_box_ZEROBYTES, queryPtr - crypto_box_ZEROBYTES,
5366 paddedQueryLen + crypto_box_ZEROBYTES, nonce, inContext->nmKey );
5367 require_noerr( err, exit );
5368
5369 memcpy( hdr->clientMagic, inContext->clientMagic, kDNSCryptClientMagicLength );
5370 memcpy( hdr->clientPublicKey, inContext->clientPublicKey, crypto_box_PUBLICKEYBYTES );
5371 memcpy( hdr->clientNonce, nonce, kDNSCryptHalfNonceLength );
5372
5373 inContext->msgLen = (size_t)( &queryPtr[ paddedQueryLen ] - inContext->msgBuf );
5374
5375 exit:
5376 return( err );
5377 }
5378
5379 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen )
5380 {
5381 OSStatus err;
5382 size_t paddedLen;
5383
5384 require_action_quiet( ( inMsgLen + kDNSCryptMinPadLength ) <= inMaxLen, exit, err = kSizeErr );
5385
5386 paddedLen = inMsgLen + kDNSCryptMinPadLength +
5387 arc4random_uniform( (uint32_t)( inMaxLen - ( inMsgLen + kDNSCryptMinPadLength ) + 1 ) );
5388 paddedLen += ( kDNSCryptBlockSize - ( paddedLen % kDNSCryptBlockSize ) );
5389 if( paddedLen > inMaxLen ) paddedLen = inMaxLen;
5390
5391 inMsgPtr[ inMsgLen ] = 0x80;
5392 memset( &inMsgPtr[ inMsgLen + 1 ], 0, paddedLen - ( inMsgLen + 1 ) );
5393
5394 if( outPaddedLen ) *outPaddedLen = paddedLen;
5395 err = kNoErr;
5396
5397 exit:
5398 return( err );
5399 }
5400
5401 //===========================================================================================================================
5402 // DNSCryptSendQuery
5403 //===========================================================================================================================
5404
5405 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext )
5406 {
5407 OSStatus err;
5408 SocketContext * sockContext;
5409 SocketRef sock = kInvalidSocketRef;
5410
5411 check( inContext->msgLen > 0 );
5412 check( !inContext->readSource );
5413
5414 err = UDPClientSocketOpen( AF_UNSPEC, &inContext->serverAddr, 0, -1, NULL, &sock );
5415 require_noerr( err, exit );
5416
5417 inContext->sendTicks = UpTicks();
5418 err = SocketWriteAll( sock, inContext->msgBuf, inContext->msgLen, 5 );
5419 require_noerr( err, exit );
5420
5421 sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) );
5422 require_action( sockContext, exit, err = kNoMemoryErr );
5423
5424 err = DispatchReadSourceCreate( sock, DNSCryptReceiveResponseHandler, SocketContextCancelHandler, sockContext,
5425 &inContext->readSource );
5426 if( err ) ForgetMem( &sockContext );
5427 require_noerr( err, exit );
5428
5429 sockContext->context = inContext;
5430 sockContext->sock = sock;
5431 sock = kInvalidSocketRef;
5432
5433 dispatch_resume( inContext->readSource );
5434
5435 exit:
5436 ForgetSocket( &sock );
5437 return( err );
5438 }
5439
5440 //===========================================================================================================================
5441 // DNSCryptPrintCertificate
5442 //===========================================================================================================================
5443
5444 #define kCertTimeStrBufLen 32
5445
5446 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] );
5447
5448 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen )
5449 {
5450 time_t startTime, endTime;
5451 int extLen;
5452 char timeBuf[ kCertTimeStrBufLen ];
5453
5454 check( inLen >= kDNSCryptCertMinimumLength );
5455
5456 startTime = (time_t) ReadBig32( inCert->startTime );
5457 endTime = (time_t) ReadBig32( inCert->endTime );
5458
5459 FPrintF( stdout, "DNSCrypt certificate (%zu bytes):\n", inLen );
5460 FPrintF( stdout, "Cert Magic: %#H\n", inCert->certMagic, kDNSCryptCertMagicLength, INT_MAX );
5461 FPrintF( stdout, "ES Version: %u\n", ReadBig16( inCert->esVersion ) );
5462 FPrintF( stdout, "Minor Version: %u\n", ReadBig16( inCert->minorVersion ) );
5463 FPrintF( stdout, "Signature: %H\n", inCert->signature, crypto_sign_BYTES / 2, INT_MAX );
5464 FPrintF( stdout, " %H\n", &inCert->signature[ crypto_sign_BYTES / 2 ], crypto_sign_BYTES / 2, INT_MAX );
5465 FPrintF( stdout, "Public Key: %H\n", inCert->publicKey, sizeof( inCert->publicKey ), INT_MAX );
5466 FPrintF( stdout, "Client Magic: %H\n", inCert->clientMagic, kDNSCryptClientMagicLength, INT_MAX );
5467 FPrintF( stdout, "Serial: %u\n", ReadBig32( inCert->serial ) );
5468 FPrintF( stdout, "Start Time: %u (%s)\n", (uint32_t) startTime, CertTimeStr( startTime, timeBuf ) );
5469 FPrintF( stdout, "End Time: %u (%s)\n", (uint32_t) endTime, CertTimeStr( endTime, timeBuf ) );
5470
5471 if( inLen > kDNSCryptCertMinimumLength )
5472 {
5473 extLen = (int)( inLen - kDNSCryptCertMinimumLength );
5474 FPrintF( stdout, "Extensions: %.1H\n", inCert->extensions, extLen, extLen );
5475 }
5476 FPrintF( stdout, "\n" );
5477 }
5478
5479 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] )
5480 {
5481 struct tm * tm;
5482
5483 tm = localtime( &inTime );
5484 if( !tm )
5485 {
5486 dlogassert( "localtime() returned a NULL pointer.\n" );
5487 *inBuffer = '\0';
5488 }
5489 else
5490 {
5491 strftime( inBuffer, kCertTimeStrBufLen, "%a %b %d %H:%M:%S %Z %Y", tm );
5492 }
5493
5494 return( inBuffer );
5495 }
5496
5497 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
5498
5499 //===========================================================================================================================
5500 // MDNSQueryCmd
5501 //===========================================================================================================================
5502
5503 #define kMDNSPort 5353
5504
5505 #define kDefaultMDNSMessageID 0
5506 #define kDefaultMDNSQueryFlags 0
5507
5508 typedef struct
5509 {
5510 const char * qnameStr; // Name (QNAME) of the record being queried as a C string.
5511 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
5512 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
5513 int localPort; // The port number to which the sockets are bound.
5514 int receiveSecs; // After send, the amount of time to spend receiving.
5515 uint32_t ifIndex; // Index of the interface over which to send the query.
5516 uint16_t qtype; // The type (QTYPE) of the record being queried.
5517 Boolean isQU; // True if the query is QU, i.e., requests unicast responses.
5518 Boolean allResponses; // True if all mDNS messages received should be printed.
5519 Boolean printRawRData; // True if RDATA should be printed as hexdumps.
5520 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
5521 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
5522 char ifName[ IF_NAMESIZE + 1 ]; // Name of the interface over which to send the query.
5523 uint8_t qname[ kDomainNameLengthMax ]; // Buffer to hold the QNAME in label format.
5524 uint8_t msgBuf[ 8940 ]; // Message buffer. 8940 is max size used by mDNSResponder.
5525
5526 } MDNSQueryContext;
5527
5528 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext );
5529 static void MDNSQueryReadHandler( void *inContext );
5530
5531 static void MDNSQueryCmd( void )
5532 {
5533 OSStatus err;
5534 MDNSQueryContext * context;
5535 struct sockaddr_in mcastAddr4;
5536 struct sockaddr_in6 mcastAddr6;
5537 SocketRef sockV4 = kInvalidSocketRef;
5538 SocketRef sockV6 = kInvalidSocketRef;
5539 ssize_t n;
5540 const char * ifNamePtr;
5541 size_t msgLen;
5542 unsigned int sendCount;
5543
5544 // Check command parameters.
5545
5546 if( gMDNSQuery_ReceiveSecs < -1 )
5547 {
5548 FPrintF( stdout, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs );
5549 err = kParamErr;
5550 goto exit;
5551 }
5552
5553 context = (MDNSQueryContext *) calloc( 1, sizeof( *context ) );
5554 require_action( context, exit, err = kNoMemoryErr );
5555
5556 context->qnameStr = gMDNSQuery_Name;
5557 context->receiveSecs = gMDNSQuery_ReceiveSecs;
5558 context->isQU = gMDNSQuery_IsQU ? true : false;
5559 context->allResponses = gMDNSQuery_AllResponses ? true : false;
5560 context->printRawRData = gMDNSQuery_RawRData ? true : false;
5561 context->useIPv4 = ( gMDNSQuery_UseIPv4 || !gMDNSQuery_UseIPv6 ) ? true : false;
5562 context->useIPv6 = ( gMDNSQuery_UseIPv6 || !gMDNSQuery_UseIPv4 ) ? true : false;
5563
5564 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
5565 require_noerr_quiet( err, exit );
5566
5567 ifNamePtr = if_indextoname( context->ifIndex, context->ifName );
5568 require_action( ifNamePtr, exit, err = kNameErr );
5569
5570 err = RecordTypeFromArgString( gMDNSQuery_Type, &context->qtype );
5571 require_noerr( err, exit );
5572
5573 // Set up IPv4 socket.
5574
5575 if( context->useIPv4 )
5576 {
5577 err = ServerSocketOpen( AF_INET, SOCK_DGRAM, IPPROTO_UDP,
5578 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
5579 &context->localPort, kSocketBufferSize_DontSet, &sockV4 );
5580 require_noerr( err, exit );
5581
5582 err = SocketSetMulticastInterface( sockV4, ifNamePtr, context->ifIndex );
5583 require_noerr( err, exit );
5584
5585 err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
5586 err = map_socket_noerr_errno( sockV4, err );
5587 require_noerr( err, exit );
5588
5589 memset( &mcastAddr4, 0, sizeof( mcastAddr4 ) );
5590 SIN_LEN_SET( &mcastAddr4 );
5591 mcastAddr4.sin_family = AF_INET;
5592 mcastAddr4.sin_port = htons( kMDNSPort );
5593 mcastAddr4.sin_addr.s_addr = htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251
5594
5595 if( !context->isQU && ( context->localPort == kMDNSPort ) )
5596 {
5597 SocketJoinMulticast( sockV4, &mcastAddr4, ifNamePtr, context->ifIndex );
5598 require_noerr( err, exit );
5599 }
5600 }
5601
5602 // Set up IPv6 socket.
5603
5604 if( context->useIPv6 )
5605 {
5606 err = ServerSocketOpen( AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
5607 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
5608 &context->localPort, kSocketBufferSize_DontSet, &sockV6 );
5609 require_noerr( err, exit );
5610
5611 err = SocketSetMulticastInterface( sockV6, ifNamePtr, context->ifIndex );
5612 require_noerr( err, exit );
5613
5614 err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
5615 err = map_socket_noerr_errno( sockV6, err );
5616 require_noerr( err, exit );
5617
5618 memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) );
5619 SIN6_LEN_SET( &mcastAddr6 );
5620 mcastAddr6.sin6_family = AF_INET6;
5621 mcastAddr6.sin6_port = htons( kMDNSPort );
5622 mcastAddr6.sin6_addr.s6_addr[ 0 ] = 0xFF; // mDNS IPv6 multicast address FF02::FB
5623 mcastAddr6.sin6_addr.s6_addr[ 1 ] = 0x02;
5624 mcastAddr6.sin6_addr.s6_addr[ 15 ] = 0xFB;
5625
5626 if( !context->isQU && ( context->localPort == kMDNSPort ) )
5627 {
5628 SocketJoinMulticast( sockV6, &mcastAddr6, ifNamePtr, context->ifIndex );
5629 require_noerr( err, exit );
5630 }
5631 }
5632
5633 // Craft mDNS query message.
5634
5635 check_compile_time_code( sizeof( context->msgBuf ) >= kDNSQueryMessageMaxLen );
5636 err = WriteDNSQueryMessage( context->msgBuf, kDefaultMDNSMessageID, kDefaultMDNSQueryFlags, context->qnameStr,
5637 context->qtype, context->isQU ? ( kDNSServiceClass_IN | kQClassUnicastResponseBit ) : kDNSServiceClass_IN, &msgLen );
5638 require_noerr( err, exit );
5639
5640 // Print prologue.
5641
5642 MDNSQueryPrintPrologue( context );
5643
5644 // Send mDNS query message.
5645
5646 sendCount = 0;
5647 if( IsValidSocket( sockV4 ) )
5648 {
5649 n = sendto( sockV4, context->msgBuf, msgLen, 0, (struct sockaddr *) &mcastAddr4, (socklen_t) sizeof( mcastAddr4 ) );
5650 err = map_socket_value_errno( sockV4, n == (ssize_t) msgLen, n );
5651 if( err )
5652 {
5653 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
5654 ForgetSocket( &sockV4 );
5655 }
5656 else
5657 {
5658 ++sendCount;
5659 }
5660 }
5661 if( IsValidSocket( sockV6 ) )
5662 {
5663 n = sendto( sockV6, context->msgBuf, msgLen, 0, (struct sockaddr *) &mcastAddr6, (socklen_t) sizeof( mcastAddr6 ) );
5664 err = map_socket_value_errno( sockV6, n == (ssize_t) msgLen, n );
5665 if( err )
5666 {
5667 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
5668 ForgetSocket( &sockV6 );
5669 }
5670 else
5671 {
5672 ++sendCount;
5673 }
5674 }
5675 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
5676
5677 // If there's no wait period after the send, then exit.
5678
5679 if( context->receiveSecs == 0 ) goto exit;
5680
5681 // Create dispatch read sources for socket(s).
5682
5683 if( IsValidSocket( sockV4 ) )
5684 {
5685 SocketContext * sockContext;
5686
5687 sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) );
5688 require_action( sockContext, exit, err = kNoMemoryErr );
5689
5690 err = DispatchReadSourceCreate( sockV4, MDNSQueryReadHandler, SocketContextCancelHandler, sockContext,
5691 &context->readSourceV4 );
5692 if( err ) ForgetMem( &sockContext );
5693 require_noerr( err, exit );
5694
5695 sockContext->context = context;
5696 sockContext->sock = sockV4;
5697 sockV4 = kInvalidSocketRef;
5698 dispatch_resume( context->readSourceV4 );
5699 }
5700
5701 if( IsValidSocket( sockV6 ) )
5702 {
5703 SocketContext * sockContext;
5704
5705 sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) );
5706 require_action( sockContext, exit, err = kNoMemoryErr );
5707
5708 err = DispatchReadSourceCreate( sockV6, MDNSQueryReadHandler, SocketContextCancelHandler, sockContext,
5709 &context->readSourceV6 );
5710 if( err ) ForgetMem( &sockContext );
5711 require_noerr( err, exit );
5712
5713 sockContext->context = context;
5714 sockContext->sock = sockV6;
5715 sockV6 = kInvalidSocketRef;
5716 dispatch_resume( context->readSourceV6 );
5717 }
5718
5719 if( context->receiveSecs > 0 )
5720 {
5721 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
5722 Exit );
5723 }
5724 dispatch_main();
5725
5726 exit:
5727 ForgetSocket( &sockV4 );
5728 ForgetSocket( &sockV6 );
5729 if( err ) exit( 1 );
5730 }
5731
5732 //===========================================================================================================================
5733 // MDNSQueryPrintPrologue
5734 //===========================================================================================================================
5735
5736 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext )
5737 {
5738 const int receiveSecs = inContext->receiveSecs;
5739 char time[ kTimestampBufLen ];
5740
5741 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, inContext->ifName );
5742 FPrintF( stdout, "Name: %s\n", inContext->qnameStr );
5743 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->qtype ), inContext->qtype );
5744 FPrintF( stdout, "Class: IN (%s)\n", inContext->isQU ? "QU" : "QM" );
5745 FPrintF( stdout, "Local port: %d\n", inContext->localPort );
5746 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
5747 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
5748 FPrintF( stdout, "Receive duration: " );
5749 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
5750 else FPrintF( stdout, "∞\n" );
5751 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
5752 }
5753
5754 //===========================================================================================================================
5755 // MDNSQueryReadHandler
5756 //===========================================================================================================================
5757
5758 static void MDNSQueryReadHandler( void *inContext )
5759 {
5760 OSStatus err;
5761 SocketContext * const sockContext = (SocketContext *) inContext;
5762 MDNSQueryContext * const context = (MDNSQueryContext *) sockContext->context;
5763 size_t msgLen;
5764 sockaddr_ip fromAddr;
5765 char time[ kTimestampBufLen ];
5766 Boolean foundAnswer = false;
5767
5768 GetTimestampStr( time );
5769
5770 err = SocketRecvFrom( sockContext->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &fromAddr,
5771 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
5772 require_noerr( err, exit );
5773
5774 if( !context->allResponses && ( msgLen >= kDNSHeaderLength ) )
5775 {
5776 const uint8_t * ptr;
5777 const DNSHeader * const hdr = (DNSHeader *) context->msgBuf;
5778 unsigned int rrCount, i;
5779 uint16_t type, class;
5780 uint8_t name[ kDomainNameLengthMax ];
5781
5782 err = DNSMessageGetAnswerSection( context->msgBuf, msgLen, &ptr );
5783 require_noerr( err, exit );
5784
5785 if( context->qname[ 0 ] == 0 )
5786 {
5787 err = DomainNameAppendString( context->qname, context->qnameStr, NULL );
5788 require_noerr( err, exit );
5789 }
5790
5791 rrCount = DNSHeaderGetAnswerCount( hdr ) + DNSHeaderGetAuthorityCount( hdr ) + DNSHeaderGetAdditionalCount( hdr );
5792 for( i = 0; i < rrCount; ++i )
5793 {
5794 err = DNSMessageExtractRecord( context->msgBuf, msgLen, ptr, name, &type, &class, NULL, NULL, NULL, &ptr );
5795 require_noerr( err, exit );
5796
5797 if( ( ( context->qtype == kDNSServiceType_ANY ) || ( type == context->qtype ) ) &&
5798 DomainNameEqual( name, context->qname ) )
5799 {
5800 foundAnswer = true;
5801 break;
5802 }
5803 }
5804 }
5805 if( context->allResponses || foundAnswer )
5806 {
5807 FPrintF( stdout, "---\n" );
5808 FPrintF( stdout, "Receive time: %s\n", time );
5809 FPrintF( stdout, "Source: %##a\n", &fromAddr );
5810 FPrintF( stdout, "Message size: %zu\n\n", msgLen );
5811
5812 PrintMDNSMessage( context->msgBuf, msgLen, context->printRawRData );
5813 }
5814
5815 exit:
5816 if( err ) exit( 1 );
5817 }
5818
5819 //===========================================================================================================================
5820 // PIDToUUIDCmd
5821 //===========================================================================================================================
5822
5823 static void PIDToUUIDCmd( void )
5824 {
5825 OSStatus err;
5826 int n;
5827 struct proc_uniqidentifierinfo info;
5828
5829 n = proc_pidinfo( gPIDToUUID_PID, PROC_PIDUNIQIDENTIFIERINFO, 1, &info, sizeof( info ) );
5830 require_action_quiet( n == (int) sizeof( info ), exit, err = kUnknownErr );
5831
5832 FPrintF( stdout, "%#U\n", info.p_uuid );
5833 err = kNoErr;
5834
5835 exit:
5836 if( err ) exit( 1 );
5837 }
5838
5839 //===========================================================================================================================
5840 // DaemonVersionCmd
5841 //===========================================================================================================================
5842
5843 static void DaemonVersionCmd( void )
5844 {
5845 OSStatus err;
5846 uint32_t size, version;
5847 char strBuf[ 16 ];
5848
5849 size = (uint32_t) sizeof( version );
5850 err = DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion, &version, &size );
5851 require_noerr( err, exit );
5852
5853 FPrintF( stdout, "Daemon version: %s\n", SourceVersionToCString( version, strBuf ) );
5854
5855 exit:
5856 if( err ) exit( 1 );
5857 }
5858
5859 //===========================================================================================================================
5860 // Exit
5861 //===========================================================================================================================
5862
5863 static void Exit( void *inContext )
5864 {
5865 const char * const reason = (const char *) inContext;
5866 char time[ kTimestampBufLen ];
5867
5868 FPrintF( stdout, "---\n" );
5869 FPrintF( stdout, "End time: %s\n", GetTimestampStr( time ) );
5870 if( reason ) FPrintF( stdout, "End reason: %s\n", reason );
5871 exit( gExitCode );
5872 }
5873
5874 //===========================================================================================================================
5875 // GetTimestampStr
5876 //===========================================================================================================================
5877
5878 static char * GetTimestampStr( char inBuffer[ kTimestampBufLen ] )
5879 {
5880 struct timeval now;
5881 struct tm * tm;
5882 size_t len;
5883
5884 gettimeofday( &now, NULL );
5885 tm = localtime( &now.tv_sec );
5886 require_action( tm, exit, *inBuffer = '\0' );
5887
5888 len = strftime( inBuffer, kTimestampBufLen, "%Y-%m-%d %H:%M:%S", tm );
5889 SNPrintF( &inBuffer[ len ], kTimestampBufLen - len, ".%06u", (unsigned int) now.tv_usec );
5890
5891 exit:
5892 return( inBuffer );
5893 }
5894
5895 //===========================================================================================================================
5896 // GetDNSSDFlagsFromOpts
5897 //===========================================================================================================================
5898
5899 static DNSServiceFlags GetDNSSDFlagsFromOpts( void )
5900 {
5901 DNSServiceFlags flags;
5902
5903 flags = (DNSServiceFlags) gDNSSDFlags;
5904 if( flags & kDNSServiceFlagsShareConnection )
5905 {
5906 FPrintF( stderr, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
5907 kDNSServiceFlagsShareConnection );
5908 }
5909
5910 if( gDNSSDFlag_BrowseDomains ) flags |= kDNSServiceFlagsBrowseDomains;
5911 if( gDNSSDFlag_DenyCellular ) flags |= kDNSServiceFlagsDenyCellular;
5912 if( gDNSSDFlag_DenyExpensive ) flags |= kDNSServiceFlagsDenyExpensive;
5913 if( gDNSSDFlag_ForceMulticast ) flags |= kDNSServiceFlagsForceMulticast;
5914 if( gDNSSDFlag_IncludeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
5915 if( gDNSSDFlag_NoAutoRename ) flags |= kDNSServiceFlagsNoAutoRename;
5916 if( gDNSSDFlag_PathEvaluationDone ) flags |= kDNSServiceFlagsPathEvaluationDone;
5917 if( gDNSSDFlag_RegistrationDomains ) flags |= kDNSServiceFlagsRegistrationDomains;
5918 if( gDNSSDFlag_ReturnIntermediates ) flags |= kDNSServiceFlagsReturnIntermediates;
5919 if( gDNSSDFlag_Shared ) flags |= kDNSServiceFlagsShared;
5920 if( gDNSSDFlag_SuppressUnusable ) flags |= kDNSServiceFlagsSuppressUnusable;
5921 if( gDNSSDFlag_Timeout ) flags |= kDNSServiceFlagsTimeout;
5922 if( gDNSSDFlag_UnicastResponse ) flags |= kDNSServiceFlagsUnicastResponse;
5923 if( gDNSSDFlag_Unique ) flags |= kDNSServiceFlagsUnique;
5924
5925 return( flags );
5926 }
5927
5928 //===========================================================================================================================
5929 // CreateConnectionFromArgString
5930 //===========================================================================================================================
5931
5932 static OSStatus
5933 CreateConnectionFromArgString(
5934 const char * inString,
5935 dispatch_queue_t inQueue,
5936 DNSServiceRef * outSDRef,
5937 ConnectionDesc * outDesc )
5938 {
5939 OSStatus err;
5940 DNSServiceRef sdRef = NULL;
5941 ConnectionType type;
5942 int32_t pid = -1; // Initializing because the analyzer claims pid may be used uninitialized.
5943 uint8_t uuid[ 16 ];
5944
5945 if( strcasecmp( inString, kConnectionArg_Normal ) == 0 )
5946 {
5947 err = DNSServiceCreateConnection( &sdRef );
5948 require_noerr( err, exit );
5949 type = kConnectionType_Normal;
5950 }
5951 else if( stricmp_prefix( inString, kConnectionArgPrefix_PID ) == 0 )
5952 {
5953 const char * const pidStr = inString + sizeof_string( kConnectionArgPrefix_PID );
5954
5955 err = StringToInt32( pidStr, &pid );
5956 if( err )
5957 {
5958 FPrintF( stderr, "Invalid delegate connection PID value: %s\n", pidStr );
5959 err = kParamErr;
5960 goto exit;
5961 }
5962
5963 memset( uuid, 0, sizeof( uuid ) );
5964 err = DNSServiceCreateDelegateConnection( &sdRef, pid, uuid );
5965 if( err )
5966 {
5967 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err, pid );
5968 goto exit;
5969 }
5970 type = kConnectionType_DelegatePID;
5971 }
5972 else if( stricmp_prefix( inString, kConnectionArgPrefix_UUID ) == 0 )
5973 {
5974 const char * const uuidStr = inString + sizeof_string( kConnectionArgPrefix_UUID );
5975
5976 check_compile_time_code( sizeof( uuid ) == sizeof( uuid_t ) );
5977
5978 err = StringToUUID( uuidStr, kSizeCString, false, uuid );
5979 if( err )
5980 {
5981 FPrintF( stderr, "Invalid delegate connection UUID value: %s\n", uuidStr );
5982 err = kParamErr;
5983 goto exit;
5984 }
5985
5986 err = DNSServiceCreateDelegateConnection( &sdRef, 0, uuid );
5987 if( err )
5988 {
5989 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err, uuid );
5990 goto exit;
5991 }
5992 type = kConnectionType_DelegateUUID;
5993 }
5994 else
5995 {
5996 FPrintF( stderr, "Unrecognized connection string \"%s\".\n", inString );
5997 err = kParamErr;
5998 goto exit;
5999 }
6000
6001 err = DNSServiceSetDispatchQueue( sdRef, inQueue );
6002 require_noerr( err, exit );
6003
6004 *outSDRef = sdRef;
6005 if( outDesc )
6006 {
6007 outDesc->type = type;
6008 if( type == kConnectionType_DelegatePID ) outDesc->delegate.pid = pid;
6009 else if( type == kConnectionType_DelegateUUID ) memcpy( outDesc->delegate.uuid, uuid, 16 );
6010 }
6011 sdRef = NULL;
6012
6013 exit:
6014 if( sdRef ) DNSServiceRefDeallocate( sdRef );
6015 return( err );
6016 }
6017
6018 //===========================================================================================================================
6019 // InterfaceIndexFromArgString
6020 //===========================================================================================================================
6021
6022 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex )
6023 {
6024 OSStatus err;
6025 uint32_t ifIndex;
6026
6027 if( inString )
6028 {
6029 ifIndex = if_nametoindex( inString );
6030 if( ifIndex == 0 )
6031 {
6032 err = StringToUInt32( inString, &ifIndex );
6033 if( err )
6034 {
6035 FPrintF( stderr, "Invalid interface value: %s\n", inString );
6036 err = kParamErr;
6037 goto exit;
6038 }
6039 }
6040 }
6041 else
6042 {
6043 ifIndex = 0;
6044 }
6045
6046 *outIndex = ifIndex;
6047 err = kNoErr;
6048
6049 exit:
6050 return( err );
6051 }
6052
6053 //===========================================================================================================================
6054 // RecordDataFromArgString
6055 //===========================================================================================================================
6056
6057 #define kRDataMaxLen UINT16_C( 0xFFFF )
6058
6059 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen )
6060 {
6061 OSStatus err;
6062 uint8_t * dataPtr = NULL;
6063 size_t dataLen;
6064 DataBuffer dataBuf;
6065
6066 DataBuffer_Init( &dataBuf, NULL, 0, kRDataMaxLen );
6067
6068 if( stricmp_prefix( inString, kRDataArgPrefix_String ) == 0 )
6069 {
6070 const char * const strPtr = inString + sizeof_string( kRDataArgPrefix_String );
6071 const size_t strLen = strlen( strPtr );
6072 size_t copiedLen;
6073 size_t totalLen;
6074
6075 if( strLen > 0 )
6076 {
6077 require_action( strLen <= kRDataMaxLen, exit, err = kSizeErr );
6078 dataPtr = (uint8_t *) malloc( strLen );
6079 require_action( dataPtr, exit, err = kNoMemoryErr );
6080
6081 copiedLen = 0;
6082 ParseQuotedEscapedString( strPtr, strPtr + strLen, "", (char *) dataPtr, strLen, &copiedLen, &totalLen, NULL );
6083 check( copiedLen == totalLen );
6084 dataLen = copiedLen;
6085 }
6086 else
6087 {
6088 dataPtr = NULL;
6089 dataLen = 0;
6090 }
6091 }
6092 else if( stricmp_prefix( inString, kRDataArgPrefix_HexString ) == 0 )
6093 {
6094 const char * const strPtr = inString + sizeof_string( kRDataArgPrefix_HexString );
6095
6096 err = HexToDataCopy( strPtr, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL );
6097 require_noerr( err, exit );
6098 require_action( dataLen <= kRDataMaxLen, exit, err = kSizeErr );
6099 }
6100 else if( stricmp_prefix( inString, kRDataArgPrefix_File ) == 0 )
6101 {
6102 const char * const path = inString + sizeof_string( kRDataArgPrefix_File );
6103
6104 err = CopyFileDataByPath( path, (char **) &dataPtr, &dataLen );
6105 require_noerr( err, exit );
6106 require_action( dataLen <= kRDataMaxLen, exit, err = kSizeErr );
6107 }
6108 else if( stricmp_prefix( inString, kRDataArgPrefix_TXT ) == 0 )
6109 {
6110 const char * strPtr = inString + sizeof_string( kRDataArgPrefix_TXT );
6111 const char * const strEnd = strPtr + strlen( strPtr );
6112
6113 while( strPtr < strEnd )
6114 {
6115 size_t copiedLen, totalLen;
6116 uint8_t kvBuf[ 1 + 255 + 1 ]; // Length byte + max key-value length + 1 for NUL terminator.
6117
6118 err = ParseEscapedString( strPtr, strEnd, ',', (char *) &kvBuf[ 1 ], sizeof( kvBuf ) - 1,
6119 &copiedLen, &totalLen, &strPtr );
6120 require_noerr_quiet( err, exit );
6121 check( copiedLen == totalLen );
6122 if( totalLen > 255 )
6123 {
6124 FPrintF( stderr, "TXT key-value pair length %zu is too long (> 255 bytes).\n", totalLen );
6125 err = kParamErr;
6126 goto exit;
6127 }
6128
6129 kvBuf[ 0 ] = (uint8_t) copiedLen;
6130 err = DataBuffer_Append( &dataBuf, kvBuf, 1 + kvBuf[ 0 ] );
6131 require_noerr( err, exit );
6132 }
6133
6134 err = DataBuffer_Commit( &dataBuf, NULL, NULL );
6135 require_noerr( err, exit );
6136
6137 err = DataBuffer_Detach( &dataBuf, &dataPtr, &dataLen );
6138 require_noerr( err, exit );
6139 }
6140 else
6141 {
6142 FPrintF( stderr, "Unrecognized record data string \"%s\".\n", inString );
6143 err = kParamErr;
6144 goto exit;
6145 }
6146 err = kNoErr;
6147
6148 *outDataLen = dataLen;
6149 *outDataPtr = dataPtr;
6150 dataPtr = NULL;
6151
6152 exit:
6153 DataBuffer_Free( &dataBuf );
6154 FreeNullSafe( dataPtr );
6155 return( err );
6156 }
6157
6158 //===========================================================================================================================
6159 // RecordTypeFromArgString
6160 //===========================================================================================================================
6161
6162 typedef struct
6163 {
6164 uint16_t value; // Record type's numeric value.
6165 const char * name; // Record type's name as a string (e.g., "A", "PTR", "SRV").
6166
6167 } RecordType;
6168
6169 static const RecordType kRecordTypes[] =
6170 {
6171 // Common types.
6172
6173 { kDNSServiceType_A, "A" },
6174 { kDNSServiceType_AAAA, "AAAA" },
6175 { kDNSServiceType_PTR, "PTR" },
6176 { kDNSServiceType_SRV, "SRV" },
6177 { kDNSServiceType_TXT, "TXT" },
6178 { kDNSServiceType_CNAME, "CNAME" },
6179 { kDNSServiceType_SOA, "SOA" },
6180 { kDNSServiceType_NSEC, "NSEC" },
6181 { kDNSServiceType_NS, "NS" },
6182 { kDNSServiceType_MX, "MX" },
6183 { kDNSServiceType_ANY, "ANY" },
6184 { kDNSServiceType_OPT, "OPT" },
6185
6186 // Less common types.
6187
6188 { kDNSServiceType_MD, "MD" },
6189 { kDNSServiceType_NS, "NS" },
6190 { kDNSServiceType_MD, "MD" },
6191 { kDNSServiceType_MF, "MF" },
6192 { kDNSServiceType_MB, "MB" },
6193 { kDNSServiceType_MG, "MG" },
6194 { kDNSServiceType_MR, "MR" },
6195 { kDNSServiceType_NULL, "NULL" },
6196 { kDNSServiceType_WKS, "WKS" },
6197 { kDNSServiceType_HINFO, "HINFO" },
6198 { kDNSServiceType_MINFO, "MINFO" },
6199 { kDNSServiceType_RP, "RP" },
6200 { kDNSServiceType_AFSDB, "AFSDB" },
6201 { kDNSServiceType_X25, "X25" },
6202 { kDNSServiceType_ISDN, "ISDN" },
6203 { kDNSServiceType_RT, "RT" },
6204 { kDNSServiceType_NSAP, "NSAP" },
6205 { kDNSServiceType_NSAP_PTR, "NSAP_PTR" },
6206 { kDNSServiceType_SIG, "SIG" },
6207 { kDNSServiceType_KEY, "KEY" },
6208 { kDNSServiceType_PX, "PX" },
6209 { kDNSServiceType_GPOS, "GPOS" },
6210 { kDNSServiceType_LOC, "LOC" },
6211 { kDNSServiceType_NXT, "NXT" },
6212 { kDNSServiceType_EID, "EID" },
6213 { kDNSServiceType_NIMLOC, "NIMLOC" },
6214 { kDNSServiceType_ATMA, "ATMA" },
6215 { kDNSServiceType_NAPTR, "NAPTR" },
6216 { kDNSServiceType_KX, "KX" },
6217 { kDNSServiceType_CERT, "CERT" },
6218 { kDNSServiceType_A6, "A6" },
6219 { kDNSServiceType_DNAME, "DNAME" },
6220 { kDNSServiceType_SINK, "SINK" },
6221 { kDNSServiceType_APL, "APL" },
6222 { kDNSServiceType_DS, "DS" },
6223 { kDNSServiceType_SSHFP, "SSHFP" },
6224 { kDNSServiceType_IPSECKEY, "IPSECKEY" },
6225 { kDNSServiceType_RRSIG, "RRSIG" },
6226 { kDNSServiceType_DNSKEY, "DNSKEY" },
6227 { kDNSServiceType_DHCID, "DHCID" },
6228 { kDNSServiceType_NSEC3, "NSEC3" },
6229 { kDNSServiceType_NSEC3PARAM, "NSEC3PARAM" },
6230 { kDNSServiceType_HIP, "HIP" },
6231 { kDNSServiceType_SPF, "SPF" },
6232 { kDNSServiceType_UINFO, "UINFO" },
6233 { kDNSServiceType_UID, "UID" },
6234 { kDNSServiceType_GID, "GID" },
6235 { kDNSServiceType_UNSPEC, "UNSPEC" },
6236 { kDNSServiceType_TKEY, "TKEY" },
6237 { kDNSServiceType_TSIG, "TSIG" },
6238 { kDNSServiceType_IXFR, "IXFR" },
6239 { kDNSServiceType_AXFR, "AXFR" },
6240 { kDNSServiceType_MAILB, "MAILB" },
6241 { kDNSServiceType_MAILA, "MAILA" }
6242 };
6243
6244 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue )
6245 {
6246 OSStatus err;
6247 int32_t i32;
6248 const RecordType * type;
6249 const RecordType * const end = kRecordTypes + countof( kRecordTypes );
6250
6251 for( type = kRecordTypes; type < end; ++type )
6252 {
6253 if( strcasecmp( type->name, inString ) == 0 )
6254 {
6255 *outValue = type->value;
6256 return( kNoErr );
6257 }
6258 }
6259
6260 err = StringToInt32( inString, &i32 );
6261 require_noerr_quiet( err, exit );
6262 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
6263
6264 *outValue = (uint16_t) i32;
6265
6266 exit:
6267 return( err );
6268 }
6269
6270 //===========================================================================================================================
6271 // RecordClassFromArgString
6272 //===========================================================================================================================
6273
6274 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue )
6275 {
6276 OSStatus err;
6277 int32_t i32;
6278
6279 if( strcasecmp( inString, "IN" ) == 0 )
6280 {
6281 *outValue = kDNSServiceClass_IN;
6282 err = kNoErr;
6283 goto exit;
6284 }
6285
6286 err = StringToInt32( inString, &i32 );
6287 require_noerr_quiet( err, exit );
6288 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
6289
6290 *outValue = (uint16_t) i32;
6291
6292 exit:
6293 return( err );
6294 }
6295
6296 //===========================================================================================================================
6297 // InterfaceIndexToName
6298 //===========================================================================================================================
6299
6300 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] )
6301 {
6302 switch( inIfIndex )
6303 {
6304 case kDNSServiceInterfaceIndexAny:
6305 strlcpy( inNameBuf, "Any", kInterfaceNameBufLen );
6306 break;
6307
6308 case kDNSServiceInterfaceIndexLocalOnly:
6309 strlcpy( inNameBuf, "LocalOnly", kInterfaceNameBufLen );
6310 break;
6311
6312 case kDNSServiceInterfaceIndexUnicast:
6313 strlcpy( inNameBuf, "Unicast", kInterfaceNameBufLen );
6314 break;
6315
6316 case kDNSServiceInterfaceIndexP2P:
6317 strlcpy( inNameBuf, "P2P", kInterfaceNameBufLen );
6318 break;
6319
6320 #if( defined( kDNSServiceInterfaceIndexBLE ) )
6321 case kDNSServiceInterfaceIndexBLE:
6322 strlcpy( inNameBuf, "BLE", kInterfaceNameBufLen );
6323 break;
6324 #endif
6325
6326 default:
6327 {
6328 const char * name;
6329
6330 name = if_indextoname( inIfIndex, inNameBuf );
6331 if( !name ) strlcpy( inNameBuf, "NO NAME", kInterfaceNameBufLen );
6332 break;
6333 }
6334 }
6335
6336 return( inNameBuf );
6337 }
6338
6339 //===========================================================================================================================
6340 // RecordTypeToString
6341 //===========================================================================================================================
6342
6343 static const char * RecordTypeToString( unsigned int inValue )
6344 {
6345 const RecordType * type;
6346 const RecordType * const end = kRecordTypes + countof( kRecordTypes );
6347
6348 for( type = kRecordTypes; type < end; ++type )
6349 {
6350 if( type->value == inValue ) return( type->name );
6351 }
6352 return( "???" );
6353 }
6354
6355 //===========================================================================================================================
6356 // DNSMessageExtractDomainName
6357 //===========================================================================================================================
6358
6359 #define IsCompressionByte( X ) ( ( ( X ) & 0xC0 ) == 0xC0 )
6360
6361 static OSStatus
6362 DNSMessageExtractDomainName(
6363 const uint8_t * inMsgPtr,
6364 size_t inMsgLen,
6365 const uint8_t * inNamePtr,
6366 uint8_t inBuf[ kDomainNameLengthMax ],
6367 const uint8_t ** outNextPtr )
6368 {
6369 OSStatus err;
6370 const uint8_t * label;
6371 uint8_t labelLen;
6372 const uint8_t * nextLabel;
6373 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
6374 uint8_t * dst = inBuf;
6375 const uint8_t * const dstLim = inBuf ? ( inBuf + kDomainNameLengthMax ) : NULL;
6376 const uint8_t * nameEnd = NULL;
6377
6378 require_action( ( inNamePtr >= inMsgPtr ) && ( inNamePtr < msgEnd ), exit, err = kRangeErr );
6379
6380 for( label = inNamePtr; ( labelLen = label[ 0 ] ) != 0; label = nextLabel )
6381 {
6382 if( labelLen <= kDomainLabelLengthMax )
6383 {
6384 nextLabel = label + 1 + labelLen;
6385 require_action( nextLabel < msgEnd, exit, err = kUnderrunErr );
6386 if( dst )
6387 {
6388 require_action( ( dstLim - dst ) > ( 1 + labelLen ), exit, err = kOverrunErr );
6389 memcpy( dst, label, 1 + labelLen );
6390 dst += ( 1 + labelLen );
6391 }
6392 }
6393 else if( IsCompressionByte( labelLen ) )
6394 {
6395 uint16_t offset;
6396
6397 require_action( ( msgEnd - label ) >= 2, exit, err = kUnderrunErr );
6398 if( !nameEnd )
6399 {
6400 nameEnd = label + 2;
6401 if( !dst ) break;
6402 }
6403 offset = (uint16_t)( ( ( label[ 0 ] & 0x3F ) << 8 ) | label[ 1 ] );
6404 nextLabel = inMsgPtr + offset;
6405 require_action( nextLabel < msgEnd, exit, err = kUnderrunErr );
6406 require_action( !IsCompressionByte( nextLabel[ 0 ] ), exit, err = kMalformedErr );
6407 }
6408 else
6409 {
6410 dlogassert( "Unhandled label length 0x%02X\n", labelLen );
6411 err = kMalformedErr;
6412 goto exit;
6413 }
6414 }
6415
6416 if( dst ) *dst = 0;
6417 if( !nameEnd ) nameEnd = label + 1;
6418
6419 if( outNextPtr ) *outNextPtr = nameEnd;
6420 err = kNoErr;
6421
6422 exit:
6423 return( err );
6424 }
6425
6426 //===========================================================================================================================
6427 // DNSMessageExtractDomainNameString
6428 //===========================================================================================================================
6429
6430 static OSStatus
6431 DNSMessageExtractDomainNameString(
6432 const void * inMsgPtr,
6433 size_t inMsgLen,
6434 const void * inNamePtr,
6435 char inBuf[ kDNSServiceMaxDomainName ],
6436 const uint8_t ** outNextPtr )
6437 {
6438 OSStatus err;
6439 const uint8_t * nextPtr;
6440 uint8_t domainName[ kDomainNameLengthMax ];
6441
6442 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inNamePtr, domainName, &nextPtr );
6443 require_noerr( err, exit );
6444
6445 err = DomainNameToString( domainName, NULL, inBuf, NULL );
6446 require_noerr( err, exit );
6447
6448 if( outNextPtr ) *outNextPtr = nextPtr;
6449
6450 exit:
6451 return( err );
6452 }
6453
6454 //===========================================================================================================================
6455 // DNSMessageExtractRecord
6456 //===========================================================================================================================
6457
6458 typedef struct
6459 {
6460 uint8_t type[ 2 ];
6461 uint8_t class[ 2 ];
6462 uint8_t ttl[ 4 ];
6463 uint8_t rdLength[ 2 ];
6464 uint8_t rdata[ 1 ];
6465
6466 } DNSRecordFields;
6467
6468 check_compile_time( offsetof( DNSRecordFields, rdata ) == 10 );
6469
6470 static OSStatus
6471 DNSMessageExtractRecord(
6472 const uint8_t * inMsgPtr,
6473 size_t inMsgLen,
6474 const uint8_t * inPtr,
6475 uint8_t inNameBuf[ kDomainNameLengthMax ],
6476 uint16_t * outType,
6477 uint16_t * outClass,
6478 uint32_t * outTTL,
6479 const uint8_t ** outRDataPtr,
6480 size_t * outRDataLen,
6481 const uint8_t ** outPtr )
6482 {
6483 OSStatus err;
6484 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
6485 const uint8_t * ptr;
6486 const DNSRecordFields * record;
6487 size_t rdLength;
6488
6489 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inPtr, inNameBuf, &ptr );
6490 require_noerr_quiet( err, exit );
6491 require_action_quiet( (size_t)( msgEnd - ptr ) >= offsetof( DNSRecordFields, rdata ), exit, err = kUnderrunErr );
6492
6493 record = (DNSRecordFields *) ptr;
6494 rdLength = ReadBig16( record->rdLength );
6495 require_action_quiet( (size_t)( msgEnd - record->rdata ) >= rdLength , exit, err = kUnderrunErr );
6496
6497 if( outType ) *outType = ReadBig16( record->type );
6498 if( outClass ) *outClass = ReadBig16( record->class );
6499 if( outTTL ) *outTTL = ReadBig32( record->ttl );
6500 if( outRDataPtr ) *outRDataPtr = record->rdata;
6501 if( outRDataLen ) *outRDataLen = rdLength;
6502 if( outPtr ) *outPtr = record->rdata + rdLength;
6503
6504 exit:
6505 return( err );
6506 }
6507
6508 //===========================================================================================================================
6509 // DNSMessageGetAnswerSection
6510 //===========================================================================================================================
6511
6512 static OSStatus DNSMessageGetAnswerSection( const uint8_t *inMsgPtr, size_t inMsgLen, const uint8_t **outPtr )
6513 {
6514 OSStatus err;
6515 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
6516 unsigned int questionCount, i;
6517 const DNSHeader * hdr;
6518 const uint8_t * ptr;
6519
6520 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
6521
6522 hdr = (DNSHeader *) inMsgPtr;
6523 questionCount = DNSHeaderGetQuestionCount( hdr );
6524
6525 ptr = (uint8_t *)( hdr + 1 );
6526 for( i = 0; i < questionCount; ++i )
6527 {
6528 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, ptr, NULL, &ptr );
6529 require_noerr( err, exit );
6530 require_action_quiet( ( msgEnd - ptr ) >= 4, exit, err = kUnderrunErr );
6531 ptr += 4;
6532 }
6533
6534 if( outPtr ) *outPtr = ptr;
6535 err = kNoErr;
6536
6537 exit:
6538 return( err );
6539 }
6540
6541 //===========================================================================================================================
6542 // DNSRecordDataToString
6543 //===========================================================================================================================
6544
6545 static OSStatus
6546 DNSRecordDataToString(
6547 const void * inRDataPtr,
6548 size_t inRDataLen,
6549 unsigned int inRDataType,
6550 const void * inMsgPtr,
6551 size_t inMsgLen,
6552 char ** outString )
6553 {
6554 OSStatus err;
6555 const uint8_t * const rdataPtr = (uint8_t *) inRDataPtr;
6556 const uint8_t * const rdataEnd = rdataPtr + inRDataLen;
6557 char * rdataStr;
6558 const uint8_t * ptr;
6559 int n;
6560 char domainNameStr[ kDNSServiceMaxDomainName ];
6561
6562 rdataStr = NULL;
6563 if( inRDataType == kDNSServiceType_A )
6564 {
6565 require_action_quiet( inRDataLen == 4, exit, err = kMalformedErr );
6566
6567 ASPrintF( &rdataStr, "%.4a", rdataPtr );
6568 require_action( rdataStr, exit, err = kNoMemoryErr );
6569 }
6570 else if( inRDataType == kDNSServiceType_AAAA )
6571 {
6572 require_action_quiet( inRDataLen == 16, exit, err = kMalformedErr );
6573
6574 ASPrintF( &rdataStr, "%.16a", rdataPtr );
6575 require_action( rdataStr, exit, err = kNoMemoryErr );
6576 }
6577 else if( ( inRDataType == kDNSServiceType_PTR ) || ( inRDataType == kDNSServiceType_CNAME ) ||
6578 ( inRDataType == kDNSServiceType_NS ) )
6579 {
6580 if( inMsgPtr )
6581 {
6582 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, NULL );
6583 require_noerr( err, exit );
6584 }
6585 else
6586 {
6587 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, NULL );
6588 require_noerr( err, exit );
6589 }
6590
6591 rdataStr = strdup( domainNameStr );
6592 require_action( rdataStr, exit, err = kNoMemoryErr );
6593 }
6594 else if( inRDataType == kDNSServiceType_SRV )
6595 {
6596 uint16_t priority, weight, port;
6597 const uint8_t * target;
6598
6599 require_action_quiet( ( rdataPtr + 6 ) < rdataEnd, exit, err = kMalformedErr );
6600
6601 priority = ReadBig16( rdataPtr );
6602 weight = ReadBig16( rdataPtr + 2 );
6603 port = ReadBig16( rdataPtr + 4 );
6604 target = rdataPtr + 6;
6605
6606 if( inMsgPtr )
6607 {
6608 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, target, domainNameStr, NULL );
6609 require_noerr( err, exit );
6610 }
6611 else
6612 {
6613 err = DomainNameToString( target, rdataEnd, domainNameStr, NULL );
6614 require_noerr( err, exit );
6615 }
6616
6617 ASPrintF( &rdataStr, "%u %u %u %s", priority, weight, port, domainNameStr );
6618 require_action( rdataStr, exit, err = kNoMemoryErr );
6619 }
6620 else if( inRDataType == kDNSServiceType_TXT )
6621 {
6622 require_action_quiet( inRDataLen > 0, exit, err = kMalformedErr );
6623
6624 if( inRDataLen == 1 )
6625 {
6626 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) inRDataLen, INT_MAX );
6627 require_action( rdataStr, exit, err = kNoMemoryErr );
6628 }
6629 else
6630 {
6631 ASPrintF( &rdataStr, "%#{txt}", rdataPtr, inRDataLen );
6632 require_action( rdataStr, exit, err = kNoMemoryErr );
6633 }
6634 }
6635 else if( inRDataType == kDNSServiceType_SOA )
6636 {
6637 uint32_t serial, refresh, retry, expire, minimum;
6638
6639 if( inMsgPtr )
6640 {
6641 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
6642 require_noerr( err, exit );
6643
6644 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
6645
6646 rdataStr = strdup( domainNameStr );
6647 require_action( rdataStr, exit, err = kNoMemoryErr );
6648
6649 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, ptr, domainNameStr, &ptr );
6650 require_noerr( err, exit );
6651 }
6652 else
6653 {
6654 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
6655 require_noerr( err, exit );
6656
6657 rdataStr = strdup( domainNameStr );
6658 require_action( rdataStr, exit, err = kNoMemoryErr );
6659
6660 err = DomainNameToString( ptr, rdataEnd, domainNameStr, &ptr );
6661 require_noerr( err, exit );
6662 }
6663
6664 require_action_quiet( ( ptr + 20 ) == rdataEnd, exit, err = kMalformedErr );
6665
6666 serial = ReadBig32( ptr );
6667 refresh = ReadBig32( ptr + 4 );
6668 retry = ReadBig32( ptr + 8 );
6669 expire = ReadBig32( ptr + 12 );
6670 minimum = ReadBig32( ptr + 16 );
6671
6672 n = AppendPrintF( &rdataStr, " %s %u %u %u %u %u\n", domainNameStr, serial, refresh, retry, expire, minimum );
6673 require_action( n > 0, exit, err = kUnknownErr );
6674 }
6675 else if( inRDataType == kDNSServiceType_NSEC )
6676 {
6677 unsigned int windowBlock, bitmapLen, i, recordType;
6678 const uint8_t * bitmapPtr;
6679
6680 if( inMsgPtr )
6681 {
6682 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
6683 require_noerr( err, exit );
6684 }
6685 else
6686 {
6687 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
6688 require_noerr( err, exit );
6689 }
6690
6691 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
6692
6693 rdataStr = strdup( domainNameStr );
6694 require_action( rdataStr, exit, err = kNoMemoryErr );
6695
6696 for( ; ptr < rdataEnd; ptr += ( 2 + bitmapLen ) )
6697 {
6698 require_action_quiet( ( ptr + 2 ) < rdataEnd, exit, err = kMalformedErr );
6699
6700 windowBlock = ptr[ 0 ];
6701 bitmapLen = ptr[ 1 ];
6702 bitmapPtr = &ptr[ 2 ];
6703
6704 require_action_quiet( ( bitmapLen >= 1 ) && ( bitmapLen <= 32 ) , exit, err = kMalformedErr );
6705 require_action_quiet( ( bitmapPtr + bitmapLen ) <= rdataEnd, exit, err = kMalformedErr );
6706
6707 for( i = 0; i < BitArray_MaxBits( bitmapLen ); ++i )
6708 {
6709 if( BitArray_GetBit( bitmapPtr, bitmapLen, i ) )
6710 {
6711 recordType = ( windowBlock * 256 ) + i;
6712 n = AppendPrintF( &rdataStr, " %s", RecordTypeToString( recordType ) );
6713 require_action( n > 0, exit, err = kUnknownErr );
6714 }
6715 }
6716 }
6717 }
6718 else if( inRDataType == kDNSServiceType_MX )
6719 {
6720 uint16_t preference;
6721 const uint8_t * exchange;
6722
6723 require_action_quiet( ( rdataPtr + 2 ) < rdataEnd, exit, err = kMalformedErr );
6724
6725 preference = ReadBig16( rdataPtr );
6726 exchange = &rdataPtr[ 2 ];
6727
6728 if( inMsgPtr )
6729 {
6730 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, exchange, domainNameStr, NULL );
6731 require_noerr( err, exit );
6732 }
6733 else
6734 {
6735 err = DomainNameToString( exchange, rdataEnd, domainNameStr, NULL );
6736 require_noerr( err, exit );
6737 }
6738
6739 n = ASPrintF( &rdataStr, "%u %s", preference, domainNameStr );
6740 require_action( n > 0, exit, err = kUnknownErr );
6741 }
6742 else
6743 {
6744 err = kNotHandledErr;
6745 goto exit;
6746 }
6747
6748 check( rdataStr );
6749 *outString = rdataStr;
6750 rdataStr = NULL;
6751 err = kNoErr;
6752
6753 exit:
6754 FreeNullSafe( rdataStr );
6755 return( err );
6756 }
6757
6758 //===========================================================================================================================
6759 // DomainNameAppendString
6760 //===========================================================================================================================
6761
6762 static OSStatus
6763 DomainNameAppendString(
6764 uint8_t inDomainName[ kDomainNameLengthMax ],
6765 const char * inString,
6766 uint8_t ** outEndPtr )
6767 {
6768 OSStatus err;
6769 const char * src;
6770 uint8_t * dst;
6771 const uint8_t * const nameLimit = inDomainName + kDomainNameLengthMax;
6772
6773 // Find the root label.
6774
6775 for( dst = inDomainName; ( dst < nameLimit ) && *dst; dst += ( 1 + *dst ) ) {}
6776 require_action_quiet( dst < nameLimit, exit, err = kMalformedErr );
6777
6778 // Append the string's labels one label at a time.
6779
6780 src = inString;
6781 while( *src )
6782 {
6783 uint8_t * const label = dst++;
6784 const uint8_t * const labelLimit = Min( dst + kDomainLabelLengthMax, nameLimit - 1 );
6785
6786 // If the first character is a label separator, then the label is empty. Empty non-root labels are not allowed.
6787
6788 require_action_quiet( *src != '.', exit, err = kMalformedErr );
6789
6790 // Write the label characters until the end of the label, a separator or NUL character, is encountered, or until no
6791 // more space is available.
6792
6793 while( ( *src != '.' ) && ( *src != '\0' ) && ( dst < labelLimit ) )
6794 {
6795 uint8_t value;
6796
6797 value = (uint8_t) *src++;
6798 if( value == '\\' )
6799 {
6800 if( *src == '\0' ) break;
6801 value = (uint8_t) *src++;
6802 if( isdigit_safe( value ) && isdigit_safe( src[ 0 ] ) && isdigit_safe( src[ 1 ] ) )
6803 {
6804 int decimalValue;
6805
6806 decimalValue = ( ( value - '0' ) * 100 ) + ( ( src[ 0 ] - '0' ) * 10 ) + ( src[ 1 ] - '0' );
6807 if( decimalValue <= 255 )
6808 {
6809 value = (uint8_t) decimalValue;
6810 src += 2;
6811 }
6812 }
6813 }
6814 *dst++ = value;
6815 }
6816 if( ( *src == '.' ) || ( *src == '\0' ) )
6817 {
6818 label[ 0 ] = (uint8_t)( dst - &label[ 1 ] ); // Write the label length.
6819 if( *src == '.' ) ++src; // Advance the pointer past the label separator.
6820 }
6821 else
6822 {
6823 label[ 0 ] = 0;
6824 err = kOverrunErr;
6825 goto exit;
6826 }
6827 }
6828
6829 *dst++ = 0; // Write the empty root label.
6830 if( outEndPtr ) *outEndPtr = dst;
6831 err = kNoErr;
6832
6833 exit:
6834 return( err );
6835 }
6836
6837 //===========================================================================================================================
6838 // DomainNameEqual
6839 //===========================================================================================================================
6840
6841 static Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 )
6842 {
6843 const uint8_t * p1 = inName1;
6844 const uint8_t * p2 = inName2;
6845 unsigned int len;
6846
6847 for( ;; )
6848 {
6849 if( ( len = *p1++ ) != *p2++ ) return( false );
6850 if( len == 0 ) break;
6851 for( ; len > 0; ++p1, ++p2, --len )
6852 {
6853 if( tolower_safe( *p1 ) != tolower_safe( *p2 ) ) return( false );
6854 }
6855 }
6856 return( true );
6857 }
6858
6859 //===========================================================================================================================
6860 // DomainNameToString
6861 //===========================================================================================================================
6862
6863 static OSStatus
6864 DomainNameToString(
6865 const uint8_t * inDomainName,
6866 const uint8_t * inEnd,
6867 char inBuf[ kDNSServiceMaxDomainName ],
6868 const uint8_t ** outNextPtr )
6869 {
6870 OSStatus err;
6871 const uint8_t * label;
6872 uint8_t labelLen;
6873 const uint8_t * nextLabel;
6874 char * dst;
6875 const uint8_t * src;
6876
6877 require_action( !inEnd || ( inDomainName < inEnd ), exit, err = kUnderrunErr );
6878
6879 // Convert each label up until the root label, i.e., the zero-length label.
6880
6881 dst = inBuf;
6882 for( label = inDomainName; ( labelLen = label[ 0 ] ) != 0; label = nextLabel )
6883 {
6884 require_action( labelLen <= kDomainLabelLengthMax, exit, err = kMalformedErr );
6885
6886 nextLabel = &label[ 1 ] + labelLen;
6887 require_action( ( nextLabel - inDomainName ) < kDomainNameLengthMax, exit, err = kMalformedErr );
6888 require_action( !inEnd || ( nextLabel < inEnd ), exit, err = kUnderrunErr );
6889
6890 for( src = &label[ 1 ]; src < nextLabel; ++src )
6891 {
6892 if( isprint_safe( *src ) )
6893 {
6894 if( ( *src == '.' ) || ( *src == '\\' ) || ( *src == ' ' ) ) *dst++ = '\\';
6895 *dst++ = (char) *src;
6896 }
6897 else
6898 {
6899 *dst++ = '\\';
6900 *dst++ = '0' + ( *src / 100 );
6901 *dst++ = '0' + ( ( *src / 10 ) % 10 );
6902 *dst++ = '0' + ( *src % 10 );
6903 }
6904 }
6905 *dst++ = '.';
6906 }
6907
6908 // At this point, label points to the root label.
6909 // If the root label was the only label, then write a dot for it.
6910
6911 if( label == inDomainName ) *dst++ = '.';
6912 *dst = '\0';
6913 if( outNextPtr ) *outNextPtr = label + 1;
6914 err = kNoErr;
6915
6916 exit:
6917 return( err );
6918 }
6919
6920 //===========================================================================================================================
6921 // PrintDNSMessage
6922 //===========================================================================================================================
6923
6924 #define DNSFlagsOpCodeToString( X ) ( \
6925 ( (X) == kDNSOpCode_Query ) ? "Query" : \
6926 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \
6927 ( (X) == kDNSOpCode_Status ) ? "Status" : \
6928 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \
6929 ( (X) == kDNSOpCode_Update ) ? "Update" : \
6930 "Unassigned" )
6931
6932 #define DNSFlagsRCodeToString( X ) ( \
6933 ( (X) == kDNSRCode_NoError ) ? "NoError" : \
6934 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \
6935 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \
6936 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \
6937 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \
6938 ( (X) == kDNSRCode_Refused ) ? "Refused" : \
6939 "???" )
6940
6941 #define DNSFlagsGetOpCode( X ) ( ( (X) >> 11 ) & 0x0F )
6942 #define DNSFlagsGetRCode( X ) ( (X) & 0x0F )
6943
6944 static OSStatus PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, const Boolean inIsMDNS, const Boolean inPrintRaw )
6945 {
6946 OSStatus err;
6947 const DNSHeader * hdr;
6948 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
6949 const uint8_t * ptr;
6950 unsigned int id, flags, opcode, rcode;
6951 unsigned int questionCount, answerCount, authorityCount, additionalCount, i, totalRRCount;
6952 char nameStr[ kDNSServiceMaxDomainName ];
6953
6954 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
6955
6956 hdr = (DNSHeader *) inMsgPtr;
6957 id = DNSHeaderGetID( hdr );
6958 flags = DNSHeaderGetFlags( hdr );
6959 questionCount = DNSHeaderGetQuestionCount( hdr );
6960 answerCount = DNSHeaderGetAnswerCount( hdr );
6961 authorityCount = DNSHeaderGetAuthorityCount( hdr );
6962 additionalCount = DNSHeaderGetAdditionalCount( hdr );
6963 opcode = DNSFlagsGetOpCode( flags );
6964 rcode = DNSFlagsGetRCode( flags );
6965
6966 FPrintF( stdout, "ID: 0x%04X (%u)\n", id, id );
6967 FPrintF( stdout, "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA %s\n",
6968 flags,
6969 ( flags & kDNSHeaderFlag_Response ) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode ),
6970 ( flags & kDNSHeaderFlag_AuthAnswer ) ? ' ' : '!',
6971 ( flags & kDNSHeaderFlag_Truncation ) ? ' ' : '!',
6972 ( flags & kDNSHeaderFlag_RecursionDesired ) ? ' ' : '!',
6973 ( flags & kDNSHeaderFlag_RecursionAvailable ) ? ' ' : '!',
6974 DNSFlagsRCodeToString( rcode ) );
6975 FPrintF( stdout, "Question count: %u\n", questionCount );
6976 FPrintF( stdout, "Answer count: %u\n", answerCount );
6977 FPrintF( stdout, "Authority count: %u\n", authorityCount );
6978 FPrintF( stdout, "Additional count: %u\n", additionalCount );
6979
6980 ptr = (uint8_t *)( hdr + 1 );
6981 for( i = 0; i < questionCount; ++i )
6982 {
6983 unsigned int qType, qClass;
6984 Boolean isQU;
6985
6986 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, ptr, nameStr, &ptr );
6987 require_noerr( err, exit );
6988
6989 if( ( msgEnd - ptr ) < 4 )
6990 {
6991 err = kUnderrunErr;
6992 goto exit;
6993 }
6994
6995 qType = ReadBig16( ptr );
6996 ptr += 2;
6997 qClass = ReadBig16( ptr );
6998 ptr += 2;
6999
7000 isQU = ( inIsMDNS && ( qClass & kQClassUnicastResponseBit ) ) ? true : false;
7001 if( inIsMDNS ) qClass &= ~kQClassUnicastResponseBit;
7002
7003 if( i == 0 ) FPrintF( stdout, "\nQUESTION SECTION\n" );
7004
7005 FPrintF( stdout, "%s %2s %?2s%?2u %-5s\n",
7006 nameStr, inIsMDNS ? ( isQU ? "QU" : "QM" ) : "",
7007 ( qClass == kDNSServiceClass_IN ), "IN", ( qClass != kDNSServiceClass_IN ), qClass,
7008 RecordTypeToString( qType ) );
7009 }
7010
7011 totalRRCount = answerCount + authorityCount + additionalCount;
7012 for( i = 0; i < totalRRCount; ++i )
7013 {
7014 uint16_t type;
7015 uint16_t class;
7016 uint32_t ttl;
7017 const uint8_t * rdataPtr;
7018 size_t rdataLen;
7019 char * rdataStr;
7020 Boolean cacheFlush;
7021 uint8_t name[ kDomainNameLengthMax ];
7022
7023 err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, &ttl, &rdataPtr, &rdataLen, &ptr );
7024 require_noerr( err, exit );
7025
7026 err = DomainNameToString( name, NULL, nameStr, NULL );
7027 require_noerr( err, exit );
7028
7029 cacheFlush = ( inIsMDNS && ( class & kRRClassCacheFlushBit ) ) ? true : false;
7030 if( inIsMDNS ) class &= ~kRRClassCacheFlushBit;
7031
7032 rdataStr = NULL;
7033 if( !inPrintRaw ) DNSRecordDataToString( rdataPtr, rdataLen, type, inMsgPtr, inMsgLen, &rdataStr );
7034 if( !rdataStr )
7035 {
7036 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) rdataLen, INT_MAX );
7037 require_action( rdataStr, exit, err = kNoMemoryErr );
7038 }
7039
7040 if( answerCount && ( i == 0 ) ) FPrintF( stdout, "\nANSWER SECTION\n" );
7041 else if( authorityCount && ( i == answerCount ) ) FPrintF( stdout, "\nAUTHORITY SECTION\n" );
7042 else if( additionalCount && ( i == ( answerCount + authorityCount ) ) ) FPrintF( stdout, "\nADDITIONAL SECTION\n" );
7043
7044 FPrintF( stdout, "%-42s %6u %2s %?2s%?2u %-5s %s\n",
7045 nameStr, ttl, cacheFlush ? "CF" : "",
7046 ( class == kDNSServiceClass_IN ), "IN", ( class != kDNSServiceClass_IN ), class,
7047 RecordTypeToString( type ), rdataStr );
7048 free( rdataStr );
7049 }
7050 FPrintF( stdout, "\n" );
7051 err = kNoErr;
7052
7053 exit:
7054 return( err );
7055 }
7056
7057 //===========================================================================================================================
7058 // WriteDNSQueryMessage
7059 //===========================================================================================================================
7060
7061 static OSStatus
7062 WriteDNSQueryMessage(
7063 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
7064 uint16_t inMsgID,
7065 uint16_t inFlags,
7066 const char * inQName,
7067 uint16_t inQType,
7068 uint16_t inQClass,
7069 size_t * outMsgLen )
7070 {
7071 OSStatus err;
7072 DNSHeader * const hdr = (DNSHeader *) inMsg;
7073 uint8_t * ptr;
7074 size_t msgLen;
7075
7076 WriteBig16( hdr->id, inMsgID );
7077 WriteBig16( hdr->flags, inFlags );
7078 WriteBig16( hdr->questionCount, 1 );
7079 WriteBig16( hdr->answerCount, 0 );
7080 WriteBig16( hdr->authorityCount, 0 );
7081 WriteBig16( hdr->additionalCount, 0 );
7082
7083 ptr = (uint8_t *)( hdr + 1 );
7084 ptr[ 0 ] = 0;
7085 err = DomainNameAppendString( ptr, inQName, &ptr );
7086 require_noerr_quiet( err, exit );
7087
7088 WriteBig16( ptr, inQType );
7089 ptr += 2;
7090 WriteBig16( ptr, inQClass );
7091 ptr += 2;
7092 msgLen = (size_t)( ptr - inMsg );
7093 check( msgLen <= kDNSQueryMessageMaxLen );
7094
7095 if( outMsgLen ) *outMsgLen = msgLen;
7096
7097 exit:
7098 return( err );
7099 }
7100
7101 //===========================================================================================================================
7102 // DispatchSignalSourceCreate
7103 //===========================================================================================================================
7104
7105 static OSStatus
7106 DispatchSignalSourceCreate(
7107 int inSignal,
7108 DispatchHandler inEventHandler,
7109 void * inContext,
7110 dispatch_source_t * outSource )
7111 {
7112 OSStatus err;
7113 dispatch_source_t source;
7114
7115 source = dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) inSignal, 0, dispatch_get_main_queue() );
7116 require_action( source, exit, err = kUnknownErr );
7117
7118 dispatch_set_context( source, inContext );
7119 dispatch_source_set_event_handler_f( source, inEventHandler );
7120
7121 *outSource = source;
7122 err = kNoErr;
7123
7124 exit:
7125 return( err );
7126 }
7127
7128 //===========================================================================================================================
7129 // DispatchReadSourceCreate
7130 //===========================================================================================================================
7131
7132 static OSStatus
7133 DispatchReadSourceCreate(
7134 SocketRef inSock,
7135 DispatchHandler inEventHandler,
7136 DispatchHandler inCancelHandler,
7137 void * inContext,
7138 dispatch_source_t * outSource )
7139 {
7140 OSStatus err;
7141 dispatch_source_t source;
7142
7143 source = dispatch_source_create( DISPATCH_SOURCE_TYPE_READ, (uintptr_t) inSock, 0, dispatch_get_main_queue() );
7144 require_action( source, exit, err = kUnknownErr );
7145
7146 dispatch_set_context( source, inContext );
7147 dispatch_source_set_event_handler_f( source, inEventHandler );
7148 dispatch_source_set_cancel_handler_f( source, inCancelHandler );
7149
7150 *outSource = source;
7151 err = kNoErr;
7152
7153 exit:
7154 return( err );
7155 }
7156
7157 //===========================================================================================================================
7158 // DispatchTimerCreate
7159 //===========================================================================================================================
7160
7161 static OSStatus
7162 DispatchTimerCreate(
7163 dispatch_time_t inStart,
7164 uint64_t inIntervalNs,
7165 uint64_t inLeewayNs,
7166 DispatchHandler inEventHandler,
7167 DispatchHandler inCancelHandler,
7168 void * inContext,
7169 dispatch_source_t * outTimer )
7170 {
7171 OSStatus err;
7172 dispatch_source_t timer;
7173
7174 timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue() );
7175 require_action( timer, exit, err = kUnknownErr );
7176
7177 dispatch_source_set_timer( timer, inStart, inIntervalNs, inLeewayNs );
7178 dispatch_set_context( timer, inContext );
7179 dispatch_source_set_event_handler_f( timer, inEventHandler );
7180 dispatch_source_set_cancel_handler_f( timer, inCancelHandler );
7181
7182 *outTimer = timer;
7183 err = kNoErr;
7184
7185 exit:
7186 return( err );
7187 }
7188
7189 //===========================================================================================================================
7190 // ServiceTypeDescription
7191 //===========================================================================================================================
7192
7193 typedef struct
7194 {
7195 const char * name; // Name of the service type in two-label "_service._proto" format.
7196 const char * description; // Description of the service type.
7197
7198 } ServiceType;
7199
7200 // A Non-comprehensive table of DNS-SD service types
7201
7202 static const ServiceType kServiceTypes[] =
7203 {
7204 { "_acp-sync._tcp", "AirPort Base Station Sync" },
7205 { "_adisk._tcp", "Automatic Disk Discovery" },
7206 { "_afpovertcp._tcp", "Apple File Sharing" },
7207 { "_airdrop._tcp", "AirDrop" },
7208 { "_airplay._tcp", "AirPlay" },
7209 { "_airport._tcp", "AirPort Base Station" },
7210 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
7211 { "_eppc._tcp", "Remote AppleEvents" },
7212 { "_ftp._tcp", "File Transfer Protocol" },
7213 { "_home-sharing._tcp", "Home Sharing" },
7214 { "_homekit._tcp", "HomeKit" },
7215 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
7216 { "_https._tcp", "HTTP over SSL/TLS" },
7217 { "_ipp._tcp", "Internet Printing Protocol" },
7218 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
7219 { "_mediaremotetv._tcp", "Media Remote" },
7220 { "_net-assistant._tcp", "Apple Remote Desktop" },
7221 { "_od-master._tcp", "OpenDirectory Master" },
7222 { "_nfs._tcp", "Network File System" },
7223 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
7224 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
7225 { "_raop._tcp", "Remote Audio Output Protocol" },
7226 { "_rfb._tcp", "Remote Frame Buffer" },
7227 { "_scanner._tcp", "Bonjour Scanning" },
7228 { "_smb._tcp", "Server Message Block over TCP/IP" },
7229 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
7230 { "_sleep-proxy._udp", "Sleep Proxy Server" },
7231 { "_ssh._tcp", "SSH Remote Login Protocol" },
7232 { "_teleport._tcp", "teleport" },
7233 { "_tftp._tcp", "Trivial File Transfer Protocol" },
7234 { "_workstation._tcp", "Workgroup Manager" },
7235 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
7236 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
7237 };
7238
7239 static const char * ServiceTypeDescription( const char *inName )
7240 {
7241 const ServiceType * serviceType;
7242 const ServiceType * const end = kServiceTypes + countof( kServiceTypes );
7243
7244 for( serviceType = kServiceTypes; serviceType < end; ++serviceType )
7245 {
7246 if( strcasecmp( inName, serviceType->name ) == 0 ) return( serviceType->description );
7247 }
7248 return( NULL );
7249 }
7250
7251 //===========================================================================================================================
7252 // SocketContextCancelHandler
7253 //===========================================================================================================================
7254
7255 static void SocketContextCancelHandler( void *inContext )
7256 {
7257 SocketContext * const context = (SocketContext *) inContext;
7258
7259 ForgetSocket( &context->sock );
7260 free( context );
7261 }
7262
7263 //===========================================================================================================================
7264 // StringToInt32
7265 //===========================================================================================================================
7266
7267 static OSStatus StringToInt32( const char *inString, int32_t *outValue )
7268 {
7269 OSStatus err;
7270 long value;
7271 char * endPtr;
7272
7273 value = strtol( inString, &endPtr, 0 );
7274 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
7275 require_action_quiet( ( value >= INT32_MIN ) && ( value <= INT32_MAX ), exit, err = kRangeErr );
7276
7277 *outValue = (int32_t) value;
7278 err = kNoErr;
7279
7280 exit:
7281 return( err );
7282 }
7283
7284 //===========================================================================================================================
7285 // StringToUInt32
7286 //===========================================================================================================================
7287
7288 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue )
7289 {
7290 OSStatus err;
7291 uint32_t value;
7292 char * endPtr;
7293
7294 value = (uint32_t) strtol( inString, &endPtr, 0 );
7295 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
7296
7297 *outValue = value;
7298 err = kNoErr;
7299
7300 exit:
7301 return( err );
7302 }
7303
7304 #if( TARGET_OS_DARWIN )
7305 //===========================================================================================================================
7306 // GetDefaultDNSServer
7307 //===========================================================================================================================
7308
7309 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr )
7310 {
7311 OSStatus err;
7312 dns_config_t * config;
7313 struct sockaddr * addr;
7314 int32_t i;
7315
7316 config = dns_configuration_copy();
7317 require_action( config, exit, err = kUnknownErr );
7318
7319 addr = NULL;
7320 for( i = 0; i < config->n_resolver; ++i )
7321 {
7322 const dns_resolver_t * const resolver = config->resolver[ i ];
7323
7324 if( !resolver->domain && ( resolver->n_nameserver > 0 ) )
7325 {
7326 addr = resolver->nameserver[ 0 ];
7327 break;
7328 }
7329 }
7330 require_action_quiet( addr, exit, err = kNotFoundErr );
7331
7332 SockAddrCopy( addr, outAddr );
7333 err = kNoErr;
7334
7335 exit:
7336 if( config ) dns_configuration_free( config );
7337 return( err );
7338 }
7339 #endif
7340
7341 //===========================================================================================================================
7342 // SocketWriteAll
7343 //
7344 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
7345 //===========================================================================================================================
7346
7347 OSStatus SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs )
7348 {
7349 OSStatus err;
7350 const uint8_t * src;
7351 const uint8_t * end;
7352 fd_set writeSet;
7353 struct timeval timeout;
7354 ssize_t n;
7355
7356 FD_ZERO( &writeSet );
7357 src = (const uint8_t *) inData;
7358 end = src + inSize;
7359 while( src < end )
7360 {
7361 FD_SET( inSock, &writeSet );
7362 timeout.tv_sec = inTimeoutSecs;
7363 timeout.tv_usec = 0;
7364 n = select( (int)( inSock + 1 ), NULL, &writeSet, NULL, &timeout );
7365 if( n == 0 ) { err = kTimeoutErr; goto exit; }
7366 err = map_socket_value_errno( inSock, n > 0, n );
7367 require_noerr( err, exit );
7368
7369 n = send( inSock, (char *) src, (size_t)( end - src ), 0 );
7370 err = map_socket_value_errno( inSock, n >= 0, n );
7371 if( err == EINTR ) continue;
7372 require_noerr( err, exit );
7373
7374 src += n;
7375 }
7376 err = kNoErr;
7377
7378 exit:
7379 return( err );
7380 }
7381
7382 //===========================================================================================================================
7383 // ParseEscapedString
7384 //
7385 // Note: This was copied from CoreUtils because the ParseEscapedString function is currently not exported in the framework.
7386 //===========================================================================================================================
7387
7388 OSStatus
7389 ParseEscapedString(
7390 const char * inSrc,
7391 const char * inEnd,
7392 char inDelimiter,
7393 char * inBuf,
7394 size_t inMaxLen,
7395 size_t * outCopiedLen,
7396 size_t * outTotalLen,
7397 const char ** outSrc )
7398 {
7399 OSStatus err;
7400 char c;
7401 char * dst;
7402 char * lim;
7403 size_t len;
7404
7405 dst = inBuf;
7406 lim = dst + ( ( inMaxLen > 0 ) ? ( inMaxLen - 1 ) : 0 ); // Leave room for null terminator.
7407 len = 0;
7408 while( ( inSrc < inEnd ) && ( ( c = *inSrc++ ) != inDelimiter ) )
7409 {
7410 if( c == '\\' )
7411 {
7412 require_action_quiet( inSrc < inEnd, exit, err = kUnderrunErr );
7413 c = *inSrc++;
7414 }
7415 if( dst < lim )
7416 {
7417 if( inBuf ) *dst = c;
7418 ++dst;
7419 }
7420 ++len;
7421 }
7422 if( inBuf && ( inMaxLen > 0 ) ) *dst = '\0';
7423 err = kNoErr;
7424
7425 exit:
7426 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - inBuf );
7427 if( outTotalLen ) *outTotalLen = len;
7428 if( outSrc ) *outSrc = inSrc;
7429 return( err );
7430 }
7431
7432 //===========================================================================================================================
7433 // ParseIPv4Address
7434 //
7435 // Warning: "inBuffer" may be modified even in error cases.
7436 //
7437 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
7438 //===========================================================================================================================
7439
7440 static OSStatus ParseIPv4Address( const char *inStr, uint8_t inBuffer[ 4 ], const char **outStr )
7441 {
7442 OSStatus err;
7443 uint8_t * dst;
7444 int segments;
7445 int sawDigit;
7446 int c;
7447 int v;
7448
7449 check( inBuffer );
7450 check( outStr );
7451
7452 dst = inBuffer;
7453 *dst = 0;
7454 sawDigit = 0;
7455 segments = 0;
7456 for( ; ( c = *inStr ) != '\0'; ++inStr )
7457 {
7458 if( isdigit_safe( c ) )
7459 {
7460 v = ( *dst * 10 ) + ( c - '0' );
7461 require_action_quiet( v <= 255, exit, err = kRangeErr );
7462 *dst = (uint8_t) v;
7463 if( !sawDigit )
7464 {
7465 ++segments;
7466 require_action_quiet( segments <= 4, exit, err = kOverrunErr );
7467 sawDigit = 1;
7468 }
7469 }
7470 else if( ( c == '.' ) && sawDigit )
7471 {
7472 require_action_quiet( segments < 4, exit, err = kMalformedErr );
7473 *++dst = 0;
7474 sawDigit = 0;
7475 }
7476 else
7477 {
7478 break;
7479 }
7480 }
7481 require_action_quiet( segments == 4, exit, err = kUnderrunErr );
7482
7483 *outStr = inStr;
7484 err = kNoErr;
7485
7486 exit:
7487 return( err );
7488 }
7489
7490 //===========================================================================================================================
7491 // StringToIPv4Address
7492 //
7493 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
7494 //===========================================================================================================================
7495
7496 OSStatus
7497 StringToIPv4Address(
7498 const char * inStr,
7499 StringToIPAddressFlags inFlags,
7500 uint32_t * outIP,
7501 int * outPort,
7502 uint32_t * outSubnet,
7503 uint32_t * outRouter,
7504 const char ** outStr )
7505 {
7506 OSStatus err;
7507 uint8_t buf[ 4 ];
7508 int c;
7509 uint32_t ip;
7510 int hasPort;
7511 int port;
7512 int hasPrefix;
7513 int prefix;
7514 uint32_t subnetMask;
7515 uint32_t router;
7516
7517 require_action( inStr, exit, err = kParamErr );
7518
7519 // Parse the address-only part of the address (e.g. "1.2.3.4").
7520
7521 err = ParseIPv4Address( inStr, buf, &inStr );
7522 require_noerr_quiet( err, exit );
7523 ip = (uint32_t)( ( buf[ 0 ] << 24 ) | ( buf[ 1 ] << 16 ) | ( buf[ 2 ] << 8 ) | buf[ 3 ] );
7524 c = *inStr;
7525
7526 // Parse the port (if any).
7527
7528 hasPort = 0;
7529 port = 0;
7530 if( c == ':' )
7531 {
7532 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
7533 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
7534 require_action_quiet( port <= 65535, exit, err = kRangeErr );
7535 hasPort = 1;
7536 }
7537
7538 // Parse the prefix length (if any).
7539
7540 hasPrefix = 0;
7541 prefix = 0;
7542 subnetMask = 0;
7543 router = 0;
7544 if( c == '/' )
7545 {
7546 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
7547 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
7548 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 32 ), exit, err = kRangeErr );
7549 hasPrefix = 1;
7550
7551 subnetMask = ( prefix > 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix ) ) : 0;
7552 router = ( ip & subnetMask ) | 1;
7553 }
7554
7555 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
7556
7557 if( outIP ) *outIP = ip;
7558 if( outPort && hasPort ) *outPort = port;
7559 if( outSubnet && hasPrefix ) *outSubnet = subnetMask;
7560 if( outRouter && hasPrefix ) *outRouter = router;
7561 if( outStr ) *outStr = inStr;
7562 err = kNoErr;
7563
7564 exit:
7565 return( err );
7566 }
7567
7568 //===========================================================================================================================
7569 // ParseIPv6Address
7570 //
7571 // Note: Parsed according to the rules specified in RFC 3513.
7572 // Warning: "inBuffer" may be modified even in error cases.
7573 //
7574 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
7575 //===========================================================================================================================
7576
7577 static OSStatus ParseIPv6Address( const char *inStr, int inAllowV4Mapped, uint8_t inBuffer[ 16 ], const char **outStr )
7578 {
7579 // Table to map uppercase hex characters - '0' to their numeric values.
7580 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
7581 static const uint8_t kASCIItoHexTable[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15 };
7582 OSStatus err;
7583 const char * ptr;
7584 uint8_t * dst;
7585 uint8_t * lim;
7586 uint8_t * colonPtr;
7587 int c;
7588 int sawDigit;
7589 unsigned int v;
7590 int i;
7591 int n;
7592
7593 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
7594
7595 for( i = 0; i < 16; ++i ) inBuffer[ i ] = 0;
7596
7597 // Special case leading :: (e.g. "::1") to simplify processing later.
7598
7599 if( *inStr == ':' )
7600 {
7601 ++inStr;
7602 require_action_quiet( *inStr == ':', exit, err = kMalformedErr );
7603 }
7604
7605 // Parse the address.
7606
7607 ptr = inStr;
7608 dst = inBuffer;
7609 lim = dst + 16;
7610 colonPtr = NULL;
7611 sawDigit = 0;
7612 v = 0;
7613 while( ( ( c = *inStr++ ) != '\0' ) && ( c != '%' ) && ( c != '/' ) && ( c != ']' ) )
7614 {
7615 if( ( c >= 'a' ) && ( c <= 'f' ) ) c -= ( 'a' - 'A' );
7616 if( ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c >= 'A' ) && ( c <= 'F' ) ) )
7617 {
7618 c -= '0';
7619 check( c < (int) countof( kASCIItoHexTable ) );
7620 v = ( v << 4 ) | kASCIItoHexTable[ c ];
7621 require_action_quiet( v <= 0xFFFF, exit, err = kRangeErr );
7622 sawDigit = 1;
7623 continue;
7624 }
7625 if( c == ':' )
7626 {
7627 ptr = inStr;
7628 if( !sawDigit )
7629 {
7630 require_action_quiet( !colonPtr, exit, err = kMalformedErr );
7631 colonPtr = dst;
7632 continue;
7633 }
7634 require_action_quiet( *inStr != '\0', exit, err = kUnderrunErr );
7635 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
7636 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
7637 *dst++ = (uint8_t)( v & 0xFF );
7638 sawDigit = 0;
7639 v = 0;
7640 continue;
7641 }
7642
7643 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
7644
7645 if( inAllowV4Mapped && ( c == '.' ) && ( ( dst + 4 ) <= lim ) )
7646 {
7647 err = ParseIPv4Address( ptr, dst, &inStr );
7648 require_noerr_quiet( err, exit );
7649 dst += 4;
7650 sawDigit = 0;
7651 ++inStr; // Increment because the code below expects the end to be at "inStr - 1".
7652 }
7653 break;
7654 }
7655 if( sawDigit )
7656 {
7657 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
7658 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
7659 *dst++ = (uint8_t)( v & 0xFF );
7660 }
7661 check( dst <= lim );
7662 if( colonPtr )
7663 {
7664 require_action_quiet( dst < lim, exit, err = kOverrunErr );
7665 n = (int)( dst - colonPtr );
7666 for( i = 1; i <= n; ++i )
7667 {
7668 lim[ -i ] = colonPtr[ n - i ];
7669 colonPtr[ n - i ] = 0;
7670 }
7671 dst = lim;
7672 }
7673 require_action_quiet( dst == lim, exit, err = kUnderrunErr );
7674
7675 *outStr = inStr - 1;
7676 err = kNoErr;
7677
7678 exit:
7679 return( err );
7680 }
7681
7682 //===========================================================================================================================
7683 // ParseIPv6Scope
7684 //
7685 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
7686 //===========================================================================================================================
7687
7688 static OSStatus ParseIPv6Scope( const char *inStr, uint32_t *outScope, const char **outStr )
7689 {
7690 #if( TARGET_OS_POSIX )
7691 OSStatus err;
7692 char scopeStr[ 64 ];
7693 char * dst;
7694 char * lim;
7695 int c;
7696 uint32_t scope;
7697 const char * ptr;
7698
7699 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
7700
7701 dst = scopeStr;
7702 lim = dst + ( countof( scopeStr ) - 1 );
7703 while( ( ( c = *inStr ) != '\0' ) && ( c != ':' ) && ( c != '/' ) && ( c != ']' ) && ( dst < lim ) )
7704 {
7705 *dst++ = *inStr++;
7706 }
7707 *dst = '\0';
7708 check( dst <= lim );
7709
7710 // First try to map as a name and if that fails, treat it as a numeric scope.
7711
7712 scope = if_nametoindex( scopeStr );
7713 if( scope == 0 )
7714 {
7715 for( ptr = scopeStr; ( ( c = *ptr ) >= '0' ) && ( c <= '9' ); ++ptr )
7716 {
7717 scope = ( scope * 10 ) + ( ( (uint8_t) c ) - '0' );
7718 }
7719 require_action_quiet( c == '\0', exit, err = kMalformedErr );
7720 require_action_quiet( ( ptr != scopeStr ) && ( ( (int)( ptr - scopeStr ) ) <= 10 ), exit, err = kMalformedErr );
7721 }
7722
7723 *outScope = scope;
7724 *outStr = inStr;
7725 err = kNoErr;
7726
7727 exit:
7728 return( err );
7729 #else
7730 OSStatus err;
7731 uint32_t scope;
7732 const char * start;
7733 int c;
7734
7735 scope = 0;
7736 for( start = inStr; ( ( c = *inStr ) >= '0' ) && ( c <= '9' ); ++inStr )
7737 {
7738 scope = ( scope * 10 ) + ( c - '0' );
7739 }
7740 require_action_quiet( ( inStr != start ) && ( ( (int)( inStr - start ) ) <= 10 ), exit, err = kMalformedErr );
7741
7742 *outScope = scope;
7743 *outStr = inStr;
7744 err = kNoErr;
7745
7746 exit:
7747 return( err );
7748 #endif
7749 }
7750
7751 //===========================================================================================================================
7752 // StringToIPv6Address
7753 //
7754 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
7755 //===========================================================================================================================
7756
7757 OSStatus
7758 StringToIPv6Address(
7759 const char * inStr,
7760 StringToIPAddressFlags inFlags,
7761 uint8_t outIPv6[ 16 ],
7762 uint32_t * outScope,
7763 int * outPort,
7764 int * outPrefix,
7765 const char ** outStr )
7766 {
7767 OSStatus err;
7768 uint8_t ipv6[ 16 ];
7769 int c;
7770 int hasScope;
7771 uint32_t scope;
7772 int hasPort;
7773 int port;
7774 int hasPrefix;
7775 int prefix;
7776 int hasBracket;
7777 int i;
7778
7779 require_action( inStr, exit, err = kParamErr );
7780
7781 if( *inStr == '[' ) ++inStr; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
7782
7783 // Parse the address-only part of the address (e.g. "1::1").
7784
7785 err = ParseIPv6Address( inStr, !( inFlags & kStringToIPAddressFlagsNoIPv4Mapped ), ipv6, &inStr );
7786 require_noerr_quiet( err, exit );
7787 c = *inStr;
7788
7789 // Parse the scope, port, or prefix length.
7790
7791 hasScope = 0;
7792 scope = 0;
7793 hasPort = 0;
7794 port = 0;
7795 hasPrefix = 0;
7796 prefix = 0;
7797 hasBracket = 0;
7798 for( ;; )
7799 {
7800 if( c == '%' ) // Scope (e.g. "%en0" or "%5")
7801 {
7802 require_action_quiet( !hasScope, exit, err = kMalformedErr );
7803 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoScope ), exit, err = kUnexpectedErr );
7804 ++inStr;
7805 err = ParseIPv6Scope( inStr, &scope, &inStr );
7806 require_noerr_quiet( err, exit );
7807 hasScope = 1;
7808 c = *inStr;
7809 }
7810 else if( c == ':' ) // Port (e.g. ":80")
7811 {
7812 require_action_quiet( !hasPort, exit, err = kMalformedErr );
7813 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
7814 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
7815 require_action_quiet( port <= 65535, exit, err = kRangeErr );
7816 hasPort = 1;
7817 }
7818 else if( c == '/' ) // Prefix Length (e.g. "/64")
7819 {
7820 require_action_quiet( !hasPrefix, exit, err = kMalformedErr );
7821 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
7822 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
7823 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 128 ), exit, err = kRangeErr );
7824 hasPrefix = 1;
7825 }
7826 else if( c == ']' )
7827 {
7828 require_action_quiet( !hasBracket, exit, err = kMalformedErr );
7829 hasBracket = 1;
7830 c = *( ++inStr );
7831 }
7832 else
7833 {
7834 break;
7835 }
7836 }
7837
7838 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
7839
7840 if( outIPv6 ) for( i = 0; i < 16; ++i ) outIPv6[ i ] = ipv6[ i ];
7841 if( outScope && hasScope ) *outScope = scope;
7842 if( outPort && hasPort ) *outPort = port;
7843 if( outPrefix && hasPrefix ) *outPrefix = prefix;
7844 if( outStr ) *outStr = inStr;
7845 err = kNoErr;
7846
7847 exit:
7848 return( err );
7849 }
7850
7851 //===========================================================================================================================
7852 // StringArray_Append
7853 //
7854 // Note: This was copied from CoreUtils because the StringArray_Append function is currently not exported in the framework.
7855 //===========================================================================================================================
7856
7857 OSStatus StringArray_Append( char ***ioArray, size_t *ioCount, const char *inStr )
7858 {
7859 OSStatus err;
7860 char * newStr;
7861 size_t oldCount;
7862 size_t newCount;
7863 char ** oldArray;
7864 char ** newArray;
7865
7866 newStr = strdup( inStr );
7867 require_action( newStr, exit, err = kNoMemoryErr );
7868
7869 oldCount = *ioCount;
7870 newCount = oldCount + 1;
7871 newArray = (char **) malloc( newCount * sizeof( *newArray ) );
7872 require_action( newArray, exit, err = kNoMemoryErr );
7873
7874 if( oldCount > 0 )
7875 {
7876 oldArray = *ioArray;
7877 memcpy( newArray, oldArray, oldCount * sizeof( *oldArray ) );
7878 free( oldArray );
7879 }
7880 newArray[ oldCount ] = newStr;
7881 newStr = NULL;
7882
7883 *ioArray = newArray;
7884 *ioCount = newCount;
7885 err = kNoErr;
7886
7887 exit:
7888 if( newStr ) free( newStr );
7889 return( err );
7890 }
7891
7892 //===========================================================================================================================
7893 // StringArray_Free
7894 //
7895 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
7896 //===========================================================================================================================
7897
7898 void StringArray_Free( char **inArray, size_t inCount )
7899 {
7900 size_t i;
7901
7902 for( i = 0; i < inCount; ++i )
7903 {
7904 free( inArray[ i ] );
7905 }
7906 if( inCount > 0 ) free( inArray );
7907 }
7908
7909 //===========================================================================================================================
7910 // ParseQuotedEscapedString
7911 //
7912 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
7913 //===========================================================================================================================
7914
7915 Boolean
7916 ParseQuotedEscapedString(
7917 const char * inSrc,
7918 const char * inEnd,
7919 const char * inDelimiters,
7920 char * inBuf,
7921 size_t inMaxLen,
7922 size_t * outCopiedLen,
7923 size_t * outTotalLen,
7924 const char ** outSrc )
7925 {
7926 const unsigned char * src;
7927 const unsigned char * end;
7928 unsigned char * dst;
7929 unsigned char * lim;
7930 unsigned char c;
7931 unsigned char c2;
7932 size_t totalLen;
7933 Boolean singleQuote;
7934 Boolean doubleQuote;
7935
7936 if( inEnd == NULL ) inEnd = inSrc + strlen( inSrc );
7937 src = (const unsigned char *) inSrc;
7938 end = (const unsigned char *) inEnd;
7939 dst = (unsigned char *) inBuf;
7940 lim = dst + inMaxLen;
7941 while( ( src < end ) && isspace_safe( *src ) ) ++src; // Skip leading spaces.
7942 if( src >= end ) return( false );
7943
7944 // Parse each argument from the string.
7945 //
7946 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
7947
7948 totalLen = 0;
7949 singleQuote = false;
7950 doubleQuote = false;
7951 while( src < end )
7952 {
7953 c = *src++;
7954 if( singleQuote )
7955 {
7956 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
7957
7958 if( c == '\'' )
7959 {
7960 singleQuote = false;
7961 continue;
7962 }
7963 }
7964 else if( doubleQuote )
7965 {
7966 // Double quotes protect everything except double quotes and backslashes. A backslash can be
7967 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
7968 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
7969 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
7970 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
7971
7972 if( c == '"' )
7973 {
7974 doubleQuote = false;
7975 continue;
7976 }
7977 else if( c == '\\' )
7978 {
7979 if( src < end )
7980 {
7981 c2 = *src;
7982 if( ( c2 == '"' ) || ( c2 == '\\' ) )
7983 {
7984 ++src;
7985 c = c2;
7986 }
7987 else if( c2 == '\n' )
7988 {
7989 ++src;
7990 continue;
7991 }
7992 else if( ( c2 == 'x' ) || ( c2 == 'X' ) )
7993 {
7994 ++src;
7995 c = c2;
7996 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
7997 {
7998 c = HexPairToByte( src );
7999 src += 2;
8000 }
8001 }
8002 else if( isoctal_safe( c2 ) )
8003 {
8004 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
8005 {
8006 c = OctalTripleToByte( src );
8007 src += 3;
8008 }
8009 }
8010 }
8011 }
8012 }
8013 else if( strchr( inDelimiters, c ) )
8014 {
8015 break;
8016 }
8017 else if( c == '\\' )
8018 {
8019 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
8020 // A backslash followed by a newline disappears completely.
8021 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
8022 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
8023
8024 if( src < end )
8025 {
8026 c = *src;
8027 if( c == '\n' )
8028 {
8029 ++src;
8030 continue;
8031 }
8032 else if( ( c == 'x' ) || ( c == 'X' ) )
8033 {
8034 ++src;
8035 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
8036 {
8037 c = HexPairToByte( src );
8038 src += 2;
8039 }
8040 }
8041 else if( isoctal_safe( c ) )
8042 {
8043 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
8044 {
8045 c = OctalTripleToByte( src );
8046 src += 3;
8047 }
8048 else
8049 {
8050 ++src;
8051 }
8052 }
8053 else
8054 {
8055 ++src;
8056 }
8057 }
8058 }
8059 else if( c == '\'' )
8060 {
8061 singleQuote = true;
8062 continue;
8063 }
8064 else if( c == '"' )
8065 {
8066 doubleQuote = true;
8067 continue;
8068 }
8069
8070 if( dst < lim )
8071 {
8072 if( inBuf ) *dst = c;
8073 ++dst;
8074 }
8075 ++totalLen;
8076 }
8077
8078 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - ( (unsigned char *) inBuf ) );
8079 if( outTotalLen ) *outTotalLen = totalLen;
8080 if( outSrc ) *outSrc = (const char *) src;
8081 return( true );
8082 }