]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/dnssdutil.c
mDNSResponder-878.50.17.tar.gz
[apple/mdnsresponder.git] / Clients / dnssdutil.c
1 /*
2 Copyright (c) 2016-2018 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/HTTPUtils.h>
13 #include <CoreUtils/MiscUtils.h>
14 #include <CoreUtils/NetUtils.h>
15 #include <CoreUtils/PrintFUtils.h>
16 #include <CoreUtils/RandomNumberUtils.h>
17 #include <CoreUtils/SoftLinking.h>
18 #include <CoreUtils/StringUtils.h>
19 #include <CoreUtils/TickUtils.h>
20 #include <dns_sd.h>
21 #include <dns_sd_private.h>
22
23 #if( TARGET_OS_DARWIN )
24 #include <dnsinfo.h>
25 #include <libproc.h>
26 #include <netdb.h>
27 #include <sys/proc_info.h>
28 #endif
29
30 #if( TARGET_OS_POSIX )
31 #include <sys/resource.h>
32 #endif
33
34 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
35 #include "tweetnacl.h" // TweetNaCl from <https://tweetnacl.cr.yp.to/software.html>.
36 #endif
37
38 //===========================================================================================================================
39 // Global Constants
40 //===========================================================================================================================
41
42 // Versioning
43
44 #define kDNSSDUtilNumVersion NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 )
45
46 #if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) )
47 #define DNSSDUTIL_SOURCE_VERSION "0.0.0"
48 #endif
49
50 // DNS-SD API flag descriptors
51
52 #define kDNSServiceFlagsDescriptors \
53 "\x00" "AutoTrigger\0" \
54 "\x01" "Add\0" \
55 "\x02" "Default\0" \
56 "\x03" "NoAutoRename\0" \
57 "\x04" "Shared\0" \
58 "\x05" "Unique\0" \
59 "\x06" "BrowseDomains\0" \
60 "\x07" "RegistrationDomains\0" \
61 "\x08" "LongLivedQuery\0" \
62 "\x09" "AllowRemoteQuery\0" \
63 "\x0A" "ForceMulticast\0" \
64 "\x0B" "KnownUnique\0" \
65 "\x0C" "ReturnIntermediates\0" \
66 "\x0D" "NonBrowsable\0" \
67 "\x0E" "ShareConnection\0" \
68 "\x0F" "SuppressUnusable\0" \
69 "\x10" "Timeout\0" \
70 "\x11" "IncludeP2P\0" \
71 "\x12" "WakeOnResolve\0" \
72 "\x13" "BackgroundTrafficClass\0" \
73 "\x14" "IncludeAWDL\0" \
74 "\x15" "Validate\0" \
75 "\x16" "UnicastResponse\0" \
76 "\x17" "ValidateOptional\0" \
77 "\x18" "WakeOnlyService\0" \
78 "\x19" "ThresholdOne\0" \
79 "\x1A" "ThresholdFinder\0" \
80 "\x1B" "DenyCellular\0" \
81 "\x1C" "ServiceIndex\0" \
82 "\x1D" "DenyExpensive\0" \
83 "\x1E" "PathEvaluationDone\0" \
84 "\x00"
85
86 #define kDNSServiceProtocolDescriptors \
87 "\x00" "IPv4\0" \
88 "\x01" "IPv6\0" \
89 "\x04" "UDP\0" \
90 "\x05" "TCP\0" \
91 "\x00"
92
93 // (m)DNS
94
95 #define kDNSHeaderFlag_Response ( 1 << 15 )
96 #define kDNSHeaderFlag_AuthAnswer ( 1 << 10 )
97 #define kDNSHeaderFlag_Truncation ( 1 << 9 )
98 #define kDNSHeaderFlag_RecursionDesired ( 1 << 8 )
99 #define kDNSHeaderFlag_RecursionAvailable ( 1 << 7 )
100
101 #define kDNSOpCode_Query 0
102 #define kDNSOpCode_InverseQuery 1
103 #define kDNSOpCode_Status 2
104 #define kDNSOpCode_Notify 4
105 #define kDNSOpCode_Update 5
106
107 #define kDNSRCode_NoError 0
108 #define kDNSRCode_FormatError 1
109 #define kDNSRCode_ServerFailure 2
110 #define kDNSRCode_NXDomain 3
111 #define kDNSRCode_NotImplemented 4
112 #define kDNSRCode_Refused 5
113
114 #define kQClassUnicastResponseBit ( 1U << 15 )
115 #define kRRClassCacheFlushBit ( 1U << 15 )
116
117 #define kDomainLabelLengthMax 63
118 #define kDomainNameLengthMax 256
119
120 //===========================================================================================================================
121 // Gerneral Command Options
122 //===========================================================================================================================
123
124 // Command option macros
125
126 #define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON ) \
127 CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None, \
128 (SHORT_HELP), NULL )
129
130 #define kRequiredOptionSuffix " [REQUIRED]"
131
132 #define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
133 CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, \
134 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
135 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
136
137 #define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
138 MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
139
140 #define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
141 CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
142 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
143 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
144
145 #define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \
146 CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL )
147
148 #define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP ) \
149 CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \
150 (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \
151 (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
152
153 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
154 StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
155
156 // DNS-SD API flag options
157
158 static int gDNSSDFlags = 0;
159 static int gDNSSDFlag_BrowseDomains = false;
160 static int gDNSSDFlag_DenyCellular = false;
161 static int gDNSSDFlag_DenyExpensive = false;
162 static int gDNSSDFlag_ForceMulticast = false;
163 static int gDNSSDFlag_IncludeAWDL = false;
164 static int gDNSSDFlag_NoAutoRename = false;
165 static int gDNSSDFlag_PathEvaluationDone = false;
166 static int gDNSSDFlag_RegistrationDomains = false;
167 static int gDNSSDFlag_ReturnIntermediates = false;
168 static int gDNSSDFlag_Shared = false;
169 static int gDNSSDFlag_SuppressUnusable = false;
170 static int gDNSSDFlag_Timeout = false;
171 static int gDNSSDFlag_UnicastResponse = false;
172 static int gDNSSDFlag_Unique = false;
173 static int gDNSSDFlag_WakeOnResolve = false;
174
175 #define DNSSDFlagsOption() \
176 IntegerOption( 'f', "flags", &gDNSSDFlags, "flags", \
177 "DNSServiceFlags as an integer. This value is bitwise ORed with other single flag options.", false )
178
179 #define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \
180 BooleanOption( SHORT_CHAR, # FLAG_NAME, &gDNSSDFlag_ ## FLAG_NAME, "Use kDNSServiceFlags" # FLAG_NAME "." )
181
182 #define DNSSDFlagsOption_DenyCellular() DNSSDFlagOption( 'C', DenyCellular )
183 #define DNSSDFlagsOption_DenyExpensive() DNSSDFlagOption( 'E', DenyExpensive )
184 #define DNSSDFlagsOption_ForceMulticast() DNSSDFlagOption( 'M', ForceMulticast )
185 #define DNSSDFlagsOption_IncludeAWDL() DNSSDFlagOption( 'A', IncludeAWDL )
186 #define DNSSDFlagsOption_NoAutoRename() DNSSDFlagOption( 'N', NoAutoRename )
187 #define DNSSDFlagsOption_PathEvalDone() DNSSDFlagOption( 'P', PathEvaluationDone )
188 #define DNSSDFlagsOption_ReturnIntermediates() DNSSDFlagOption( 'I', ReturnIntermediates )
189 #define DNSSDFlagsOption_Shared() DNSSDFlagOption( 'S', Shared )
190 #define DNSSDFlagsOption_SuppressUnusable() DNSSDFlagOption( 'S', SuppressUnusable )
191 #define DNSSDFlagsOption_Timeout() DNSSDFlagOption( 'T', Timeout )
192 #define DNSSDFlagsOption_UnicastResponse() DNSSDFlagOption( 'U', UnicastResponse )
193 #define DNSSDFlagsOption_Unique() DNSSDFlagOption( 'U', Unique )
194 #define DNSSDFlagsOption_WakeOnResolve() DNSSDFlagOption( 'W', WakeOnResolve )
195
196 // Interface option
197
198 static const char * gInterface = NULL;
199
200 #define InterfaceOption() \
201 StringOption( 'i', "interface", &gInterface, "interface", \
202 "Network interface by name or index. Use index -1 for local-only.", false )
203
204 // Connection options
205
206 #define kConnectionArg_Normal ""
207 #define kConnectionArgPrefix_PID "pid:"
208 #define kConnectionArgPrefix_UUID "uuid:"
209
210 static const char * gConnectionOpt = kConnectionArg_Normal;
211
212 #define ConnectionOptions() \
213 { kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type", \
214 kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL, \
215 "Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL }
216
217 #define kConnectionSection_Name "Connection Option"
218 #define kConnectionSection_Text \
219 "The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n" \
220 "the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n" \
221 "specifying the connection option without an argument, i.e.,\n" \
222 "\n" \
223 " --connection\n" \
224 "\n" \
225 "To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n" \
226 "\n" \
227 " --connection=pid:<PID>\n" \
228 "\n" \
229 "to specify the delegator by PID, or use\n" \
230 "\n" \
231 " --connection=uuid:<UUID>\n" \
232 "\n" \
233 "to specify the delegator by UUID.\n" \
234 "\n" \
235 "To not use a main connection at all, but instead perform operations on their own implicit connections, use\n" \
236 "\n" \
237 " --no-connection\n"
238
239 #define ConnectionSection() CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
240
241 // Help text for record data options
242
243 #define kRDataArgPrefix_Domain "domain:"
244 #define kRDataArgPrefix_File "file:"
245 #define kRDataArgPrefix_HexString "hex:"
246 #define kRDataArgPrefix_IPv4 "ipv4:"
247 #define kRDataArgPrefix_IPv6 "ipv6:"
248 #define kRDataArgPrefix_SRV "srv:"
249 #define kRDataArgPrefix_String "string:"
250 #define kRDataArgPrefix_TXT "txt:"
251
252 #define kRecordDataSection_Name "Record Data Arguments"
253 #define kRecordDataSection_Text \
254 "A record data argument is specified in one of the following formats:\n" \
255 "\n" \
256 "Format Syntax Example\n" \
257 "Domain name domain:<domain name> domain:demo._test._tcp.local\n" \
258 "File containing record data file:<file path> file:/path/to/rdata.bin\n" \
259 "Hexadecimal string hex:<hex string> hex:c0000201 or hex:'C0 00 02 01'\n" \
260 "IPv4 address ipv4:<IPv4 address> ipv4:192.0.2.1\n" \
261 "IPv6 address ipv6:<IPv6 address> ipv6:2001:db8::1\n" \
262 "SRV record srv:<priority>,<weight>,<port>,<target> srv:0,0,64206,example.local\n" \
263 "String (w/escaped hex bytes) string:<string> string:'\\x09color=red'\n" \
264 "TXT record keys and values txt:<comma-delimited keys and values> txt:'vers=1.0,lang=en\\,es\\,fr,passreq'\n"
265
266 #define RecordDataSection() CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text )
267
268 //===========================================================================================================================
269 // Browse Command Options
270 //===========================================================================================================================
271
272 static char ** gBrowse_ServiceTypes = NULL;
273 static size_t gBrowse_ServiceTypesCount = 0;
274 static const char * gBrowse_Domain = NULL;
275 static int gBrowse_DoResolve = false;
276 static int gBrowse_QueryTXT = false;
277 static int gBrowse_TimeLimitSecs = 0;
278
279 static CLIOption kBrowseOpts[] =
280 {
281 InterfaceOption(),
282 MultiStringOption( 't', "type", &gBrowse_ServiceTypes, &gBrowse_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ),
283 StringOption( 'd', "domain", &gBrowse_Domain, "domain", "Domain in which to browse for the service type(s).", false ),
284
285 CLI_OPTION_GROUP( "Flags" ),
286 DNSSDFlagsOption(),
287 DNSSDFlagsOption_IncludeAWDL(),
288
289 CLI_OPTION_GROUP( "Operation" ),
290 ConnectionOptions(),
291 BooleanOption( 0 , "resolve", &gBrowse_DoResolve, "Resolve service instances." ),
292 BooleanOption( 0 , "queryTXT", &gBrowse_QueryTXT, "Query TXT records of service instances." ),
293 IntegerOption( 'l', "timeLimit", &gBrowse_TimeLimitSecs, "seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ),
294
295 ConnectionSection(),
296 CLI_OPTION_END()
297 };
298
299 //===========================================================================================================================
300 // GetAddrInfo Command Options
301 //===========================================================================================================================
302
303 static const char * gGetAddrInfo_Name = NULL;
304 static int gGetAddrInfo_ProtocolIPv4 = false;
305 static int gGetAddrInfo_ProtocolIPv6 = false;
306 static int gGetAddrInfo_OneShot = false;
307 static int gGetAddrInfo_TimeLimitSecs = 0;
308
309 static CLIOption kGetAddrInfoOpts[] =
310 {
311 InterfaceOption(),
312 StringOption( 'n', "name", &gGetAddrInfo_Name, "domain name", "Domain name to resolve.", true ),
313 BooleanOption( 0 , "ipv4", &gGetAddrInfo_ProtocolIPv4, "Use kDNSServiceProtocol_IPv4." ),
314 BooleanOption( 0 , "ipv6", &gGetAddrInfo_ProtocolIPv6, "Use kDNSServiceProtocol_IPv6." ),
315
316 CLI_OPTION_GROUP( "Flags" ),
317 DNSSDFlagsOption(),
318 DNSSDFlagsOption_DenyCellular(),
319 DNSSDFlagsOption_DenyExpensive(),
320 DNSSDFlagsOption_PathEvalDone(),
321 DNSSDFlagsOption_ReturnIntermediates(),
322 DNSSDFlagsOption_SuppressUnusable(),
323 DNSSDFlagsOption_Timeout(),
324
325 CLI_OPTION_GROUP( "Operation" ),
326 ConnectionOptions(),
327 BooleanOption( 'o', "oneshot", &gGetAddrInfo_OneShot, "Finish after first set of results." ),
328 IntegerOption( 'l', "timeLimit", &gGetAddrInfo_TimeLimitSecs, "seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ),
329
330 ConnectionSection(),
331 CLI_OPTION_END()
332 };
333
334 //===========================================================================================================================
335 // QueryRecord Command Options
336 //===========================================================================================================================
337
338 static const char * gQueryRecord_Name = NULL;
339 static const char * gQueryRecord_Type = NULL;
340 static int gQueryRecord_OneShot = false;
341 static int gQueryRecord_TimeLimitSecs = 0;
342 static int gQueryRecord_RawRData = false;
343
344 static CLIOption kQueryRecordOpts[] =
345 {
346 InterfaceOption(),
347 StringOption( 'n', "name", &gQueryRecord_Name, "domain name", "Full domain name of record to query.", true ),
348 StringOption( 't', "type", &gQueryRecord_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
349
350 CLI_OPTION_GROUP( "Flags" ),
351 DNSSDFlagsOption(),
352 DNSSDFlagsOption_IncludeAWDL(),
353 DNSSDFlagsOption_ForceMulticast(),
354 DNSSDFlagsOption_Timeout(),
355 DNSSDFlagsOption_ReturnIntermediates(),
356 DNSSDFlagsOption_SuppressUnusable(),
357 DNSSDFlagsOption_UnicastResponse(),
358 DNSSDFlagsOption_DenyCellular(),
359 DNSSDFlagsOption_DenyExpensive(),
360 DNSSDFlagsOption_PathEvalDone(),
361
362 CLI_OPTION_GROUP( "Operation" ),
363 ConnectionOptions(),
364 BooleanOption( 'o', "oneshot", &gQueryRecord_OneShot, "Finish after first set of results." ),
365 IntegerOption( 'l', "timeLimit", &gQueryRecord_TimeLimitSecs, "seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ),
366 BooleanOption( 0 , "raw", &gQueryRecord_RawRData, "Show record data as a hexdump." ),
367
368 ConnectionSection(),
369 CLI_OPTION_END()
370 };
371
372 //===========================================================================================================================
373 // Register Command Options
374 //===========================================================================================================================
375
376 static const char * gRegister_Name = NULL;
377 static const char * gRegister_Type = NULL;
378 static const char * gRegister_Domain = NULL;
379 static int gRegister_Port = 0;
380 static const char * gRegister_TXT = NULL;
381 static int gRegister_LifetimeMs = -1;
382 static const char ** gAddRecord_Types = NULL;
383 static size_t gAddRecord_TypesCount = 0;
384 static const char ** gAddRecord_Data = NULL;
385 static size_t gAddRecord_DataCount = 0;
386 static const char ** gAddRecord_TTLs = NULL;
387 static size_t gAddRecord_TTLsCount = 0;
388 static const char * gUpdateRecord_Data = NULL;
389 static int gUpdateRecord_DelayMs = 0;
390 static int gUpdateRecord_TTL = 0;
391
392 static CLIOption kRegisterOpts[] =
393 {
394 InterfaceOption(),
395 StringOption( 'n', "name", &gRegister_Name, "service name", "Name of service.", false ),
396 StringOption( 't', "type", &gRegister_Type, "service type", "Service type, e.g., \"_ssh._tcp\".", true ),
397 StringOption( 'd', "domain", &gRegister_Domain, "domain", "Domain in which to advertise the service.", false ),
398 IntegerOption( 'p', "port", &gRegister_Port, "port number", "Service's port number.", true ),
399 StringOption( 0 , "txt", &gRegister_TXT, "record data", "The TXT record data. See " kRecordDataSection_Name " below.", false ),
400
401 CLI_OPTION_GROUP( "Flags" ),
402 DNSSDFlagsOption(),
403 DNSSDFlagsOption_IncludeAWDL(),
404 DNSSDFlagsOption_NoAutoRename(),
405
406 CLI_OPTION_GROUP( "Operation" ),
407 IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
408
409 CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ),
410 StringOption( 0 , "updateData", &gUpdateRecord_Data, "record data", "Record data for the record update. See " kRecordDataSection_Name " below.", false ),
411 IntegerOption( 0 , "updateDelay", &gUpdateRecord_DelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ),
412 IntegerOption( 0 , "updateTTL", &gUpdateRecord_TTL, "seconds", "Time-to-live of the updated record.", false ),
413
414 CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ),
415 MultiStringOption( 0 , "addType", &gAddRecord_Types, &gAddRecord_TypesCount, "record type", "Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ),
416 MultiStringOptionEx( 0 , "addData", &gAddRecord_Data, &gAddRecord_DataCount, "record data", "Additional record's data. See " kRecordDataSection_Name " below.", false, NULL ),
417 MultiStringOption( 0 , "addTTL", &gAddRecord_TTLs, &gAddRecord_TTLsCount, "seconds", "Time-to-live of additional record in seconds. Use '0' for default.", false ),
418
419 RecordDataSection(),
420 CLI_OPTION_END()
421 };
422
423 //===========================================================================================================================
424 // RegisterRecord Command Options
425 //===========================================================================================================================
426
427 static const char * gRegisterRecord_Name = NULL;
428 static const char * gRegisterRecord_Type = NULL;
429 static const char * gRegisterRecord_Data = NULL;
430 static int gRegisterRecord_TTL = 0;
431 static int gRegisterRecord_LifetimeMs = -1;
432 static const char * gRegisterRecord_UpdateData = NULL;
433 static int gRegisterRecord_UpdateDelayMs = 0;
434 static int gRegisterRecord_UpdateTTL = 0;
435
436 static CLIOption kRegisterRecordOpts[] =
437 {
438 InterfaceOption(),
439 StringOption( 'n', "name", &gRegisterRecord_Name, "record name", "Fully qualified domain name of record.", true ),
440 StringOption( 't', "type", &gRegisterRecord_Type, "record type", "Record type by name (e.g., TXT, PTR, A) or number.", true ),
441 StringOption( 'd', "data", &gRegisterRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ),
442 IntegerOption( 0 , "ttl", &gRegisterRecord_TTL, "seconds", "Time-to-live in seconds. Use '0' for default.", false ),
443
444 CLI_OPTION_GROUP( "Flags" ),
445 DNSSDFlagsOption(),
446 DNSSDFlagsOption_IncludeAWDL(),
447 DNSSDFlagsOption_Shared(),
448 DNSSDFlagsOption_Unique(),
449
450 CLI_OPTION_GROUP( "Operation" ),
451 IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
452
453 CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ),
454 StringOption( 0 , "updateData", &gRegisterRecord_UpdateData, "record data", "Record data for the record update.", false ),
455 IntegerOption( 0 , "updateDelay", &gRegisterRecord_UpdateDelayMs, "ms", "Number of milliseconds after registration to wait before record update.", false ),
456 IntegerOption( 0 , "updateTTL", &gRegisterRecord_UpdateTTL, "seconds", "Time-to-live of the updated record.", false ),
457
458 RecordDataSection(),
459 CLI_OPTION_END()
460 };
461
462 //===========================================================================================================================
463 // Resolve Command Options
464 //===========================================================================================================================
465
466 static char * gResolve_Name = NULL;
467 static char * gResolve_Type = NULL;
468 static char * gResolve_Domain = NULL;
469 static int gResolve_TimeLimitSecs = 0;
470
471 static CLIOption kResolveOpts[] =
472 {
473 InterfaceOption(),
474 StringOption( 'n', "name", &gResolve_Name, "service name", "Name of the service instance to resolve.", true ),
475 StringOption( 't', "type", &gResolve_Type, "service type", "Type of the service instance to resolve.", true ),
476 StringOption( 'd', "domain", &gResolve_Domain, "domain", "Domain of the service instance to resolve.", true ),
477
478 CLI_OPTION_GROUP( "Flags" ),
479 DNSSDFlagsOption(),
480 DNSSDFlagsOption_ForceMulticast(),
481 DNSSDFlagsOption_IncludeAWDL(),
482 DNSSDFlagsOption_ReturnIntermediates(),
483 DNSSDFlagsOption_WakeOnResolve(),
484
485 CLI_OPTION_GROUP( "Operation" ),
486 ConnectionOptions(),
487 IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ),
488
489 ConnectionSection(),
490 CLI_OPTION_END()
491 };
492
493 //===========================================================================================================================
494 // Reconfirm Command Options
495 //===========================================================================================================================
496
497 static const char * gReconfirmRecord_Name = NULL;
498 static const char * gReconfirmRecord_Type = NULL;
499 static const char * gReconfirmRecord_Class = NULL;
500 static const char * gReconfirmRecord_Data = NULL;
501
502 static CLIOption kReconfirmOpts[] =
503 {
504 InterfaceOption(),
505 StringOption( 'n', "name", &gReconfirmRecord_Name, "record name", "Full name of the record to reconfirm.", true ),
506 StringOption( 't', "type", &gReconfirmRecord_Type, "record type", "Type of the record to reconfirm.", true ),
507 StringOption( 'c', "class", &gReconfirmRecord_Class, "record class", "Class of the record to reconfirm. Default class is IN.", false ),
508 StringOption( 'd', "data", &gReconfirmRecord_Data, "record data", "The record data. See " kRecordDataSection_Name " below.", false ),
509
510 CLI_OPTION_GROUP( "Flags" ),
511 DNSSDFlagsOption(),
512
513 RecordDataSection(),
514 CLI_OPTION_END()
515 };
516
517 //===========================================================================================================================
518 // getaddrinfo-POSIX Command Options
519 //===========================================================================================================================
520
521 static const char * gGAIPOSIX_HostName = NULL;
522 static const char * gGAIPOSIX_ServName = NULL;
523 static const char * gGAIPOSIX_Family = NULL;
524 static int gGAIPOSIXFlag_AddrConfig = false;
525 static int gGAIPOSIXFlag_All = false;
526 static int gGAIPOSIXFlag_CanonName = false;
527 static int gGAIPOSIXFlag_NumericHost = false;
528 static int gGAIPOSIXFlag_NumericServ = false;
529 static int gGAIPOSIXFlag_Passive = false;
530 static int gGAIPOSIXFlag_V4Mapped = false;
531 #if( defined( AI_V4MAPPED_CFG ) )
532 static int gGAIPOSIXFlag_V4MappedCFG = false;
533 #endif
534 #if( defined( AI_DEFAULT ) )
535 static int gGAIPOSIXFlag_Default = false;
536 #endif
537 #if( defined( AI_UNUSABLE ) )
538 static int gGAIPOSIXFlag_Unusable = false;
539 #endif
540
541 static CLIOption kGetAddrInfoPOSIXOpts[] =
542 {
543 StringOption( 'n', "hostname", &gGAIPOSIX_HostName, "hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ),
544 StringOption( 's', "servname", &gGAIPOSIX_ServName, "servname", "Port number in decimal or service name from services(5).", false ),
545
546 CLI_OPTION_GROUP( "Hints " ),
547 StringOptionEx( 'f', "family", &gGAIPOSIX_Family, "address family", "Address family to use for hints ai_family field.", false,
548 "\n"
549 "Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n"
550 "address family is specified, then AF_UNSPEC is used.\n"
551 "\n" ),
552 BooleanOption( 0 , "flag-addrconfig", &gGAIPOSIXFlag_AddrConfig, "In hints ai_flags field, set AI_ADDRCONFIG." ),
553 BooleanOption( 0 , "flag-all", &gGAIPOSIXFlag_All, "In hints ai_flags field, set AI_ALL." ),
554 BooleanOption( 0 , "flag-canonname", &gGAIPOSIXFlag_CanonName, "In hints ai_flags field, set AI_CANONNAME." ),
555 BooleanOption( 0 , "flag-numerichost", &gGAIPOSIXFlag_NumericHost, "In hints ai_flags field, set AI_NUMERICHOST." ),
556 BooleanOption( 0 , "flag-numericserv", &gGAIPOSIXFlag_NumericServ, "In hints ai_flags field, set AI_NUMERICSERV." ),
557 BooleanOption( 0 , "flag-passive", &gGAIPOSIXFlag_Passive, "In hints ai_flags field, set AI_PASSIVE." ),
558 BooleanOption( 0 , "flag-v4mapped", &gGAIPOSIXFlag_V4Mapped, "In hints ai_flags field, set AI_V4MAPPED." ),
559 #if( defined( AI_V4MAPPED_CFG ) )
560 BooleanOption( 0 , "flag-v4mappedcfg", &gGAIPOSIXFlag_V4MappedCFG, "In hints ai_flags field, set AI_V4MAPPED_CFG." ),
561 #endif
562 #if( defined( AI_DEFAULT ) )
563 BooleanOption( 0 , "flag-default", &gGAIPOSIXFlag_Default, "In hints ai_flags field, set AI_DEFAULT." ),
564 #endif
565 #if( defined( AI_UNUSABLE ) )
566 BooleanOption( 0 , "flag-unusable", &gGAIPOSIXFlag_Unusable, "In hints ai_flags field, set AI_UNUSABLE." ),
567 #endif
568
569 CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ),
570 CLI_OPTION_END()
571 };
572
573 //===========================================================================================================================
574 // ReverseLookup Command Options
575 //===========================================================================================================================
576
577 static const char * gReverseLookup_IPAddr = NULL;
578 static int gReverseLookup_OneShot = false;
579 static int gReverseLookup_TimeLimitSecs = 0;
580
581 static CLIOption kReverseLookupOpts[] =
582 {
583 InterfaceOption(),
584 StringOption( 'a', "address", &gReverseLookup_IPAddr, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ),
585
586 CLI_OPTION_GROUP( "Flags" ),
587 DNSSDFlagsOption(),
588 DNSSDFlagsOption_ForceMulticast(),
589 DNSSDFlagsOption_ReturnIntermediates(),
590 DNSSDFlagsOption_SuppressUnusable(),
591
592 CLI_OPTION_GROUP( "Operation" ),
593 ConnectionOptions(),
594 BooleanOption( 'o', "oneshot", &gReverseLookup_OneShot, "Finish after first set of results." ),
595 IntegerOption( 'l', "timeLimit", &gReverseLookup_TimeLimitSecs, "seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ),
596
597 ConnectionSection(),
598 CLI_OPTION_END()
599 };
600
601 //===========================================================================================================================
602 // PortMapping Command Options
603 //===========================================================================================================================
604
605 static int gPortMapping_ProtocolTCP = false;
606 static int gPortMapping_ProtocolUDP = false;
607 static int gPortMapping_InternalPort = 0;
608 static int gPortMapping_ExternalPort = 0;
609 static int gPortMapping_TTL = 0;
610
611 static CLIOption kPortMappingOpts[] =
612 {
613 InterfaceOption(),
614 BooleanOption( 0, "tcp", &gPortMapping_ProtocolTCP, "Use kDNSServiceProtocol_TCP." ),
615 BooleanOption( 0, "udp", &gPortMapping_ProtocolUDP, "Use kDNSServiceProtocol_UDP." ),
616 IntegerOption( 0, "internalPort", &gPortMapping_InternalPort, "port number", "Internal port.", false ),
617 IntegerOption( 0, "externalPort", &gPortMapping_ExternalPort, "port number", "Requested external port. Use '0' for any external port.", false ),
618 IntegerOption( 0, "ttl", &gPortMapping_TTL, "seconds", "Requested TTL (renewal period) in seconds. Use '0' for a default value.", false ),
619
620 CLI_OPTION_GROUP( "Flags" ),
621 DNSSDFlagsOption(),
622
623 CLI_OPTION_GROUP( "Operation" ),
624 ConnectionOptions(),
625
626 ConnectionSection(),
627 CLI_OPTION_END()
628 };
629
630 //===========================================================================================================================
631 // BrowseAll Command Options
632 //===========================================================================================================================
633
634 static const char * gBrowseAll_Domain = NULL;
635 static char ** gBrowseAll_ServiceTypes = NULL;
636 static size_t gBrowseAll_ServiceTypesCount = 0;
637 static int gBrowseAll_IncludeAWDL = false;
638 static int gBrowseAll_BrowseTimeSecs = 5;
639 static int gBrowseAll_MaxConnectTimeSecs = 0;
640
641 static CLIOption kBrowseAllOpts[] =
642 {
643 InterfaceOption(),
644 StringOption( 'd', "domain", &gBrowseAll_Domain, "domain", "Domain in which to browse for the service.", false ),
645 MultiStringOption( 't', "type", &gBrowseAll_ServiceTypes, &gBrowseAll_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\". All services are browsed for if none is specified.", false ),
646
647 CLI_OPTION_GROUP( "Flags" ),
648 DNSSDFlagsOption_IncludeAWDL(),
649
650 CLI_OPTION_GROUP( "Operation" ),
651 IntegerOption( 'b', "browseTime", &gBrowseAll_BrowseTimeSecs, "seconds", "Amount of time to spend browsing. (Default: 5 seconds)", false ),
652 IntegerOption( 'c', "maxConnectTime", &gBrowseAll_MaxConnectTimeSecs, "seconds", "Max duration of connection attempts. If <= 0, then no connections are attempted. (Default: 0 seconds)", false ),
653 CLI_OPTION_END()
654 };
655
656 //===========================================================================================================================
657 // GetAddrInfoStress Command Options
658 //===========================================================================================================================
659
660 static int gGAIStress_TestDurationSecs = 0;
661 static int gGAIStress_ConnectionCount = 0;
662 static int gGAIStress_DurationMinMs = 0;
663 static int gGAIStress_DurationMaxMs = 0;
664 static int gGAIStress_RequestCountMax = 0;
665
666 static CLIOption kGetAddrInfoStressOpts[] =
667 {
668 InterfaceOption(),
669
670 CLI_OPTION_GROUP( "Flags" ),
671 DNSSDFlagsOption_ReturnIntermediates(),
672 DNSSDFlagsOption_SuppressUnusable(),
673
674 CLI_OPTION_GROUP( "Operation" ),
675 IntegerOption( 0, "testDuration", &gGAIStress_TestDurationSecs, "seconds", "Stress test duration in seconds. Use '0' for forever.", false ),
676 IntegerOption( 0, "connectionCount", &gGAIStress_ConnectionCount, "integer", "Number of simultaneous DNS-SD connections.", true ),
677 IntegerOption( 0, "requestDurationMin", &gGAIStress_DurationMinMs, "ms", "Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
678 IntegerOption( 0, "requestDurationMax", &gGAIStress_DurationMaxMs, "ms", "Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
679 IntegerOption( 0, "consecutiveRequestMax", &gGAIStress_RequestCountMax, "integer", "Maximum number of requests on a connection before restarting it.", true ),
680 CLI_OPTION_END()
681 };
682
683 //===========================================================================================================================
684 // DNSQuery Command Options
685 //===========================================================================================================================
686
687 static char * gDNSQuery_Name = NULL;
688 static char * gDNSQuery_Type = "A";
689 static char * gDNSQuery_Server = NULL;
690 static int gDNSQuery_TimeLimitSecs = 5;
691 static int gDNSQuery_UseTCP = false;
692 static int gDNSQuery_Flags = kDNSHeaderFlag_RecursionDesired;
693 static int gDNSQuery_RawRData = false;
694 static int gDNSQuery_Verbose = false;
695
696 #if( TARGET_OS_DARWIN )
697 #define kDNSQueryServerOptionIsRequired false
698 #else
699 #define kDNSQueryServerOptionIsRequired true
700 #endif
701
702 static CLIOption kDNSQueryOpts[] =
703 {
704 StringOption( 'n', "name", &gDNSQuery_Name, "name", "Question name (QNAME) to put in DNS query message.", true ),
705 StringOption( 't', "type", &gDNSQuery_Type, "type", "Question type (QTYPE) to put in DNS query message. Default value is 'A'.", false ),
706 StringOption( 's', "server", &gDNSQuery_Server, "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired ),
707 IntegerOption( 'l', "timeLimit", &gDNSQuery_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ),
708 BooleanOption( 0 , "tcp", &gDNSQuery_UseTCP, "Send the DNS query via TCP instead of UDP." ),
709 IntegerOption( 'f', "flags", &gDNSQuery_Flags, "flags", "16-bit value for DNS header flags/codes field. Default value is 0x0100 (Recursion Desired).", false ),
710 BooleanOption( 0 , "raw", &gDNSQuery_RawRData, "Present record data as a hexdump." ),
711 BooleanOption( 'v', "verbose", &gDNSQuery_Verbose, "Prints the DNS message to be sent to the server." ),
712 CLI_OPTION_END()
713 };
714
715 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
716 //===========================================================================================================================
717 // DNSCrypt Command Options
718 //===========================================================================================================================
719
720 static char * gDNSCrypt_ProviderName = NULL;
721 static char * gDNSCrypt_ProviderKey = NULL;
722 static char * gDNSCrypt_Name = NULL;
723 static char * gDNSCrypt_Type = NULL;
724 static char * gDNSCrypt_Server = NULL;
725 static int gDNSCrypt_TimeLimitSecs = 5;
726 static int gDNSCrypt_RawRData = false;
727 static int gDNSCrypt_Verbose = false;
728
729 static CLIOption kDNSCryptOpts[] =
730 {
731 StringOption( 'p', "providerName", &gDNSCrypt_ProviderName, "name", "The DNSCrypt provider name.", true ),
732 StringOption( 'k', "providerKey", &gDNSCrypt_ProviderKey, "hex string", "The DNSCrypt provider's public signing key.", true ),
733 StringOption( 'n', "name", &gDNSCrypt_Name, "name", "Question name (QNAME) to put in DNS query message.", true ),
734 StringOption( 't', "type", &gDNSCrypt_Type, "type", "Question type (QTYPE) to put in DNS query message.", true ),
735 StringOption( 's', "server", &gDNSCrypt_Server, "IP address", "DNS server's IPv4 or IPv6 address.", true ),
736 IntegerOption( 'l', "timeLimit", &gDNSCrypt_TimeLimitSecs, "seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ),
737 BooleanOption( 0 , "raw", &gDNSCrypt_RawRData, "Present record data as a hexdump." ),
738 BooleanOption( 'v', "verbose", &gDNSCrypt_Verbose, "Prints the DNS message to be sent to the server." ),
739 CLI_OPTION_END()
740 };
741 #endif
742
743 //===========================================================================================================================
744 // MDNSQuery Command Options
745 //===========================================================================================================================
746
747 static char * gMDNSQuery_Name = NULL;
748 static char * gMDNSQuery_Type = NULL;
749 static int gMDNSQuery_SourcePort = 0;
750 static int gMDNSQuery_IsQU = false;
751 static int gMDNSQuery_RawRData = false;
752 static int gMDNSQuery_UseIPv4 = false;
753 static int gMDNSQuery_UseIPv6 = false;
754 static int gMDNSQuery_AllResponses = false;
755 static int gMDNSQuery_ReceiveSecs = 1;
756
757 static CLIOption kMDNSQueryOpts[] =
758 {
759 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
760 StringOption( 'n', "name", &gMDNSQuery_Name, "name", "Question name (QNAME) to put in mDNS message.", true ),
761 StringOption( 't', "type", &gMDNSQuery_Type, "type", "Question type (QTYPE) to put in mDNS message.", true ),
762 IntegerOption( 'p', "sourcePort", &gMDNSQuery_SourcePort, "port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ),
763 BooleanOption( 'u', "QU", &gMDNSQuery_IsQU, "Set the unicast-response bit, i.e., send a QU question." ),
764 BooleanOption( 0 , "raw", &gMDNSQuery_RawRData, "Present record data as a hexdump." ),
765 BooleanOption( 0 , "ipv4", &gMDNSQuery_UseIPv4, "Use IPv4." ),
766 BooleanOption( 0 , "ipv6", &gMDNSQuery_UseIPv6, "Use IPv6." ),
767 BooleanOption( 'a', "allResponses", &gMDNSQuery_AllResponses, "Print all received mDNS messages, not just those containing answers." ),
768 IntegerOption( 'r', "receiveTime", &gMDNSQuery_ReceiveSecs, "seconds", "Amount of time to spend receiving messages after the query is sent. The default is one second. Use -1 for unlimited time.", false ),
769 CLI_OPTION_END()
770 };
771
772 //===========================================================================================================================
773 // PIDToUUID Command Options
774 //===========================================================================================================================
775
776 static int gPIDToUUID_PID = 0;
777
778 static CLIOption kPIDToUUIDOpts[] =
779 {
780 IntegerOption( 'p', "pid", &gPIDToUUID_PID, "PID", "Process ID.", true ),
781 CLI_OPTION_END()
782 };
783
784 //===========================================================================================================================
785 // SSDP Command Options
786 //===========================================================================================================================
787
788 static int gSSDPDiscover_MX = 1;
789 static const char * gSSDPDiscover_ST = "ssdp:all";
790 static int gSSDPDiscover_ReceiveSecs = 1;
791 static int gSSDPDiscover_UseIPv4 = false;
792 static int gSSDPDiscover_UseIPv6 = false;
793 static int gSSDPDiscover_Verbose = false;
794
795 static CLIOption kSSDPDiscoverOpts[] =
796 {
797 StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
798 IntegerOption( 'm', "mx", &gSSDPDiscover_MX, "seconds", "MX value in search request, i.e., max response delay in seconds. (Default: 1 second)", false ),
799 StringOption( 's', "st", &gSSDPDiscover_ST, "string", "ST value in search request, i.e., the search target. (Default: \"ssdp:all\")", false ),
800 IntegerOption( 'r', "receiveTime", &gSSDPDiscover_ReceiveSecs, "seconds", "Amount of time to spend receiving responses. -1 means unlimited. (Default: 1 second)", false ),
801 BooleanOption( 0 , "ipv4", &gSSDPDiscover_UseIPv4, "Use IPv4, i.e., multicast to 239.255.255.250:1900." ),
802 BooleanOption( 0 , "ipv6", &gSSDPDiscover_UseIPv6, "Use IPv6, i.e., multicast to [ff02::c]:1900" ),
803 BooleanOption( 'v', "verbose", &gSSDPDiscover_Verbose, "Prints the search request(s) that were sent." ),
804 CLI_OPTION_END()
805 };
806
807 static void SSDPDiscoverCmd( void );
808
809 static CLIOption kSSDPOpts[] =
810 {
811 Command( "discover", SSDPDiscoverCmd, kSSDPDiscoverOpts, "Crafts and multicasts an SSDP search message.", false ),
812 CLI_OPTION_END()
813 };
814
815 //===========================================================================================================================
816 // res_query Command Options
817 //===========================================================================================================================
818
819 static const char * gResQuery_Name = NULL;
820 static const char * gResQuery_Type = NULL;
821 static const char * gResQuery_Class = NULL;
822 static int gResQuery_UseLibInfo = false;
823
824 static CLIOption kResQueryOpts[] =
825 {
826 StringOption( 'n', "name", &gResQuery_Name, "domain name", "Full domain name of record to query.", true ),
827 StringOption( 't', "type", &gResQuery_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
828 StringOption( 'c', "class", &gResQuery_Class, "record class", "Record class by name or number. Default class is IN.", false ),
829 BooleanOption( 0 , "libinfo", &gResQuery_UseLibInfo, "Use res_query from libinfo instead of libresolv." ),
830 CLI_OPTION_END()
831 };
832
833 //===========================================================================================================================
834 // dns_query Command Options
835 //===========================================================================================================================
836
837 static const char * gResolvDNSQuery_Name = NULL;
838 static const char * gResolvDNSQuery_Type = NULL;
839 static const char * gResolvDNSQuery_Class = NULL;
840 static const char * gResolvDNSQuery_Path = NULL;
841
842 static CLIOption kResolvDNSQueryOpts[] =
843 {
844 StringOption( 'n', "name", &gResolvDNSQuery_Name, "domain name", "Full domain name of record to query.", true ),
845 StringOption( 't', "type", &gResolvDNSQuery_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
846 StringOption( 'c', "class", &gResolvDNSQuery_Class, "record class", "Record class by name or number. Default class is IN.", false ),
847 StringOption( 'p', "path", &gResolvDNSQuery_Path, "file path", "The path argument to pass to dns_open() before calling dns_query(). Default value is NULL.", false ),
848 CLI_OPTION_END()
849 };
850
851 //===========================================================================================================================
852 // Command Table
853 //===========================================================================================================================
854
855 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset );
856
857 static void BrowseCmd( void );
858 static void GetAddrInfoCmd( void );
859 static void QueryRecordCmd( void );
860 static void RegisterCmd( void );
861 static void RegisterRecordCmd( void );
862 static void ResolveCmd( void );
863 static void ReconfirmCmd( void );
864 static void GetAddrInfoPOSIXCmd( void );
865 static void ReverseLookupCmd( void );
866 static void PortMappingCmd( void );
867 static void BrowseAllCmd( void );
868 static void GetAddrInfoStressCmd( void );
869 static void DNSQueryCmd( void );
870 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
871 static void DNSCryptCmd( void );
872 #endif
873 static void MDNSQueryCmd( void );
874 static void PIDToUUIDCmd( void );
875 #if( TARGET_OS_DARWIN )
876 static void ResQueryCmd( void );
877 static void ResolvDNSQueryCmd( void );
878 #endif
879 static void DaemonVersionCmd( void );
880
881 static CLIOption kGlobalOpts[] =
882 {
883 CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback, NULL, NULL,
884 kCLIOptionFlags_NoArgument | kCLIOptionFlags_GlobalOnly, "Displays the version of this tool.", NULL ),
885 CLI_OPTION_HELP(),
886
887 // Common commands.
888
889 Command( "browse", BrowseCmd, kBrowseOpts, "Uses DNSServiceBrowse() to browse for one or more service types.", false ),
890 Command( "getAddrInfo", GetAddrInfoCmd, kGetAddrInfoOpts, "Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
891 Command( "queryRecord", QueryRecordCmd, kQueryRecordOpts, "Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
892 Command( "register", RegisterCmd, kRegisterOpts, "Uses DNSServiceRegister() to register a service.", false ),
893 Command( "registerRecord", RegisterRecordCmd, kRegisterRecordOpts, "Uses DNSServiceRegisterRecord() to register a record.", false ),
894 Command( "resolve", ResolveCmd, kResolveOpts, "Uses DNSServiceResolve() to resolve a service.", false ),
895 Command( "reconfirm", ReconfirmCmd, kReconfirmOpts, "Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
896 Command( "getaddrinfo-posix", GetAddrInfoPOSIXCmd, kGetAddrInfoPOSIXOpts, "Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
897 Command( "reverseLookup", ReverseLookupCmd, kReverseLookupOpts, "Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
898 Command( "portMapping", PortMappingCmd, kPortMappingOpts, "Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ),
899 Command( "browseAll", BrowseAllCmd, kBrowseAllOpts, "Browse and resolve all (or specific) services and, optionally, attempt connections.", false ),
900
901 // Uncommon commands.
902
903 Command( "getAddrInfoStress", GetAddrInfoStressCmd, kGetAddrInfoStressOpts, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
904 Command( "DNSQuery", DNSQueryCmd, kDNSQueryOpts, "Crafts and sends a DNS query.", true ),
905 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
906 Command( "DNSCrypt", DNSCryptCmd, kDNSCryptOpts, "Crafts and sends a DNSCrypt query.", true ),
907 #endif
908 Command( "mDNSQuery", MDNSQueryCmd, kMDNSQueryOpts, "Crafts and sends an mDNS query over the specified interface.", true ),
909 Command( "pid2uuid", PIDToUUIDCmd, kPIDToUUIDOpts, "Prints the UUID of a process.", true ),
910 Command( "ssdp", NULL, kSSDPOpts, "Commands for testing with Simple Service Discovery Protocol (SSDP).", true ),
911 #if( TARGET_OS_DARWIN )
912 Command( "res_query", ResQueryCmd, kResQueryOpts, "Uses res_query() from either libresolv or libinfo to query for a record.", true ),
913 Command( "dns_query", ResolvDNSQueryCmd, kResolvDNSQueryOpts, "Uses dns_query() from libresolv to query for a record.", true ),
914 #endif
915 Command( "daemonVersion", DaemonVersionCmd, NULL, "Prints the version of the DNS-SD daemon.", true ),
916
917 CLI_COMMAND_HELP(),
918 CLI_OPTION_END()
919 };
920
921 //===========================================================================================================================
922 // Helper Prototypes
923 //===========================================================================================================================
924
925 #define kExitReason_OneShotDone "one-shot done"
926 #define kExitReason_ReceivedResponse "received response"
927 #define kExitReason_SIGINT "interrupt signal"
928 #define kExitReason_Timeout "timeout"
929 #define kExitReason_TimeLimit "time limit"
930
931 static void Exit( void *inContext ) ATTRIBUTE_NORETURN;
932
933 #define kTimestampBufLen 27
934
935 static char * GetTimestampStr( char inBuffer[ kTimestampBufLen ] );
936 static DNSServiceFlags GetDNSSDFlagsFromOpts( void );
937
938 typedef enum
939 {
940 kConnectionType_None = 0,
941 kConnectionType_Normal = 1,
942 kConnectionType_DelegatePID = 2,
943 kConnectionType_DelegateUUID = 3
944
945 } ConnectionType;
946
947 typedef struct
948 {
949 ConnectionType type;
950 union
951 {
952 int32_t pid;
953 uint8_t uuid[ 16 ];
954
955 } delegate;
956
957 } ConnectionDesc;
958
959 static OSStatus
960 CreateConnectionFromArgString(
961 const char * inString,
962 dispatch_queue_t inQueue,
963 DNSServiceRef * outSDRef,
964 ConnectionDesc * outDesc );
965 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex );
966 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen );
967 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue );
968 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue );
969
970 #define kInterfaceNameBufLen ( Max( IF_NAMESIZE, 16 ) + 1 )
971
972 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] );
973 static const char * RecordTypeToString( unsigned int inValue );
974
975 // DNS message helpers
976
977 typedef struct
978 {
979 uint8_t id[ 2 ];
980 uint8_t flags[ 2 ];
981 uint8_t questionCount[ 2 ];
982 uint8_t answerCount[ 2 ];
983 uint8_t authorityCount[ 2 ];
984 uint8_t additionalCount[ 2 ];
985
986 } DNSHeader;
987
988 #define kDNSHeaderLength 12
989 check_compile_time( sizeof( DNSHeader ) == kDNSHeaderLength );
990
991 #define DNSHeaderGetID( HDR ) ReadBig16( ( HDR )->id )
992 #define DNSHeaderGetFlags( HDR ) ReadBig16( ( HDR )->flags )
993 #define DNSHeaderGetQuestionCount( HDR ) ReadBig16( ( HDR )->questionCount )
994 #define DNSHeaderGetAnswerCount( HDR ) ReadBig16( ( HDR )->answerCount )
995 #define DNSHeaderGetAuthorityCount( HDR ) ReadBig16( ( HDR )->authorityCount )
996 #define DNSHeaderGetAdditionalCount( HDR ) ReadBig16( ( HDR )->additionalCount )
997
998 static OSStatus
999 DNSMessageExtractDomainName(
1000 const uint8_t * inMsgPtr,
1001 size_t inMsgLen,
1002 const uint8_t * inNamePtr,
1003 uint8_t inBuf[ kDomainNameLengthMax ],
1004 const uint8_t ** outNextPtr );
1005 static OSStatus
1006 DNSMessageExtractDomainNameString(
1007 const void * inMsgPtr,
1008 size_t inMsgLen,
1009 const void * inNamePtr,
1010 char inBuf[ kDNSServiceMaxDomainName ],
1011 const uint8_t ** outNextPtr );
1012 static OSStatus
1013 DNSMessageExtractRecord(
1014 const uint8_t * inMsgPtr,
1015 size_t inMsgLen,
1016 const uint8_t * inPtr,
1017 uint8_t inNameBuf[ kDomainNameLengthMax ],
1018 uint16_t * outType,
1019 uint16_t * outClass,
1020 uint32_t * outTTL,
1021 const uint8_t ** outRDataPtr,
1022 size_t * outRDataLen,
1023 const uint8_t ** outPtr );
1024 static OSStatus DNSMessageGetAnswerSection( const uint8_t *inMsgPtr, size_t inMsgLen, const uint8_t **outPtr );
1025 static OSStatus
1026 DNSRecordDataToString(
1027 const void * inRDataPtr,
1028 size_t inRDataLen,
1029 unsigned int inRDataType,
1030 const void * inMsgPtr,
1031 size_t inMsgLen,
1032 char ** outString );
1033 static OSStatus
1034 DomainNameAppendString(
1035 uint8_t inDomainName[ kDomainNameLengthMax ],
1036 const char * inString,
1037 uint8_t ** outEndPtr );
1038 static Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 );
1039 static OSStatus
1040 DomainNameFromString(
1041 uint8_t inDomainName[ kDomainNameLengthMax ],
1042 const char * inString,
1043 uint8_t ** outEndPtr );
1044 static OSStatus
1045 DomainNameToString(
1046 const uint8_t * inDomainName,
1047 const uint8_t * inEnd,
1048 char inBuf[ kDNSServiceMaxDomainName ],
1049 const uint8_t ** outNextPtr );
1050
1051 static OSStatus PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, Boolean inIsMDNS, Boolean inPrintRaw );
1052
1053 #define PrintMDNSMessage( MSGPTR, MSGLEN, RAW ) PrintDNSMessage( MSGPTR, MSGLEN, true, RAW )
1054 #define PrintUDNSMessage( MSGPTR, MSGLEN, RAW ) PrintDNSMessage( MSGPTR, MSGLEN, false, RAW )
1055
1056 #define kDNSQueryMessageMaxLen ( kDNSHeaderLength + kDomainNameLengthMax + 4 )
1057
1058 static OSStatus
1059 WriteDNSQueryMessage(
1060 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
1061 uint16_t inMsgID,
1062 uint16_t inFlags,
1063 const char * inQName,
1064 uint16_t inQType,
1065 uint16_t inQClass,
1066 size_t * outMsgLen );
1067
1068 // Dispatch helpers
1069
1070 typedef void ( *DispatchHandler )( void *inContext );
1071
1072 static OSStatus
1073 DispatchSignalSourceCreate(
1074 int inSignal,
1075 DispatchHandler inEventHandler,
1076 void * inContext,
1077 dispatch_source_t * outSource );
1078 static OSStatus
1079 DispatchReadSourceCreate(
1080 SocketRef inSock,
1081 DispatchHandler inEventHandler,
1082 DispatchHandler inCancelHandler,
1083 void * inContext,
1084 dispatch_source_t * outSource );
1085 static OSStatus
1086 DispatchTimerCreate(
1087 dispatch_time_t inStart,
1088 uint64_t inIntervalNs,
1089 uint64_t inLeewayNs,
1090 DispatchHandler inEventHandler,
1091 DispatchHandler inCancelHandler,
1092 void * inContext,
1093 dispatch_source_t * outTimer );
1094
1095 static const char * ServiceTypeDescription( const char *inName );
1096
1097 typedef struct
1098 {
1099 SocketRef sock;
1100 void * context;
1101
1102 } SocketContext;
1103
1104 static void SocketContextCancelHandler( void *inContext );
1105 static OSStatus StringToInt32( const char *inString, int32_t *outValue );
1106 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue );
1107 #if( TARGET_OS_DARWIN )
1108 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr );
1109 #endif
1110
1111 #define AddRmvString( X ) ( ( (X) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" )
1112 #define Unused( X ) (void)(X)
1113
1114 //===========================================================================================================================
1115 // main
1116 //===========================================================================================================================
1117
1118 int main( int argc, const char **argv )
1119 {
1120 // Route DebugServices logging output to stderr.
1121
1122 dlog_control( "DebugServices:output=file;stderr" );
1123
1124 CLIInit( argc, argv );
1125 CLIParse( kGlobalOpts, kCLIFlags_None );
1126
1127 return( gExitCode );
1128 }
1129
1130 //===========================================================================================================================
1131 // VersionOptionCallback
1132 //===========================================================================================================================
1133
1134 static OSStatus VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset )
1135 {
1136 const char * srcVers;
1137 #if( MDNSRESPONDER_PROJECT )
1138 char srcStr[ 16 ];
1139 #endif
1140
1141 Unused( inOption );
1142 Unused( inArg );
1143 Unused( inUnset );
1144
1145 #if( MDNSRESPONDER_PROJECT )
1146 srcVers = SourceVersionToCString( _DNS_SD_H, srcStr );
1147 #else
1148 srcVers = DNSSDUTIL_SOURCE_VERSION;
1149 #endif
1150 FPrintF( stdout, "%s version %v (%s)\n", gProgramName, kDNSSDUtilNumVersion, srcVers );
1151
1152 return( kEndingErr );
1153 }
1154
1155 //===========================================================================================================================
1156 // BrowseCmd
1157 //===========================================================================================================================
1158
1159 typedef struct BrowseResolveOp BrowseResolveOp;
1160
1161 struct BrowseResolveOp
1162 {
1163 BrowseResolveOp * next; // Next resolve operation in list.
1164 DNSServiceRef sdRef; // sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
1165 char * fullName; // Full name of the service to resolve.
1166 uint32_t interfaceIndex; // Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
1167 };
1168
1169 typedef struct
1170 {
1171 DNSServiceRef mainRef; // Main sdRef for shared connection.
1172 DNSServiceRef * opRefs; // Array of sdRefs for individual Browse operarions.
1173 size_t opRefsCount; // Count of array of sdRefs for non-shared connections.
1174 const char * domain; // Domain for DNSServiceBrowse operation(s).
1175 DNSServiceFlags flags; // Flags for DNSServiceBrowse operation(s).
1176 char ** serviceTypes; // Array of service types to browse for.
1177 size_t serviceTypesCount; // Count of array of service types to browse for.
1178 int timeLimitSecs; // Time limit of DNSServiceBrowse operation in seconds.
1179 BrowseResolveOp * resolveList; // List of resolve and/or TXT record query operations.
1180 uint32_t ifIndex; // Interface index of DNSServiceBrowse operation(s).
1181 Boolean printedHeader; // True if results header has been printed.
1182 Boolean doResolve; // True if service instances are to be resolved.
1183 Boolean doResolveTXTOnly; // True if TXT records of service instances are to be queried.
1184
1185 } BrowseContext;
1186
1187 static void BrowsePrintPrologue( const BrowseContext *inContext );
1188 static void BrowseContextFree( BrowseContext *inContext );
1189 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp );
1190 static void BrowseResolveOpFree( BrowseResolveOp *inOp );
1191 static void DNSSD_API
1192 BrowseCallback(
1193 DNSServiceRef inSDRef,
1194 DNSServiceFlags inFlags,
1195 uint32_t inInterfaceIndex,
1196 DNSServiceErrorType inError,
1197 const char * inName,
1198 const char * inRegType,
1199 const char * inDomain,
1200 void * inContext );
1201 static void DNSSD_API
1202 BrowseResolveCallback(
1203 DNSServiceRef inSDRef,
1204 DNSServiceFlags inFlags,
1205 uint32_t inInterfaceIndex,
1206 DNSServiceErrorType inError,
1207 const char * inFullName,
1208 const char * inHostname,
1209 uint16_t inPort,
1210 uint16_t inTXTLen,
1211 const unsigned char * inTXTPtr,
1212 void * inContext );
1213 static void DNSSD_API
1214 BrowseQueryRecordCallback(
1215 DNSServiceRef inSDRef,
1216 DNSServiceFlags inFlags,
1217 uint32_t inInterfaceIndex,
1218 DNSServiceErrorType inError,
1219 const char * inFullName,
1220 uint16_t inType,
1221 uint16_t inClass,
1222 uint16_t inRDataLen,
1223 const void * inRDataPtr,
1224 uint32_t inTTL,
1225 void * inContext );
1226
1227 static void BrowseCmd( void )
1228 {
1229 OSStatus err;
1230 size_t i;
1231 BrowseContext * context = NULL;
1232 dispatch_source_t signalSource = NULL;
1233 int useMainConnection;
1234
1235 // Set up SIGINT handler.
1236
1237 signal( SIGINT, SIG_IGN );
1238 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
1239 require_noerr( err, exit );
1240 dispatch_resume( signalSource );
1241
1242 // Create context.
1243
1244 context = (BrowseContext *) calloc( 1, sizeof( *context ) );
1245 require_action( context, exit, err = kNoMemoryErr );
1246
1247 context->opRefs = (DNSServiceRef *) calloc( gBrowse_ServiceTypesCount, sizeof( DNSServiceRef ) );
1248 require_action( context->opRefs, exit, err = kNoMemoryErr );
1249 context->opRefsCount = gBrowse_ServiceTypesCount;
1250
1251 // Check command parameters.
1252
1253 if( gBrowse_TimeLimitSecs < 0 )
1254 {
1255 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs );
1256 err = kParamErr;
1257 goto exit;
1258 }
1259
1260 // Create main connection.
1261
1262 if( gConnectionOpt )
1263 {
1264 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
1265 require_noerr_quiet( err, exit );
1266 useMainConnection = true;
1267 }
1268 else
1269 {
1270 useMainConnection = false;
1271 }
1272
1273 // Get flags.
1274
1275 context->flags = GetDNSSDFlagsFromOpts();
1276 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
1277
1278 // Get interface.
1279
1280 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
1281 require_noerr_quiet( err, exit );
1282
1283 // Set remaining parameters.
1284
1285 context->serviceTypes = gBrowse_ServiceTypes;
1286 context->serviceTypesCount = gBrowse_ServiceTypesCount;
1287 context->domain = gBrowse_Domain;
1288 context->doResolve = gBrowse_DoResolve ? true : false;
1289 context->timeLimitSecs = gBrowse_TimeLimitSecs;
1290 context->doResolveTXTOnly = gBrowse_QueryTXT ? true : false;
1291
1292 // Print prologue.
1293
1294 BrowsePrintPrologue( context );
1295
1296 // Start operation(s).
1297
1298 for( i = 0; i < context->serviceTypesCount; ++i )
1299 {
1300 DNSServiceRef sdRef;
1301
1302 if( useMainConnection ) sdRef = context->mainRef;
1303 err = DNSServiceBrowse( &sdRef, context->flags, context->ifIndex, context->serviceTypes[ i ], context->domain,
1304 BrowseCallback, context );
1305 require_noerr( err, exit );
1306
1307 context->opRefs[ i ] = sdRef;
1308 if( !useMainConnection )
1309 {
1310 err = DNSServiceSetDispatchQueue( context->opRefs[ i ], dispatch_get_main_queue() );
1311 require_noerr( err, exit );
1312 }
1313 }
1314
1315 // Set time limit.
1316
1317 if( context->timeLimitSecs > 0 )
1318 {
1319 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
1320 kExitReason_TimeLimit, Exit );
1321 }
1322 dispatch_main();
1323
1324 exit:
1325 dispatch_source_forget( &signalSource );
1326 if( context ) BrowseContextFree( context );
1327 if( err ) exit( 1 );
1328 }
1329
1330 //===========================================================================================================================
1331 // BrowsePrintPrologue
1332 //===========================================================================================================================
1333
1334 static void BrowsePrintPrologue( const BrowseContext *inContext )
1335 {
1336 const int timeLimitSecs = inContext->timeLimitSecs;
1337 const char * const * serviceType = (const char **) inContext->serviceTypes;
1338 const char * const * const end = (const char **) inContext->serviceTypes + inContext->serviceTypesCount;
1339 char time[ kTimestampBufLen ];
1340 char ifName[ kInterfaceNameBufLen ];
1341
1342 InterfaceIndexToName( inContext->ifIndex, ifName );
1343
1344 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
1345 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
1346 FPrintF( stdout, "Service types: %s", *serviceType++ );
1347 while( serviceType < end ) FPrintF( stdout, ", %s", *serviceType++ );
1348 FPrintF( stdout, "\n" );
1349 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
1350 FPrintF( stdout, "Time limit: " );
1351 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
1352 else FPrintF( stdout, "∞\n" );
1353 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
1354 FPrintF( stdout, "---\n" );
1355 }
1356
1357 //===========================================================================================================================
1358 // BrowseContextFree
1359 //===========================================================================================================================
1360
1361 static void BrowseContextFree( BrowseContext *inContext )
1362 {
1363 size_t i;
1364
1365 for( i = 0; i < inContext->opRefsCount; ++i )
1366 {
1367 DNSServiceForget( &inContext->opRefs[ i ] );
1368 }
1369 if( inContext->serviceTypes )
1370 {
1371 StringArray_Free( inContext->serviceTypes, inContext->serviceTypesCount );
1372 inContext->serviceTypes = NULL;
1373 inContext->serviceTypesCount = 0;
1374 }
1375 DNSServiceForget( &inContext->mainRef );
1376 free( inContext );
1377 }
1378
1379 //===========================================================================================================================
1380 // BrowseResolveOpCreate
1381 //===========================================================================================================================
1382
1383 static OSStatus BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp )
1384 {
1385 OSStatus err;
1386 BrowseResolveOp * resolveOp;
1387
1388 resolveOp = (BrowseResolveOp *) calloc( 1, sizeof( *resolveOp ) );
1389 require_action( resolveOp, exit, err = kNoMemoryErr );
1390
1391 resolveOp->fullName = strdup( inFullName );
1392 require_action( resolveOp->fullName, exit, err = kNoMemoryErr );
1393
1394 resolveOp->interfaceIndex = inInterfaceIndex;
1395
1396 *outOp = resolveOp;
1397 resolveOp = NULL;
1398 err = kNoErr;
1399
1400 exit:
1401 if( resolveOp ) BrowseResolveOpFree( resolveOp );
1402 return( err );
1403 }
1404
1405 //===========================================================================================================================
1406 // BrowseResolveOpFree
1407 //===========================================================================================================================
1408
1409 static void BrowseResolveOpFree( BrowseResolveOp *inOp )
1410 {
1411 DNSServiceForget( &inOp->sdRef );
1412 ForgetMem( &inOp->fullName );
1413 free( inOp );
1414 }
1415
1416 //===========================================================================================================================
1417 // BrowseCallback
1418 //===========================================================================================================================
1419
1420 static void DNSSD_API
1421 BrowseCallback(
1422 DNSServiceRef inSDRef,
1423 DNSServiceFlags inFlags,
1424 uint32_t inInterfaceIndex,
1425 DNSServiceErrorType inError,
1426 const char * inName,
1427 const char * inRegType,
1428 const char * inDomain,
1429 void * inContext )
1430 {
1431 BrowseContext * const context = (BrowseContext *) inContext;
1432 OSStatus err;
1433 BrowseResolveOp * newOp = NULL;
1434 BrowseResolveOp ** p;
1435 char fullName[ kDNSServiceMaxDomainName ];
1436 char time[ kTimestampBufLen ];
1437
1438 Unused( inSDRef );
1439
1440 GetTimestampStr( time );
1441
1442 err = inError;
1443 require_noerr( err, exit );
1444
1445 if( !context->printedHeader )
1446 {
1447 FPrintF( stdout, "%-26s A/R Flags IF %-20s %-20s Instance Name\n", "Timestamp", "Domain", "Service Type" );
1448 context->printedHeader = true;
1449 }
1450 FPrintF( stdout, "%-26s %-3s %5X %2d %-20s %-20s %s\n",
1451 time, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inDomain, inRegType, inName );
1452
1453 if( !context->doResolve && !context->doResolveTXTOnly ) goto exit;
1454
1455 err = DNSServiceConstructFullName( fullName, inName, inRegType, inDomain );
1456 require_noerr( err, exit );
1457
1458 if( inFlags & kDNSServiceFlagsAdd )
1459 {
1460 DNSServiceRef sdRef;
1461 DNSServiceFlags flags;
1462
1463 err = BrowseResolveOpCreate( fullName, inInterfaceIndex, &newOp );
1464 require_noerr( err, exit );
1465
1466 if( context->mainRef )
1467 {
1468 sdRef = context->mainRef;
1469 flags = kDNSServiceFlagsShareConnection;
1470 }
1471 else
1472 {
1473 flags = 0;
1474 }
1475 if( context->doResolve )
1476 {
1477 err = DNSServiceResolve( &sdRef, flags, inInterfaceIndex, inName, inRegType, inDomain, BrowseResolveCallback,
1478 NULL );
1479 require_noerr( err, exit );
1480 }
1481 else
1482 {
1483 err = DNSServiceQueryRecord( &sdRef, flags, inInterfaceIndex, fullName, kDNSServiceType_TXT, kDNSServiceClass_IN,
1484 BrowseQueryRecordCallback, NULL );
1485 require_noerr( err, exit );
1486 }
1487
1488 newOp->sdRef = sdRef;
1489 if( !context->mainRef )
1490 {
1491 err = DNSServiceSetDispatchQueue( newOp->sdRef, dispatch_get_main_queue() );
1492 require_noerr( err, exit );
1493 }
1494 for( p = &context->resolveList; *p; p = &( *p )->next ) {}
1495 *p = newOp;
1496 newOp = NULL;
1497 }
1498 else
1499 {
1500 BrowseResolveOp * resolveOp;
1501
1502 for( p = &context->resolveList; ( resolveOp = *p ) != NULL; p = &resolveOp->next )
1503 {
1504 if( ( resolveOp->interfaceIndex == inInterfaceIndex ) && ( strcasecmp( resolveOp->fullName, fullName ) == 0 ) )
1505 {
1506 break;
1507 }
1508 }
1509 if( resolveOp )
1510 {
1511 *p = resolveOp->next;
1512 BrowseResolveOpFree( resolveOp );
1513 }
1514 }
1515
1516 exit:
1517 if( newOp ) BrowseResolveOpFree( newOp );
1518 if( err ) exit( 1 );
1519 }
1520
1521 //===========================================================================================================================
1522 // BrowseQueryRecordCallback
1523 //===========================================================================================================================
1524
1525 static void DNSSD_API
1526 BrowseQueryRecordCallback(
1527 DNSServiceRef inSDRef,
1528 DNSServiceFlags inFlags,
1529 uint32_t inInterfaceIndex,
1530 DNSServiceErrorType inError,
1531 const char * inFullName,
1532 uint16_t inType,
1533 uint16_t inClass,
1534 uint16_t inRDataLen,
1535 const void * inRDataPtr,
1536 uint32_t inTTL,
1537 void * inContext )
1538 {
1539 OSStatus err;
1540 char time[ kTimestampBufLen ];
1541
1542 Unused( inSDRef );
1543 Unused( inClass );
1544 Unused( inTTL );
1545 Unused( inContext );
1546
1547 GetTimestampStr( time );
1548
1549 err = inError;
1550 require_noerr( err, exit );
1551 require_action( inType == kDNSServiceType_TXT, exit, err = kTypeErr );
1552
1553 FPrintF( stdout, "%s %s %s TXT on interface %d\n TXT: %#{txt}\n",
1554 time, AddRmvString( inFlags ), inFullName, (int32_t) inInterfaceIndex, inRDataPtr, (size_t) inRDataLen );
1555
1556 exit:
1557 if( err ) exit( 1 );
1558 }
1559
1560 //===========================================================================================================================
1561 // BrowseResolveCallback
1562 //===========================================================================================================================
1563
1564 static void DNSSD_API
1565 BrowseResolveCallback(
1566 DNSServiceRef inSDRef,
1567 DNSServiceFlags inFlags,
1568 uint32_t inInterfaceIndex,
1569 DNSServiceErrorType inError,
1570 const char * inFullName,
1571 const char * inHostname,
1572 uint16_t inPort,
1573 uint16_t inTXTLen,
1574 const unsigned char * inTXTPtr,
1575 void * inContext )
1576 {
1577 char time[ kTimestampBufLen ];
1578 char errorStr[ 64 ];
1579
1580 Unused( inSDRef );
1581 Unused( inFlags );
1582 Unused( inContext );
1583
1584 GetTimestampStr( time );
1585
1586 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
1587
1588 FPrintF( stdout, "%s %s can be reached at %s:%u (interface %d)%?s\n",
1589 time, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
1590 if( inTXTLen == 1 )
1591 {
1592 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
1593 }
1594 else
1595 {
1596 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
1597 }
1598 }
1599
1600 //===========================================================================================================================
1601 // GetAddrInfoCmd
1602 //===========================================================================================================================
1603
1604 typedef struct
1605 {
1606 DNSServiceRef mainRef; // Main sdRef for shared connection.
1607 DNSServiceRef opRef; // sdRef for the DNSServiceGetAddrInfo operation.
1608 const char * name; // Hostname to resolve.
1609 DNSServiceFlags flags; // Flags argument for DNSServiceGetAddrInfo().
1610 DNSServiceProtocol protocols; // Protocols argument for DNSServiceGetAddrInfo().
1611 uint32_t ifIndex; // Interface index argument for DNSServiceGetAddrInfo().
1612 int timeLimitSecs; // Time limit for the DNSServiceGetAddrInfo() operation in seconds.
1613 Boolean printedHeader; // True if the results header has been printed.
1614 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
1615 Boolean needIPv4; // True if in one-shot mode and an IPv4 result is needed.
1616 Boolean needIPv6; // True if in one-shot mode and an IPv6 result is needed.
1617
1618 } GetAddrInfoContext;
1619
1620 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext );
1621 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext );
1622 static void DNSSD_API
1623 GetAddrInfoCallback(
1624 DNSServiceRef inSDRef,
1625 DNSServiceFlags inFlags,
1626 uint32_t inInterfaceIndex,
1627 DNSServiceErrorType inError,
1628 const char * inHostname,
1629 const struct sockaddr * inSockAddr,
1630 uint32_t inTTL,
1631 void * inContext );
1632
1633 static void GetAddrInfoCmd( void )
1634 {
1635 OSStatus err;
1636 DNSServiceRef sdRef;
1637 GetAddrInfoContext * context = NULL;
1638 dispatch_source_t signalSource = NULL;
1639 int useMainConnection;
1640
1641 // Set up SIGINT handler.
1642
1643 signal( SIGINT, SIG_IGN );
1644 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
1645 require_noerr( err, exit );
1646 dispatch_resume( signalSource );
1647
1648 // Check command parameters.
1649
1650 if( gGetAddrInfo_TimeLimitSecs < 0 )
1651 {
1652 FPrintF( stderr, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs );
1653 err = kParamErr;
1654 goto exit;
1655 }
1656
1657 // Create context.
1658
1659 context = (GetAddrInfoContext *) calloc( 1, sizeof( *context ) );
1660 require_action( context, exit, err = kNoMemoryErr );
1661
1662 // Create main connection.
1663
1664 if( gConnectionOpt )
1665 {
1666 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
1667 require_noerr_quiet( err, exit );
1668 useMainConnection = true;
1669 }
1670 else
1671 {
1672 useMainConnection = false;
1673 }
1674
1675 // Get flags.
1676
1677 context->flags = GetDNSSDFlagsFromOpts();
1678 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
1679
1680 // Get interface.
1681
1682 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
1683 require_noerr_quiet( err, exit );
1684
1685 // Set remaining parameters.
1686
1687 context->name = gGetAddrInfo_Name;
1688 context->timeLimitSecs = gGetAddrInfo_TimeLimitSecs;
1689 if( gGetAddrInfo_ProtocolIPv4 ) context->protocols |= kDNSServiceProtocol_IPv4;
1690 if( gGetAddrInfo_ProtocolIPv6 ) context->protocols |= kDNSServiceProtocol_IPv6;
1691 if( gGetAddrInfo_OneShot )
1692 {
1693 context->oneShotMode = true;
1694 context->needIPv4 = ( gGetAddrInfo_ProtocolIPv4 || !gGetAddrInfo_ProtocolIPv6 ) ? true : false;
1695 context->needIPv6 = ( gGetAddrInfo_ProtocolIPv6 || !gGetAddrInfo_ProtocolIPv4 ) ? true : false;
1696 }
1697
1698 // Print prologue.
1699
1700 GetAddrInfoPrintPrologue( context );
1701
1702 // Start operation.
1703
1704 if( useMainConnection ) sdRef = context->mainRef;
1705 err = DNSServiceGetAddrInfo( &sdRef, context->flags, context->ifIndex, context->protocols, context->name,
1706 GetAddrInfoCallback, context );
1707 require_noerr( err, exit );
1708
1709 context->opRef = sdRef;
1710 if( !useMainConnection )
1711 {
1712 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
1713 require_noerr( err, exit );
1714 }
1715
1716 // Set time limit.
1717
1718 if( context->timeLimitSecs > 0 )
1719 {
1720 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
1721 kExitReason_TimeLimit, Exit );
1722 }
1723 dispatch_main();
1724
1725 exit:
1726 dispatch_source_forget( &signalSource );
1727 if( context ) GetAddrInfoContextFree( context );
1728 if( err ) exit( 1 );
1729 }
1730
1731 //===========================================================================================================================
1732 // GetAddrInfoPrintPrologue
1733 //===========================================================================================================================
1734
1735 static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext )
1736 {
1737 const int timeLimitSecs = inContext->timeLimitSecs;
1738 char ifName[ kInterfaceNameBufLen ];
1739 char time[ kTimestampBufLen ];
1740
1741 InterfaceIndexToName( inContext->ifIndex, ifName );
1742
1743 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
1744 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
1745 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
1746 FPrintF( stdout, "Name: %s\n", inContext->name );
1747 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
1748 FPrintF( stdout, "Time limit: " );
1749 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
1750 else FPrintF( stdout, "∞\n" );
1751 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
1752 FPrintF( stdout, "---\n" );
1753 }
1754
1755 //===========================================================================================================================
1756 // GetAddrInfoContextFree
1757 //===========================================================================================================================
1758
1759 static void GetAddrInfoContextFree( GetAddrInfoContext *inContext )
1760 {
1761 DNSServiceForget( &inContext->opRef );
1762 DNSServiceForget( &inContext->mainRef );
1763 free( inContext );
1764 }
1765
1766 //===========================================================================================================================
1767 // GetAddrInfoCallback
1768 //===========================================================================================================================
1769
1770 static void DNSSD_API
1771 GetAddrInfoCallback(
1772 DNSServiceRef inSDRef,
1773 DNSServiceFlags inFlags,
1774 uint32_t inInterfaceIndex,
1775 DNSServiceErrorType inError,
1776 const char * inHostname,
1777 const struct sockaddr * inSockAddr,
1778 uint32_t inTTL,
1779 void * inContext )
1780 {
1781 GetAddrInfoContext * const context = (GetAddrInfoContext *) inContext;
1782 OSStatus err;
1783 const char * addrStr;
1784 char addrStrBuf[ kSockAddrStringMaxSize ];
1785 char time[ kTimestampBufLen ];
1786
1787 Unused( inSDRef );
1788
1789 GetTimestampStr( time );
1790
1791 switch( inError )
1792 {
1793 case kDNSServiceErr_NoError:
1794 case kDNSServiceErr_NoSuchRecord:
1795 err = kNoErr;
1796 break;
1797
1798 case kDNSServiceErr_Timeout:
1799 Exit( kExitReason_Timeout );
1800
1801 default:
1802 err = inError;
1803 goto exit;
1804 }
1805
1806 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
1807 {
1808 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
1809 err = kTypeErr;
1810 goto exit;
1811 }
1812
1813 if( !inError )
1814 {
1815 err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf );
1816 require_noerr( err, exit );
1817 addrStr = addrStrBuf;
1818 }
1819 else
1820 {
1821 addrStr = ( inSockAddr->sa_family == AF_INET ) ? "No Such Record (A)" : "No Such Record (AAAA)";
1822 }
1823
1824 if( !context->printedHeader )
1825 {
1826 FPrintF( stdout, "%-26s A/R Flags IF %-32s %-38s %6s\n", "Timestamp", "Hostname", "Address", "TTL" );
1827 context->printedHeader = true;
1828 }
1829 FPrintF( stdout, "%-26s %s %5X %2d %-32s %-38s %6u\n",
1830 time, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inHostname, addrStr, inTTL );
1831
1832 if( context->oneShotMode )
1833 {
1834 if( inFlags & kDNSServiceFlagsAdd )
1835 {
1836 if( inSockAddr->sa_family == AF_INET ) context->needIPv4 = false;
1837 else context->needIPv6 = false;
1838 }
1839 if( !( inFlags & kDNSServiceFlagsMoreComing ) && !context->needIPv4 && !context->needIPv6 )
1840 {
1841 Exit( kExitReason_OneShotDone );
1842 }
1843 }
1844
1845 exit:
1846 if( err ) exit( 1 );
1847 }
1848
1849 //===========================================================================================================================
1850 // QueryRecordCmd
1851 //===========================================================================================================================
1852
1853 typedef struct
1854 {
1855 DNSServiceRef mainRef; // Main sdRef for shared connection.
1856 DNSServiceRef opRef; // sdRef for the DNSServiceQueryRecord operation.
1857 const char * recordName; // Resource record name argument for DNSServiceQueryRecord().
1858 DNSServiceFlags flags; // Flags argument for DNSServiceQueryRecord().
1859 uint32_t ifIndex; // Interface index argument for DNSServiceQueryRecord().
1860 int timeLimitSecs; // Time limit for the DNSServiceQueryRecord() operation in seconds.
1861 uint16_t recordType; // Resource record type argument for DNSServiceQueryRecord().
1862 Boolean printedHeader; // True if the results header was printed.
1863 Boolean oneShotMode; // True if command is done after the first set of results (one-shot mode).
1864 Boolean gotRecord; // True if in one-shot mode and received at least one record of the desired type.
1865 Boolean printRawRData; // True if RDATA results are not to be formatted when printed.
1866
1867 } QueryRecordContext;
1868
1869 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext );
1870 static void QueryRecordContextFree( QueryRecordContext *inContext );
1871 static void DNSSD_API
1872 QueryRecordCallback(
1873 DNSServiceRef inSDRef,
1874 DNSServiceFlags inFlags,
1875 uint32_t inInterfaceIndex,
1876 DNSServiceErrorType inError,
1877 const char * inFullName,
1878 uint16_t inType,
1879 uint16_t inClass,
1880 uint16_t inRDataLen,
1881 const void * inRDataPtr,
1882 uint32_t inTTL,
1883 void * inContext );
1884
1885 static void QueryRecordCmd( void )
1886 {
1887 OSStatus err;
1888 DNSServiceRef sdRef;
1889 QueryRecordContext * context = NULL;
1890 dispatch_source_t signalSource = NULL;
1891 int useMainConnection;
1892
1893 // Set up SIGINT handler.
1894
1895 signal( SIGINT, SIG_IGN );
1896 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
1897 require_noerr( err, exit );
1898 dispatch_resume( signalSource );
1899
1900 // Create context.
1901
1902 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
1903 require_action( context, exit, err = kNoMemoryErr );
1904
1905 // Check command parameters.
1906
1907 if( gQueryRecord_TimeLimitSecs < 0 )
1908 {
1909 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs );
1910 err = kParamErr;
1911 goto exit;
1912 }
1913
1914 // Create main connection.
1915
1916 if( gConnectionOpt )
1917 {
1918 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
1919 require_noerr_quiet( err, exit );
1920 useMainConnection = true;
1921 }
1922 else
1923 {
1924 useMainConnection = false;
1925 }
1926
1927 // Get flags.
1928
1929 context->flags = GetDNSSDFlagsFromOpts();
1930 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
1931
1932 // Get interface.
1933
1934 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
1935 require_noerr_quiet( err, exit );
1936
1937 // Get record type.
1938
1939 err = RecordTypeFromArgString( gQueryRecord_Type, &context->recordType );
1940 require_noerr( err, exit );
1941
1942 // Set remaining parameters.
1943
1944 context->recordName = gQueryRecord_Name;
1945 context->timeLimitSecs = gQueryRecord_TimeLimitSecs;
1946 context->oneShotMode = gQueryRecord_OneShot ? true : false;
1947 context->printRawRData = gQueryRecord_RawRData ? true : false;
1948
1949 // Print prologue.
1950
1951 QueryRecordPrintPrologue( context );
1952
1953 // Start operation.
1954
1955 if( useMainConnection ) sdRef = context->mainRef;
1956 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
1957 kDNSServiceClass_IN, QueryRecordCallback, context );
1958 require_noerr( err, exit );
1959
1960 context->opRef = sdRef;
1961 if( !useMainConnection )
1962 {
1963 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
1964 require_noerr( err, exit );
1965 }
1966
1967 // Set time limit.
1968
1969 if( context->timeLimitSecs > 0 )
1970 {
1971 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_TimeLimit,
1972 Exit );
1973 }
1974 dispatch_main();
1975
1976 exit:
1977 dispatch_source_forget( &signalSource );
1978 if( context ) QueryRecordContextFree( context );
1979 if( err ) exit( 1 );
1980 }
1981
1982 //===========================================================================================================================
1983 // QueryRecordContextFree
1984 //===========================================================================================================================
1985
1986 static void QueryRecordContextFree( QueryRecordContext *inContext )
1987 {
1988 DNSServiceForget( &inContext->opRef );
1989 DNSServiceForget( &inContext->mainRef );
1990 free( inContext );
1991 }
1992
1993 //===========================================================================================================================
1994 // QueryRecordPrintPrologue
1995 //===========================================================================================================================
1996
1997 static void QueryRecordPrintPrologue( const QueryRecordContext *inContext )
1998 {
1999 const int timeLimitSecs = inContext->timeLimitSecs;
2000 char ifName[ kInterfaceNameBufLen ];
2001 char time[ kTimestampBufLen ];
2002
2003 InterfaceIndexToName( inContext->ifIndex, ifName );
2004
2005 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
2006 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
2007 FPrintF( stdout, "Name: %s\n", inContext->recordName );
2008 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
2009 FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" );
2010 FPrintF( stdout, "Time limit: " );
2011 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
2012 else FPrintF( stdout, "∞\n" );
2013 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
2014 FPrintF( stdout, "---\n" );
2015
2016 }
2017
2018 //===========================================================================================================================
2019 // QueryRecordCallback
2020 //===========================================================================================================================
2021
2022 static void DNSSD_API
2023 QueryRecordCallback(
2024 DNSServiceRef inSDRef,
2025 DNSServiceFlags inFlags,
2026 uint32_t inInterfaceIndex,
2027 DNSServiceErrorType inError,
2028 const char * inFullName,
2029 uint16_t inType,
2030 uint16_t inClass,
2031 uint16_t inRDataLen,
2032 const void * inRDataPtr,
2033 uint32_t inTTL,
2034 void * inContext )
2035 {
2036 QueryRecordContext * const context = (QueryRecordContext *) inContext;
2037 OSStatus err;
2038 char * rdataStr = NULL;
2039 char time[ kTimestampBufLen ];
2040
2041 Unused( inSDRef );
2042
2043 GetTimestampStr( time );
2044
2045 switch( inError )
2046 {
2047 case kDNSServiceErr_NoError:
2048 case kDNSServiceErr_NoSuchRecord:
2049 err = kNoErr;
2050 break;
2051
2052 case kDNSServiceErr_Timeout:
2053 Exit( kExitReason_Timeout );
2054
2055 default:
2056 err = inError;
2057 goto exit;
2058 }
2059
2060 if( inError == kDNSServiceErr_NoSuchRecord )
2061 {
2062 ASPrintF( &rdataStr, "No Such Record" );
2063 }
2064 else
2065 {
2066 if( !context->printRawRData ) DNSRecordDataToString( inRDataPtr, inRDataLen, inType, NULL, 0, &rdataStr );
2067 if( !rdataStr )
2068 {
2069 ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, INT_MAX );
2070 require_action( rdataStr, exit, err = kNoMemoryErr );
2071 }
2072 }
2073
2074 if( !context->printedHeader )
2075 {
2076 FPrintF( stdout, "%-26s A/R Flags IF %-32s %-5s %-5s %6s RData\n", "Timestamp", "Name", "Type", "Class", "TTL" );
2077 context->printedHeader = true;
2078 }
2079 FPrintF( stdout, "%-26s %-3s %5X %2d %-32s %-5s %?-5s%?5u %6u %s\n",
2080 time, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inFullName, RecordTypeToString( inType ),
2081 ( inClass == kDNSServiceClass_IN ), "IN", ( inClass != kDNSServiceClass_IN ), inClass, inTTL, rdataStr );
2082
2083 if( context->oneShotMode )
2084 {
2085 if( ( inFlags & kDNSServiceFlagsAdd ) &&
2086 ( ( context->recordType == kDNSServiceType_ANY ) || ( context->recordType == inType ) ) )
2087 {
2088 context->gotRecord = true;
2089 }
2090 if( !( inFlags & kDNSServiceFlagsMoreComing ) && context->gotRecord ) Exit( kExitReason_OneShotDone );
2091 }
2092
2093 exit:
2094 FreeNullSafe( rdataStr );
2095 if( err ) exit( 1 );
2096 }
2097
2098 //===========================================================================================================================
2099 // RegisterCmd
2100 //===========================================================================================================================
2101
2102 typedef struct
2103 {
2104 DNSRecordRef recordRef; // Reference returned by DNSServiceAddRecord().
2105 uint8_t * dataPtr; // Record data.
2106 size_t dataLen; // Record data length.
2107 uint32_t ttl; // Record TTL value.
2108 uint16_t type; // Record type.
2109
2110 } ExtraRecord;
2111
2112 typedef struct
2113 {
2114 DNSServiceRef opRef; // sdRef for DNSServiceRegister operation.
2115 const char * name; // Service name argument for DNSServiceRegister().
2116 const char * type; // Service type argument for DNSServiceRegister().
2117 const char * domain; // Domain in which advertise the service.
2118 uint8_t * txtPtr; // Service TXT record data. (malloc'd)
2119 size_t txtLen; // Service TXT record data len.
2120 ExtraRecord * extraRecords; // Array of extra records to add to registered service.
2121 size_t extraRecordsCount; // Number of extra records.
2122 uint8_t * updateTXTPtr; // Pointer to record data for TXT record update. (malloc'd)
2123 size_t updateTXTLen; // Length of record data for TXT record update.
2124 uint32_t updateTTL; // TTL of updated TXT record.
2125 int updateDelayMs; // Post-registration TXT record update delay in milliseconds.
2126 DNSServiceFlags flags; // Flags argument for DNSServiceRegister().
2127 uint32_t ifIndex; // Interface index argument for DNSServiceRegister().
2128 int lifetimeMs; // Lifetime of the record registration in milliseconds.
2129 uint16_t port; // Service instance's port number.
2130 Boolean printedHeader; // True if results header was printed.
2131 Boolean didRegister; // True if service was registered.
2132
2133 } RegisterContext;
2134
2135 static void RegisterPrintPrologue( const RegisterContext *inContext );
2136 static void RegisterContextFree( RegisterContext *inContext );
2137 static void DNSSD_API
2138 RegisterCallback(
2139 DNSServiceRef inSDRef,
2140 DNSServiceFlags inFlags,
2141 DNSServiceErrorType inError,
2142 const char * inName,
2143 const char * inType,
2144 const char * inDomain,
2145 void * inContext );
2146 static void RegisterUpdate( void *inContext );
2147
2148 static void RegisterCmd( void )
2149 {
2150 OSStatus err;
2151 RegisterContext * context = NULL;
2152 dispatch_source_t signalSource = NULL;
2153
2154 // Set up SIGINT handler.
2155
2156 signal( SIGINT, SIG_IGN );
2157 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
2158 require_noerr( err, exit );
2159 dispatch_resume( signalSource );
2160
2161 // Create context.
2162
2163 context = (RegisterContext *) calloc( 1, sizeof( *context ) );
2164 require_action( context, exit, err = kNoMemoryErr );
2165
2166 // Check command parameters.
2167
2168 if( ( gRegister_Port < 0 ) || ( gRegister_Port > UINT16_MAX ) )
2169 {
2170 FPrintF( stderr, "Port number %d is out-of-range.\n", gRegister_Port );
2171 err = kParamErr;
2172 goto exit;
2173 }
2174
2175 if( ( gAddRecord_DataCount != gAddRecord_TypesCount ) || ( gAddRecord_TTLsCount != gAddRecord_TypesCount ) )
2176 {
2177 FPrintF( stderr, "There are missing additional record parameters.\n" );
2178 err = kParamErr;
2179 goto exit;
2180 }
2181
2182 // Get flags.
2183
2184 context->flags = GetDNSSDFlagsFromOpts();
2185
2186 // Get interface index.
2187
2188 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
2189 require_noerr_quiet( err, exit );
2190
2191 // Get TXT record data.
2192
2193 if( gRegister_TXT )
2194 {
2195 err = RecordDataFromArgString( gRegister_TXT, &context->txtPtr, &context->txtLen );
2196 require_noerr_quiet( err, exit );
2197 }
2198
2199 // Set remaining parameters.
2200
2201 context->name = gRegister_Name;
2202 context->type = gRegister_Type;
2203 context->domain = gRegister_Domain;
2204 context->port = (uint16_t) gRegister_Port;
2205 context->lifetimeMs = gRegister_LifetimeMs;
2206
2207 if( gAddRecord_TypesCount > 0 )
2208 {
2209 size_t i;
2210
2211 context->extraRecords = (ExtraRecord *) calloc( gAddRecord_TypesCount, sizeof( ExtraRecord ) );
2212 require_action( context, exit, err = kNoMemoryErr );
2213 context->extraRecordsCount = gAddRecord_TypesCount;
2214
2215 for( i = 0; i < gAddRecord_TypesCount; ++i )
2216 {
2217 ExtraRecord * const extraRecord = &context->extraRecords[ i ];
2218
2219 err = RecordTypeFromArgString( gAddRecord_Types[ i ], &extraRecord->type );
2220 require_noerr( err, exit );
2221
2222 err = StringToUInt32( gAddRecord_TTLs[ i ], &extraRecord->ttl );
2223 if( err )
2224 {
2225 FPrintF( stderr, "Invalid TTL value: %s\n", gAddRecord_TTLs[ i ] );
2226 err = kParamErr;
2227 goto exit;
2228 }
2229
2230 err = RecordDataFromArgString( gAddRecord_Data[ i ], &extraRecord->dataPtr, &extraRecord->dataLen );
2231 require_noerr_quiet( err, exit );
2232 }
2233 }
2234
2235 if( gUpdateRecord_Data )
2236 {
2237 err = RecordDataFromArgString( gUpdateRecord_Data, &context->updateTXTPtr, &context->updateTXTLen );
2238 require_noerr_quiet( err, exit );
2239
2240 context->updateTTL = (uint32_t) gUpdateRecord_TTL;
2241 context->updateDelayMs = gUpdateRecord_DelayMs;
2242 }
2243
2244 // Print prologue.
2245
2246 RegisterPrintPrologue( context );
2247
2248 // Start operation.
2249
2250 err = DNSServiceRegister( &context->opRef, context->flags, context->ifIndex, context->name, context->type,
2251 context->domain, NULL, htons( context->port ), (uint16_t) context->txtLen, context->txtPtr,
2252 RegisterCallback, context );
2253 ForgetMem( &context->txtPtr );
2254 require_noerr( err, exit );
2255
2256 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
2257 require_noerr( err, exit );
2258
2259 dispatch_main();
2260
2261 exit:
2262 dispatch_source_forget( &signalSource );
2263 if( context ) RegisterContextFree( context );
2264 if( err ) exit( 1 );
2265 }
2266
2267 //===========================================================================================================================
2268 // RegisterPrintPrologue
2269 //===========================================================================================================================
2270
2271 static void RegisterPrintPrologue( const RegisterContext *inContext )
2272 {
2273 size_t i;
2274 int infinite;
2275 char ifName[ kInterfaceNameBufLen ];
2276 char time[ kTimestampBufLen ];
2277
2278 InterfaceIndexToName( inContext->ifIndex, ifName );
2279
2280 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
2281 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
2282 FPrintF( stdout, "Name: %s\n", inContext->name ? inContext->name : "<NULL>" );
2283 FPrintF( stdout, "Type: %s\n", inContext->type );
2284 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "<NULL> (default domains)" );
2285 FPrintF( stdout, "Port: %u\n", inContext->port );
2286 FPrintF( stdout, "TXT data: %#{txt}\n", inContext->txtPtr, inContext->txtLen );
2287 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
2288 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
2289 if( inContext->updateTXTPtr )
2290 {
2291 FPrintF( stdout, "\nUpdate record:\n" );
2292 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs > 0 ) ? inContext->updateDelayMs : 0 );
2293 FPrintF( stdout, " TTL: %u%?s\n",
2294 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
2295 FPrintF( stdout, " TXT data: %#{txt}\n", inContext->updateTXTPtr, inContext->updateTXTLen );
2296 }
2297 if( inContext->extraRecordsCount > 0 ) FPrintF( stdout, "\n" );
2298 for( i = 0; i < inContext->extraRecordsCount; ++i )
2299 {
2300 const ExtraRecord * record = &inContext->extraRecords[ i ];
2301
2302 FPrintF( stdout, "Extra record %zu:\n", i + 1 );
2303 FPrintF( stdout, " Type: %s (%u)\n", RecordTypeToString( record->type ), record->type );
2304 FPrintF( stdout, " TTL: %u%?s\n", record->ttl, record->ttl == 0, " (system will use a default value.)" );
2305 FPrintF( stdout, " RData: %#H\n\n", record->dataPtr, (int) record->dataLen, INT_MAX );
2306 }
2307 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
2308 FPrintF( stdout, "---\n" );
2309 }
2310
2311 //===========================================================================================================================
2312 // RegisterContextFree
2313 //===========================================================================================================================
2314
2315 static void RegisterContextFree( RegisterContext *inContext )
2316 {
2317 ExtraRecord * record;
2318 const ExtraRecord * const end = inContext->extraRecords + inContext->extraRecordsCount;
2319
2320 DNSServiceForget( &inContext->opRef );
2321 ForgetMem( &inContext->txtPtr );
2322 for( record = inContext->extraRecords; record < end; ++record )
2323 {
2324 check( !record->recordRef );
2325 ForgetMem( &record->dataPtr );
2326 }
2327 ForgetMem( &inContext->extraRecords );
2328 ForgetMem( &inContext->updateTXTPtr );
2329 free( inContext );
2330 }
2331
2332 //===========================================================================================================================
2333 // RegisterCallback
2334 //===========================================================================================================================
2335
2336 static void DNSSD_API
2337 RegisterCallback(
2338 DNSServiceRef inSDRef,
2339 DNSServiceFlags inFlags,
2340 DNSServiceErrorType inError,
2341 const char * inName,
2342 const char * inType,
2343 const char * inDomain,
2344 void * inContext )
2345 {
2346 RegisterContext * const context = (RegisterContext *) inContext;
2347 OSStatus err;
2348 char time[ kTimestampBufLen ];
2349
2350 Unused( inSDRef );
2351
2352 GetTimestampStr( time );
2353
2354 if( !context->printedHeader )
2355 {
2356 FPrintF( stdout, "%-26s A/R Flags Service\n", "Timestamp" );
2357 context->printedHeader = true;
2358 }
2359 FPrintF( stdout, "%-26s %-3s %5X %s.%s%s %?#m\n",
2360 time, AddRmvString( inFlags ), inFlags, inName, inType, inDomain, inError, inError );
2361
2362 require_noerr_action_quiet( inError, exit, err = inError );
2363
2364 if( !context->didRegister && ( inFlags & kDNSServiceFlagsAdd ) )
2365 {
2366 context->didRegister = true;
2367 if( context->updateTXTPtr )
2368 {
2369 if( context->updateDelayMs > 0 )
2370 {
2371 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
2372 context, RegisterUpdate );
2373 }
2374 else
2375 {
2376 RegisterUpdate( context );
2377 }
2378 }
2379 if( context->extraRecordsCount > 0 )
2380 {
2381 ExtraRecord * record;
2382 const ExtraRecord * const end = context->extraRecords + context->extraRecordsCount;
2383
2384 for( record = context->extraRecords; record < end; ++record )
2385 {
2386 err = DNSServiceAddRecord( context->opRef, &record->recordRef, 0, record->type,
2387 (uint16_t) record->dataLen, record->dataPtr, record->ttl );
2388 require_noerr( err, exit );
2389 }
2390 }
2391 if( context->lifetimeMs == 0 )
2392 {
2393 Exit( kExitReason_TimeLimit );
2394 }
2395 else if( context->lifetimeMs > 0 )
2396 {
2397 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
2398 kExitReason_TimeLimit, Exit );
2399 }
2400 }
2401 err = kNoErr;
2402
2403 exit:
2404 if( err ) exit( 1 );
2405 }
2406
2407 //===========================================================================================================================
2408 // RegisterUpdate
2409 //===========================================================================================================================
2410
2411 static void RegisterUpdate( void *inContext )
2412 {
2413 OSStatus err;
2414 RegisterContext * const context = (RegisterContext *) inContext;
2415
2416 err = DNSServiceUpdateRecord( context->opRef, NULL, 0, (uint16_t) context->updateTXTLen, context->updateTXTPtr,
2417 context->updateTTL );
2418 require_noerr( err, exit );
2419
2420 exit:
2421 if( err ) exit( 1 );
2422 }
2423
2424 //===========================================================================================================================
2425 // RegisterRecordCmd
2426 //===========================================================================================================================
2427
2428 typedef struct
2429 {
2430 DNSServiceRef conRef; // sdRef to be initialized by DNSServiceCreateConnection().
2431 DNSRecordRef recordRef; // Registered record reference.
2432 const char * recordName; // Name of resource record.
2433 uint8_t * dataPtr; // Pointer to resource record data.
2434 size_t dataLen; // Length of resource record data.
2435 uint32_t ttl; // TTL value of resource record in seconds.
2436 uint32_t ifIndex; // Interface index argument for DNSServiceRegisterRecord().
2437 DNSServiceFlags flags; // Flags argument for DNSServiceRegisterRecord().
2438 int lifetimeMs; // Lifetime of the record registration in milliseconds.
2439 uint16_t recordType; // Resource record type.
2440 uint8_t * updateDataPtr; // Pointer to data for record update. (malloc'd)
2441 size_t updateDataLen; // Length of data for record update.
2442 uint32_t updateTTL; // TTL for updated record.
2443 int updateDelayMs; // Post-registration record update delay in milliseconds.
2444 Boolean didRegister; // True if the record was registered.
2445
2446 } RegisterRecordContext;
2447
2448 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext );
2449 static void RegisterRecordContextFree( RegisterRecordContext *inContext );
2450 static void DNSSD_API
2451 RegisterRecordCallback(
2452 DNSServiceRef inSDRef,
2453 DNSRecordRef inRecordRef,
2454 DNSServiceFlags inFlags,
2455 DNSServiceErrorType inError,
2456 void * inContext );
2457 static void RegisterRecordUpdate( void *inContext );
2458
2459 static void RegisterRecordCmd( void )
2460 {
2461 OSStatus err;
2462 RegisterRecordContext * context = NULL;
2463 dispatch_source_t signalSource = NULL;
2464
2465 // Set up SIGINT handler.
2466
2467 signal( SIGINT, SIG_IGN );
2468 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
2469 require_noerr( err, exit );
2470 dispatch_resume( signalSource );
2471
2472 // Create context.
2473
2474 context = (RegisterRecordContext *) calloc( 1, sizeof( *context ) );
2475 require_action( context, exit, err = kNoMemoryErr );
2476
2477 // Create connection.
2478
2479 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->conRef, NULL );
2480 require_noerr_quiet( err, exit );
2481
2482 // Get flags.
2483
2484 context->flags = GetDNSSDFlagsFromOpts();
2485
2486 // Get interface.
2487
2488 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
2489 require_noerr_quiet( err, exit );
2490
2491 // Get record type.
2492
2493 err = RecordTypeFromArgString( gRegisterRecord_Type, &context->recordType );
2494 require_noerr( err, exit );
2495
2496 // Get record data.
2497
2498 if( gRegisterRecord_Data )
2499 {
2500 err = RecordDataFromArgString( gRegisterRecord_Data, &context->dataPtr, &context->dataLen );
2501 require_noerr_quiet( err, exit );
2502 }
2503
2504 // Set remaining parameters.
2505
2506 context->recordName = gRegisterRecord_Name;
2507 context->ttl = (uint32_t) gRegisterRecord_TTL;
2508 context->lifetimeMs = gRegisterRecord_LifetimeMs;
2509
2510 // Get update data.
2511
2512 if( gRegisterRecord_UpdateData )
2513 {
2514 err = RecordDataFromArgString( gRegisterRecord_UpdateData, &context->updateDataPtr, &context->updateDataLen );
2515 require_noerr_quiet( err, exit );
2516
2517 context->updateTTL = (uint32_t) gRegisterRecord_UpdateTTL;
2518 context->updateDelayMs = gRegisterRecord_UpdateDelayMs;
2519 }
2520
2521 // Print prologue.
2522
2523 RegisterRecordPrintPrologue( context );
2524
2525 // Start operation.
2526
2527 err = DNSServiceRegisterRecord( context->conRef, &context->recordRef, context->flags, context->ifIndex,
2528 context->recordName, context->recordType, kDNSServiceClass_IN, (uint16_t) context->dataLen, context->dataPtr,
2529 context->ttl, RegisterRecordCallback, context );
2530 if( err )
2531 {
2532 FPrintF( stderr, "DNSServiceRegisterRecord() returned %#m\n", err );
2533 goto exit;
2534 }
2535
2536 dispatch_main();
2537
2538 exit:
2539 dispatch_source_forget( &signalSource );
2540 if( context ) RegisterRecordContextFree( context );
2541 if( err ) exit( 1 );
2542 }
2543
2544 //===========================================================================================================================
2545 // RegisterRecordPrintPrologue
2546 //===========================================================================================================================
2547
2548 static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext )
2549 {
2550 int infinite;
2551 char time[ kTimestampBufLen ];
2552 char ifName[ kInterfaceNameBufLen ];
2553
2554 InterfaceIndexToName( inContext->ifIndex, ifName );
2555
2556 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
2557 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
2558 FPrintF( stdout, "Name: %s\n", inContext->recordName );
2559 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->recordType ), inContext->recordType );
2560 FPrintF( stdout, "TTL: %u\n", inContext->ttl );
2561 FPrintF( stdout, "Data: %#H\n", inContext->dataPtr, (int) inContext->dataLen, INT_MAX );
2562 infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
2563 FPrintF( stdout, "Lifetime: %?s%?d ms\n", infinite, "∞", !infinite, inContext->lifetimeMs );
2564 if( inContext->updateDataPtr )
2565 {
2566 FPrintF( stdout, "\nUpdate record:\n" );
2567 FPrintF( stdout, " Delay: %d ms\n", ( inContext->updateDelayMs >= 0 ) ? inContext->updateDelayMs : 0 );
2568 FPrintF( stdout, " TTL: %u%?s\n",
2569 inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
2570 FPrintF( stdout, " RData: %#H\n", inContext->updateDataPtr, (int) inContext->updateDataLen, INT_MAX );
2571 }
2572 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
2573 FPrintF( stdout, "---\n" );
2574 }
2575
2576 //===========================================================================================================================
2577 // RegisterRecordContextFree
2578 //===========================================================================================================================
2579
2580 static void RegisterRecordContextFree( RegisterRecordContext *inContext )
2581 {
2582 DNSServiceForget( &inContext->conRef );
2583 ForgetMem( &inContext->dataPtr );
2584 ForgetMem( &inContext->updateDataPtr );
2585 free( inContext );
2586 }
2587
2588 //===========================================================================================================================
2589 // RegisterRecordCallback
2590 //===========================================================================================================================
2591
2592 static void
2593 RegisterRecordCallback(
2594 DNSServiceRef inSDRef,
2595 DNSRecordRef inRecordRef,
2596 DNSServiceFlags inFlags,
2597 DNSServiceErrorType inError,
2598 void * inContext )
2599 {
2600 RegisterRecordContext * context = (RegisterRecordContext *) inContext;
2601 char time[ kTimestampBufLen ];
2602
2603 Unused( inSDRef );
2604 Unused( inRecordRef );
2605 Unused( inFlags );
2606 Unused( context );
2607
2608 GetTimestampStr( time );
2609 FPrintF( stdout, "%s Record registration result (error %#m)\n", time, inError );
2610
2611 if( !context->didRegister && !inError )
2612 {
2613 context->didRegister = true;
2614 if( context->updateDataPtr )
2615 {
2616 if( context->updateDelayMs > 0 )
2617 {
2618 dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
2619 context, RegisterRecordUpdate );
2620 }
2621 else
2622 {
2623 RegisterRecordUpdate( context );
2624 }
2625 }
2626 if( context->lifetimeMs == 0 )
2627 {
2628 Exit( kExitReason_TimeLimit );
2629 }
2630 else if( context->lifetimeMs > 0 )
2631 {
2632 dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
2633 kExitReason_TimeLimit, Exit );
2634 }
2635 }
2636 }
2637
2638 //===========================================================================================================================
2639 // RegisterRecordUpdate
2640 //===========================================================================================================================
2641
2642 static void RegisterRecordUpdate( void *inContext )
2643 {
2644 OSStatus err;
2645 RegisterRecordContext * const context = (RegisterRecordContext *) inContext;
2646
2647 err = DNSServiceUpdateRecord( context->conRef, context->recordRef, 0, (uint16_t) context->updateDataLen,
2648 context->updateDataPtr, context->updateTTL );
2649 require_noerr( err, exit );
2650
2651 exit:
2652 if( err ) exit( 1 );
2653 }
2654
2655 //===========================================================================================================================
2656 // ResolveCmd
2657 //===========================================================================================================================
2658
2659 typedef struct
2660 {
2661 DNSServiceRef mainRef; // Main sdRef for shared connections.
2662 DNSServiceRef opRef; // sdRef for the DNSServiceResolve operation.
2663 DNSServiceFlags flags; // Flags argument for DNSServiceResolve().
2664 const char * name; // Service name argument for DNSServiceResolve().
2665 const char * type; // Service type argument for DNSServiceResolve().
2666 const char * domain; // Domain argument for DNSServiceResolve().
2667 uint32_t ifIndex; // Interface index argument for DNSServiceResolve().
2668 int timeLimitSecs; // Time limit for the DNSServiceResolve operation in seconds.
2669
2670 } ResolveContext;
2671
2672 static void ResolvePrintPrologue( const ResolveContext *inContext );
2673 static void ResolveContextFree( ResolveContext *inContext );
2674 static void DNSSD_API
2675 ResolveCallback(
2676 DNSServiceRef inSDRef,
2677 DNSServiceFlags inFlags,
2678 uint32_t inInterfaceIndex,
2679 DNSServiceErrorType inError,
2680 const char * inFullName,
2681 const char * inHostname,
2682 uint16_t inPort,
2683 uint16_t inTXTLen,
2684 const unsigned char * inTXTPtr,
2685 void * inContext );
2686
2687 static void ResolveCmd( void )
2688 {
2689 OSStatus err;
2690 DNSServiceRef sdRef;
2691 ResolveContext * context = NULL;
2692 dispatch_source_t signalSource = NULL;
2693 int useMainConnection;
2694
2695 // Set up SIGINT handler.
2696
2697 signal( SIGINT, SIG_IGN );
2698 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
2699 require_noerr( err, exit );
2700 dispatch_resume( signalSource );
2701
2702 // Create context.
2703
2704 context = (ResolveContext *) calloc( 1, sizeof( *context ) );
2705 require_action( context, exit, err = kNoMemoryErr );
2706
2707 // Check command parameters.
2708
2709 if( gResolve_TimeLimitSecs < 0 )
2710 {
2711 FPrintF( stderr, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs );
2712 err = kParamErr;
2713 goto exit;
2714 }
2715
2716 // Create main connection.
2717
2718 if( gConnectionOpt )
2719 {
2720 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
2721 require_noerr_quiet( err, exit );
2722 useMainConnection = true;
2723 }
2724 else
2725 {
2726 useMainConnection = false;
2727 }
2728
2729 // Get flags.
2730
2731 context->flags = GetDNSSDFlagsFromOpts();
2732 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
2733
2734 // Get interface index.
2735
2736 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
2737 require_noerr_quiet( err, exit );
2738
2739 // Set remaining parameters.
2740
2741 context->name = gResolve_Name;
2742 context->type = gResolve_Type;
2743 context->domain = gResolve_Domain;
2744 context->timeLimitSecs = gResolve_TimeLimitSecs;
2745
2746 // Print prologue.
2747
2748 ResolvePrintPrologue( context );
2749
2750 // Start operation.
2751
2752 if( useMainConnection ) sdRef = context->mainRef;
2753 err = DNSServiceResolve( &sdRef, context->flags, context->ifIndex, context->name, context->type, context->domain,
2754 ResolveCallback, NULL );
2755 require_noerr( err, exit );
2756
2757 context->opRef = sdRef;
2758 if( !useMainConnection )
2759 {
2760 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
2761 require_noerr( err, exit );
2762 }
2763
2764 // Set time limit.
2765
2766 if( context->timeLimitSecs > 0 )
2767 {
2768 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
2769 kExitReason_TimeLimit, Exit );
2770 }
2771 dispatch_main();
2772
2773 exit:
2774 dispatch_source_forget( &signalSource );
2775 if( context ) ResolveContextFree( context );
2776 if( err ) exit( 1 );
2777 }
2778
2779 //===========================================================================================================================
2780 // ReconfirmCmd
2781 //===========================================================================================================================
2782
2783 static void ReconfirmCmd( void )
2784 {
2785 OSStatus err;
2786 uint8_t * rdataPtr = NULL;
2787 size_t rdataLen = 0;
2788 DNSServiceFlags flags;
2789 uint32_t ifIndex;
2790 uint16_t type, class;
2791 char ifName[ kInterfaceNameBufLen ];
2792
2793 // Get flags.
2794
2795 flags = GetDNSSDFlagsFromOpts();
2796
2797 // Get interface index.
2798
2799 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
2800 require_noerr_quiet( err, exit );
2801
2802 // Get record type.
2803
2804 err = RecordTypeFromArgString( gReconfirmRecord_Type, &type );
2805 require_noerr( err, exit );
2806
2807 // Get record data.
2808
2809 if( gReconfirmRecord_Data )
2810 {
2811 err = RecordDataFromArgString( gReconfirmRecord_Data, &rdataPtr, &rdataLen );
2812 require_noerr_quiet( err, exit );
2813 }
2814
2815 // Get record class.
2816
2817 if( gReconfirmRecord_Class )
2818 {
2819 err = RecordClassFromArgString( gReconfirmRecord_Class, &class );
2820 require_noerr( err, exit );
2821 }
2822 else
2823 {
2824 class = kDNSServiceClass_IN;
2825 }
2826
2827 // Print prologue.
2828
2829 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
2830 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
2831 FPrintF( stdout, "Name: %s\n", gReconfirmRecord_Name );
2832 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
2833 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
2834 FPrintF( stdout, "Data: %#H\n", rdataPtr, (int) rdataLen, INT_MAX );
2835 FPrintF( stdout, "---\n" );
2836
2837 err = DNSServiceReconfirmRecord( flags, ifIndex, gReconfirmRecord_Name, type, class, (uint16_t) rdataLen, rdataPtr );
2838 FPrintF( stdout, "Error: %#m\n", err );
2839
2840 exit:
2841 FreeNullSafe( rdataPtr );
2842 if( err ) exit( 1 );
2843 }
2844
2845 //===========================================================================================================================
2846 // ResolvePrintPrologue
2847 //===========================================================================================================================
2848
2849 static void ResolvePrintPrologue( const ResolveContext *inContext )
2850 {
2851 const int timeLimitSecs = inContext->timeLimitSecs;
2852 char ifName[ kInterfaceNameBufLen ];
2853 char time[ kTimestampBufLen ];
2854
2855 InterfaceIndexToName( inContext->ifIndex, ifName );
2856
2857 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
2858 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
2859 FPrintF( stdout, "Name: %s\n", inContext->name );
2860 FPrintF( stdout, "Type: %s\n", inContext->type );
2861 FPrintF( stdout, "Domain: %s\n", inContext->domain );
2862 FPrintF( stdout, "Time limit: " );
2863 if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
2864 else FPrintF( stdout, "∞\n" );
2865 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
2866 FPrintF( stdout, "---\n" );
2867 }
2868
2869 //===========================================================================================================================
2870 // ResolveContextFree
2871 //===========================================================================================================================
2872
2873 static void ResolveContextFree( ResolveContext *inContext )
2874 {
2875 DNSServiceForget( &inContext->opRef );
2876 DNSServiceForget( &inContext->mainRef );
2877 free( inContext );
2878 }
2879
2880 //===========================================================================================================================
2881 // ResolveCallback
2882 //===========================================================================================================================
2883
2884 static void DNSSD_API
2885 ResolveCallback(
2886 DNSServiceRef inSDRef,
2887 DNSServiceFlags inFlags,
2888 uint32_t inInterfaceIndex,
2889 DNSServiceErrorType inError,
2890 const char * inFullName,
2891 const char * inHostname,
2892 uint16_t inPort,
2893 uint16_t inTXTLen,
2894 const unsigned char * inTXTPtr,
2895 void * inContext )
2896 {
2897 char time[ kTimestampBufLen ];
2898 char errorStr[ 64 ];
2899
2900 Unused( inSDRef );
2901 Unused( inFlags );
2902 Unused( inContext );
2903
2904 GetTimestampStr( time );
2905
2906 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
2907
2908 FPrintF( stdout, "%s: %s can be reached at %s:%u (interface %d)%?s\n",
2909 time, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
2910 if( inTXTLen == 1 )
2911 {
2912 FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
2913 }
2914 else
2915 {
2916 FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
2917 }
2918 }
2919
2920 //===========================================================================================================================
2921 // GetAddrInfoPOSIXCmd
2922 //===========================================================================================================================
2923
2924 #define AddressFamilyStr( X ) ( \
2925 ( (X) == AF_INET ) ? "inet" : \
2926 ( (X) == AF_INET6 ) ? "inet6" : \
2927 ( (X) == AF_UNSPEC ) ? "unspec" : \
2928 "???" )
2929
2930 typedef struct
2931 {
2932 unsigned int flag;
2933 const char * str;
2934
2935 } FlagStringPair;
2936
2937 #define CaseFlagStringify( X ) { (X), # X }
2938
2939 const FlagStringPair kGAIPOSIXFlagStringPairs[] =
2940 {
2941 #if( defined( AI_UNUSABLE ) )
2942 CaseFlagStringify( AI_UNUSABLE ),
2943 #endif
2944 CaseFlagStringify( AI_NUMERICSERV ),
2945 CaseFlagStringify( AI_V4MAPPED ),
2946 CaseFlagStringify( AI_ADDRCONFIG ),
2947 #if( defined( AI_V4MAPPED_CFG ) )
2948 CaseFlagStringify( AI_V4MAPPED_CFG ),
2949 #endif
2950 CaseFlagStringify( AI_ALL ),
2951 CaseFlagStringify( AI_NUMERICHOST ),
2952 CaseFlagStringify( AI_CANONNAME ),
2953 CaseFlagStringify( AI_PASSIVE ),
2954 { 0, NULL }
2955 };
2956
2957 static void GetAddrInfoPOSIXCmd( void )
2958 {
2959 OSStatus err;
2960 struct addrinfo hints;
2961 const struct addrinfo * addrInfo;
2962 struct addrinfo * addrInfoList = NULL;
2963 const FlagStringPair * pair;
2964 char time[ kTimestampBufLen ];
2965
2966 memset( &hints, 0, sizeof( hints ) );
2967 hints.ai_socktype = SOCK_STREAM;
2968
2969 // Set hints address family.
2970
2971 if( !gGAIPOSIX_Family ) hints.ai_family = AF_UNSPEC;
2972 else if( strcasecmp( gGAIPOSIX_Family, "inet" ) == 0 ) hints.ai_family = AF_INET;
2973 else if( strcasecmp( gGAIPOSIX_Family, "inet6" ) == 0 ) hints.ai_family = AF_INET6;
2974 else if( strcasecmp( gGAIPOSIX_Family, "unspec" ) == 0 ) hints.ai_family = AF_UNSPEC;
2975 else
2976 {
2977 FPrintF( stderr, "Invalid address family: %s.\n", gGAIPOSIX_Family );
2978 err = kParamErr;
2979 goto exit;
2980 }
2981
2982 // Set hints flags.
2983
2984 if( gGAIPOSIXFlag_AddrConfig ) hints.ai_flags |= AI_ADDRCONFIG;
2985 if( gGAIPOSIXFlag_All ) hints.ai_flags |= AI_ALL;
2986 if( gGAIPOSIXFlag_CanonName ) hints.ai_flags |= AI_CANONNAME;
2987 if( gGAIPOSIXFlag_NumericHost ) hints.ai_flags |= AI_NUMERICHOST;
2988 if( gGAIPOSIXFlag_NumericServ ) hints.ai_flags |= AI_NUMERICSERV;
2989 if( gGAIPOSIXFlag_Passive ) hints.ai_flags |= AI_PASSIVE;
2990 if( gGAIPOSIXFlag_V4Mapped ) hints.ai_flags |= AI_V4MAPPED;
2991 #if( defined( AI_V4MAPPED_CFG ) )
2992 if( gGAIPOSIXFlag_V4MappedCFG ) hints.ai_flags |= AI_V4MAPPED_CFG;
2993 #endif
2994 #if( defined( AI_DEFAULT ) )
2995 if( gGAIPOSIXFlag_Default ) hints.ai_flags |= AI_DEFAULT;
2996 #endif
2997 #if( defined( AI_UNUSABLE ) )
2998 if( gGAIPOSIXFlag_Unusable ) hints.ai_flags |= AI_UNUSABLE;
2999 #endif
3000
3001 // Print prologue.
3002
3003 FPrintF( stdout, "Hostname: %s\n", gGAIPOSIX_HostName );
3004 FPrintF( stdout, "Servname: %s\n", gGAIPOSIX_ServName );
3005 FPrintF( stdout, "Address family: %s\n", AddressFamilyStr( hints.ai_family ) );
3006 FPrintF( stdout, "Flags: 0x%X < ", hints.ai_flags );
3007 for( pair = kGAIPOSIXFlagStringPairs; pair->str != NULL; ++pair )
3008 {
3009 if( ( (unsigned int) hints.ai_flags ) & pair->flag ) FPrintF( stdout, "%s ", pair->str );
3010 }
3011 FPrintF( stdout, ">\n" );
3012 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
3013 FPrintF( stdout, "---\n" );
3014
3015 // Call getaddrinfo().
3016
3017 err = getaddrinfo( gGAIPOSIX_HostName, gGAIPOSIX_ServName, &hints, &addrInfoList );
3018 GetTimestampStr( time );
3019 if( err )
3020 {
3021 FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
3022 }
3023 else
3024 {
3025 int addrCount = 0;
3026
3027 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next ) { ++addrCount; }
3028
3029 FPrintF( stdout, "Addresses (%d total):\n", addrCount );
3030 for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next )
3031 {
3032 FPrintF( stdout, "%##a\n", addrInfo->ai_addr );
3033 }
3034 }
3035 FPrintF( stdout, "---\n" );
3036 FPrintF( stdout, "End time: %s\n", time );
3037
3038 exit:
3039 if( addrInfoList ) freeaddrinfo( addrInfoList );
3040 if( err ) exit( 1 );
3041 }
3042
3043 //===========================================================================================================================
3044 // ReverseLookupCmd
3045 //===========================================================================================================================
3046
3047 static void ReverseLookupCmd( void )
3048 {
3049 OSStatus err;
3050 QueryRecordContext * context = NULL;
3051 DNSServiceRef sdRef;
3052 dispatch_source_t signalSource = NULL;
3053 uint32_t ipv4Addr;
3054 uint8_t ipv6Addr[ 16 ];
3055 char recordName[ ( 16 * 4 ) + 9 + 1 ];
3056 int useMainConnection;
3057 const char * endPtr;
3058
3059 // Set up SIGINT handler.
3060
3061 signal( SIGINT, SIG_IGN );
3062 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3063 require_noerr( err, exit );
3064 dispatch_resume( signalSource );
3065
3066 // Create context.
3067
3068 context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
3069 require_action( context, exit, err = kNoMemoryErr );
3070
3071 // Check command parameters.
3072
3073 if( gReverseLookup_TimeLimitSecs < 0 )
3074 {
3075 FPrintF( stderr, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs );
3076 err = kParamErr;
3077 goto exit;
3078 }
3079
3080 // Create main connection.
3081
3082 if( gConnectionOpt )
3083 {
3084 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3085 require_noerr_quiet( err, exit );
3086 useMainConnection = true;
3087 }
3088 else
3089 {
3090 useMainConnection = false;
3091 }
3092
3093 // Get flags.
3094
3095 context->flags = GetDNSSDFlagsFromOpts();
3096 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3097
3098 // Get interface index.
3099
3100 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3101 require_noerr_quiet( err, exit );
3102
3103 // Create reverse lookup record name.
3104
3105 err = StringToIPv4Address( gReverseLookup_IPAddr, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix,
3106 &ipv4Addr, NULL, NULL, NULL, &endPtr );
3107 if( err || ( *endPtr != '\0' ) )
3108 {
3109 char * dst;
3110 int i;
3111
3112 err = StringToIPv6Address( gReverseLookup_IPAddr,
3113 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
3114 ipv6Addr, NULL, NULL, NULL, &endPtr );
3115 if( err || ( *endPtr != '\0' ) )
3116 {
3117 FPrintF( stderr, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr );
3118 err = kParamErr;
3119 goto exit;
3120 }
3121 dst = recordName;
3122 for( i = 15; i >= 0; --i )
3123 {
3124 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] & 0x0F ];
3125 *dst++ = '.';
3126 *dst++ = kHexDigitsLowercase[ ipv6Addr[ i ] >> 4 ];
3127 *dst++ = '.';
3128 }
3129 strcpy( dst, "ip6.arpa." );
3130 check( ( strlen( recordName ) + 1 ) <= sizeof( recordName ) );
3131 }
3132 else
3133 {
3134 SNPrintF( recordName, sizeof( recordName ), "%u.%u.%u.%u.in-addr.arpa.",
3135 ipv4Addr & 0xFF,
3136 ( ipv4Addr >> 8 ) & 0xFF,
3137 ( ipv4Addr >> 16 ) & 0xFF,
3138 ( ipv4Addr >> 24 ) & 0xFF );
3139 }
3140
3141 // Set remaining parameters.
3142
3143 context->recordName = recordName;
3144 context->recordType = kDNSServiceType_PTR;
3145 context->timeLimitSecs = gReverseLookup_TimeLimitSecs;
3146 context->oneShotMode = gReverseLookup_OneShot ? true : false;
3147
3148 // Print prologue.
3149
3150 QueryRecordPrintPrologue( context );
3151
3152 // Start operation.
3153
3154 if( useMainConnection ) sdRef = context->mainRef;
3155 err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
3156 kDNSServiceClass_IN, QueryRecordCallback, context );
3157 require_noerr( err, exit );
3158
3159 context->opRef = sdRef;
3160 if( !useMainConnection )
3161 {
3162 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3163 require_noerr( err, exit );
3164 }
3165
3166 // Set time limit.
3167
3168 if( context->timeLimitSecs > 0 )
3169 {
3170 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
3171 kExitReason_TimeLimit, Exit );
3172 }
3173 dispatch_main();
3174
3175 exit:
3176 dispatch_source_forget( &signalSource );
3177 if( context ) QueryRecordContextFree( context );
3178 if( err ) exit( 1 );
3179 }
3180
3181 //===========================================================================================================================
3182 // PortMappingCmd
3183 //===========================================================================================================================
3184
3185 typedef struct
3186 {
3187 DNSServiceRef mainRef; // Main sdRef for shared connection.
3188 DNSServiceRef opRef; // sdRef for the DNSServiceNATPortMappingCreate operation.
3189 DNSServiceFlags flags; // Flags for DNSServiceNATPortMappingCreate operation.
3190 uint32_t ifIndex; // Interface index argument for DNSServiceNATPortMappingCreate operation.
3191 DNSServiceProtocol protocols; // Protocols argument for DNSServiceNATPortMappingCreate operation.
3192 uint32_t ttl; // TTL argument for DNSServiceNATPortMappingCreate operation.
3193 uint16_t internalPort; // Internal port argument for DNSServiceNATPortMappingCreate operation.
3194 uint16_t externalPort; // External port argument for DNSServiceNATPortMappingCreate operation.
3195 Boolean printedHeader; // True if results header was printed.
3196
3197 } PortMappingContext;
3198
3199 static void PortMappingPrintPrologue( const PortMappingContext *inContext );
3200 static void PortMappingContextFree( PortMappingContext *inContext );
3201 static void DNSSD_API
3202 PortMappingCallback(
3203 DNSServiceRef inSDRef,
3204 DNSServiceFlags inFlags,
3205 uint32_t inInterfaceIndex,
3206 DNSServiceErrorType inError,
3207 uint32_t inExternalIPv4Address,
3208 DNSServiceProtocol inProtocol,
3209 uint16_t inInternalPort,
3210 uint16_t inExternalPort,
3211 uint32_t inTTL,
3212 void * inContext );
3213
3214 static void PortMappingCmd( void )
3215 {
3216 OSStatus err;
3217 PortMappingContext * context = NULL;
3218 DNSServiceRef sdRef;
3219 dispatch_source_t signalSource = NULL;
3220 int useMainConnection;
3221
3222 // Set up SIGINT handler.
3223
3224 signal( SIGINT, SIG_IGN );
3225 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
3226 require_noerr( err, exit );
3227 dispatch_resume( signalSource );
3228
3229 // Create context.
3230
3231 context = (PortMappingContext *) calloc( 1, sizeof( *context ) );
3232 require_action( context, exit, err = kNoMemoryErr );
3233
3234 // Check command parameters.
3235
3236 if( ( gPortMapping_InternalPort < 0 ) || ( gPortMapping_InternalPort > UINT16_MAX ) )
3237 {
3238 FPrintF( stderr, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort );
3239 err = kParamErr;
3240 goto exit;
3241 }
3242
3243 if( ( gPortMapping_ExternalPort < 0 ) || ( gPortMapping_ExternalPort > UINT16_MAX ) )
3244 {
3245 FPrintF( stderr, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort );
3246 err = kParamErr;
3247 goto exit;
3248 }
3249
3250 // Create main connection.
3251
3252 if( gConnectionOpt )
3253 {
3254 err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3255 require_noerr_quiet( err, exit );
3256 useMainConnection = true;
3257 }
3258 else
3259 {
3260 useMainConnection = false;
3261 }
3262
3263 // Get flags.
3264
3265 context->flags = GetDNSSDFlagsFromOpts();
3266 if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3267
3268 // Get interface index.
3269
3270 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3271 require_noerr_quiet( err, exit );
3272
3273 // Set remaining parameters.
3274
3275 if( gPortMapping_ProtocolTCP ) context->protocols |= kDNSServiceProtocol_TCP;
3276 if( gPortMapping_ProtocolUDP ) context->protocols |= kDNSServiceProtocol_UDP;
3277 context->ttl = (uint32_t) gPortMapping_TTL;
3278 context->internalPort = (uint16_t) gPortMapping_InternalPort;
3279 context->externalPort = (uint16_t) gPortMapping_ExternalPort;
3280
3281 // Print prologue.
3282
3283 PortMappingPrintPrologue( context );
3284
3285 // Start operation.
3286
3287 if( useMainConnection ) sdRef = context->mainRef;
3288 err = DNSServiceNATPortMappingCreate( &sdRef, context->flags, context->ifIndex, context->protocols,
3289 htons( context->internalPort ), htons( context->externalPort ), context->ttl, PortMappingCallback, context );
3290 require_noerr( err, exit );
3291
3292 context->opRef = sdRef;
3293 if( !useMainConnection )
3294 {
3295 err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3296 require_noerr( err, exit );
3297 }
3298
3299 dispatch_main();
3300
3301 exit:
3302 dispatch_source_forget( &signalSource );
3303 if( context ) PortMappingContextFree( context );
3304 if( err ) exit( 1 );
3305 }
3306
3307 //===========================================================================================================================
3308 // PortMappingPrintPrologue
3309 //===========================================================================================================================
3310
3311 static void PortMappingPrintPrologue( const PortMappingContext *inContext )
3312 {
3313 char ifName[ kInterfaceNameBufLen ];
3314 char time[ kTimestampBufLen ];
3315
3316 InterfaceIndexToName( inContext->ifIndex, ifName );
3317
3318 FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors );
3319 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3320 FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors );
3321 FPrintF( stdout, "Internal Port: %u\n", inContext->internalPort );
3322 FPrintF( stdout, "External Port: %u\n", inContext->externalPort );
3323 FPrintF( stdout, "TTL: %u%?s\n", inContext->ttl, !inContext->ttl, " (system will use a default value.)" );
3324 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
3325 FPrintF( stdout, "---\n" );
3326
3327 }
3328
3329 //===========================================================================================================================
3330 // PortMappingContextFree
3331 //===========================================================================================================================
3332
3333 static void PortMappingContextFree( PortMappingContext *inContext )
3334 {
3335 DNSServiceForget( &inContext->opRef );
3336 DNSServiceForget( &inContext->mainRef );
3337 free( inContext );
3338 }
3339
3340 //===========================================================================================================================
3341 // PortMappingCallback
3342 //===========================================================================================================================
3343
3344 static void DNSSD_API
3345 PortMappingCallback(
3346 DNSServiceRef inSDRef,
3347 DNSServiceFlags inFlags,
3348 uint32_t inInterfaceIndex,
3349 DNSServiceErrorType inError,
3350 uint32_t inExternalIPv4Address,
3351 DNSServiceProtocol inProtocol,
3352 uint16_t inInternalPort,
3353 uint16_t inExternalPort,
3354 uint32_t inTTL,
3355 void * inContext )
3356 {
3357 PortMappingContext * const context = (PortMappingContext *) inContext;
3358 char time[ kTimestampBufLen ];
3359 char errorStr[ 128 ];
3360
3361 Unused( inSDRef );
3362 Unused( inFlags );
3363
3364 GetTimestampStr( time );
3365
3366 if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " (error: %#m)", inError );
3367 if( !context->printedHeader )
3368 {
3369 FPrintF( stdout, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" );
3370 context->printedHeader = true;
3371 }
3372 FPrintF( stdout, "%-26s %2u %7u %15.4a %7u %6u %#{flags}%?s\n",
3373 time, inInterfaceIndex, ntohs( inInternalPort), &inExternalIPv4Address, ntohs( inExternalPort ), inTTL,
3374 inProtocol, kDNSServiceProtocolDescriptors, inError, errorStr );
3375 }
3376
3377 //===========================================================================================================================
3378 // BrowseAllCmd
3379 //===========================================================================================================================
3380
3381 typedef struct BrowseDomain BrowseDomain;
3382 typedef struct BrowseType BrowseType;
3383 typedef struct BrowseOp BrowseOp;
3384 typedef struct BrowseInstance BrowseInstance;
3385 typedef struct BrowseIPAddr BrowseIPAddr;
3386
3387 typedef struct
3388 {
3389 int refCount;
3390 DNSServiceRef mainRef;
3391 DNSServiceRef domainsQuery;
3392 const char * domain;
3393 BrowseDomain * domainList;
3394 char ** serviceTypes;
3395 size_t serviceTypesCount;
3396 dispatch_source_t exitTimer;
3397 uint32_t ifIndex;
3398 int pendingConnectCount;
3399 int browseTimeSecs;
3400 int maxConnectTimeSecs;
3401 Boolean includeAWDL;
3402 Boolean useColoredText;
3403
3404 } BrowseAllContext;
3405
3406 struct BrowseDomain
3407 {
3408 BrowseDomain * next;
3409 char * name;
3410 DNSServiceRef servicesQuery;
3411 BrowseAllContext * context;
3412 BrowseType * typeList;
3413 };
3414
3415 struct BrowseType
3416 {
3417 BrowseType * next;
3418 char * name;
3419 BrowseOp * browseList;
3420 };
3421
3422 struct BrowseOp
3423 {
3424 BrowseOp * next;
3425 BrowseAllContext * context;
3426 DNSServiceRef browse;
3427 uint64_t startTicks;
3428 BrowseInstance * instanceList;
3429 uint32_t ifIndex;
3430 Boolean isTCP;
3431 };
3432
3433 struct BrowseInstance
3434 {
3435 BrowseInstance * next;
3436 BrowseAllContext * context;
3437 char * name;
3438 uint64_t foundTicks;
3439 DNSServiceRef resolve;
3440 uint64_t resolveStartTicks;
3441 uint64_t resolveDoneTicks;
3442 DNSServiceRef getAddr;
3443 uint64_t getAddrStartTicks;
3444 BrowseIPAddr * addrList;
3445 uint8_t * txtPtr;
3446 size_t txtLen;
3447 char * hostname;
3448 uint32_t ifIndex;
3449 uint16_t port;
3450 Boolean isTCP;
3451 };
3452
3453 typedef enum
3454 {
3455 kConnectStatus_None = 0,
3456 kConnectStatus_Pending = 1,
3457 kConnectStatus_Succeeded = 2,
3458 kConnectStatus_Failed = 3
3459
3460 } ConnectStatus;
3461
3462 struct BrowseIPAddr
3463 {
3464 BrowseIPAddr * next;
3465 sockaddr_ip sip;
3466 int refCount;
3467 BrowseAllContext * context;
3468 uint64_t foundTicks;
3469 AsyncConnectionRef connection;
3470 ConnectStatus connectStatus;
3471 CFTimeInterval connectTimeSecs;
3472 OSStatus connectError;
3473 };
3474
3475 static void BrowseAllPrintPrologue( const BrowseAllContext *inContext );
3476 static void DNSSD_API
3477 BrowseAllQueryDomainsCallback(
3478 DNSServiceRef inSDRef,
3479 DNSServiceFlags inFlags,
3480 uint32_t inInterfaceIndex,
3481 DNSServiceErrorType inError,
3482 const char * inFullName,
3483 uint16_t inType,
3484 uint16_t inClass,
3485 uint16_t inRDataLen,
3486 const void * inRDataPtr,
3487 uint32_t inTTL,
3488 void * inContext );
3489 static void DNSSD_API
3490 BrowseAllQueryCallback(
3491 DNSServiceRef inSDRef,
3492 DNSServiceFlags inFlags,
3493 uint32_t inInterfaceIndex,
3494 DNSServiceErrorType inError,
3495 const char * inFullName,
3496 uint16_t inType,
3497 uint16_t inClass,
3498 uint16_t inRDataLen,
3499 const void * inRDataPtr,
3500 uint32_t inTTL,
3501 void * inContext );
3502 static void DNSSD_API
3503 BrowseAllBrowseCallback(
3504 DNSServiceRef inSDRef,
3505 DNSServiceFlags inFlags,
3506 uint32_t inInterfaceIndex,
3507 DNSServiceErrorType inError,
3508 const char * inName,
3509 const char * inRegType,
3510 const char * inDomain,
3511 void * inContext );
3512 static void DNSSD_API
3513 BrowseAllResolveCallback(
3514 DNSServiceRef inSDRef,
3515 DNSServiceFlags inFlags,
3516 uint32_t inInterfaceIndex,
3517 DNSServiceErrorType inError,
3518 const char * inFullName,
3519 const char * inHostname,
3520 uint16_t inPort,
3521 uint16_t inTXTLen,
3522 const unsigned char * inTXTPtr,
3523 void * inContext );
3524 static void DNSSD_API
3525 BrowseAllGAICallback(
3526 DNSServiceRef inSDRef,
3527 DNSServiceFlags inFlags,
3528 uint32_t inInterfaceIndex,
3529 DNSServiceErrorType inError,
3530 const char * inHostname,
3531 const struct sockaddr * inSockAddr,
3532 uint32_t inTTL,
3533 void * inContext );
3534 static void BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg );
3535 static void BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg );
3536 static void BrowseAllStop( void *inContext );
3537 static void BrowseAllExit( void *inContext );
3538 static OSStatus BrowseAllAddDomain( BrowseAllContext *inContext, const char *inName );
3539 static OSStatus BrowseAllRemoveDomain( BrowseAllContext *inContext, const char *inName );
3540 static void BrowseAllContextRelease( BrowseAllContext *inContext );
3541 static OSStatus
3542 BrowseAllAddServiceType(
3543 BrowseAllContext * inContext,
3544 BrowseDomain * inDomain,
3545 const char * inName,
3546 uint32_t inIfIndex,
3547 Boolean inIncludeAWDL );
3548 static OSStatus
3549 BrowseAllRemoveServiceType(
3550 BrowseAllContext * inContext,
3551 BrowseDomain * inDomain,
3552 const char * inName,
3553 uint32_t inIfIndex );
3554 static OSStatus
3555 BrowseAllAddServiceInstance(
3556 BrowseAllContext * inContext,
3557 BrowseOp * inBrowse,
3558 const char * inName,
3559 const char * inRegType,
3560 const char * inDomain,
3561 uint32_t inIfIndex );
3562 static OSStatus
3563 BrowseAllRemoveServiceInstance(
3564 BrowseAllContext * inContext,
3565 BrowseOp * inBrowse,
3566 const char * inName,
3567 uint32_t inIfIndex );
3568 static OSStatus
3569 BrowseAllAddIPAddress(
3570 BrowseAllContext * inContext,
3571 BrowseInstance * inInstance,
3572 const struct sockaddr * inSockAddr );
3573 static OSStatus
3574 BrowseAllRemoveIPAddress(
3575 BrowseAllContext * inContext,
3576 BrowseInstance * inInstance,
3577 const struct sockaddr * inSockAddr );
3578 static void BrowseDomainFree( BrowseDomain *inDomain );
3579 static void BrowseTypeFree( BrowseType *inType );
3580 static void BrowseOpFree( BrowseOp *inBrowse );
3581 static void BrowseInstanceFree( BrowseInstance *inInstance );
3582 static void BrowseIPAddrRelease( BrowseIPAddr *inAddr );
3583 static void BrowseIPAddrReleaseList( BrowseIPAddr *inList );
3584
3585 #define ForgetIPAddressList( X ) ForgetCustom( X, BrowseIPAddrReleaseList )
3586 #define ForgetBrowseAllContext( X ) ForgetCustom( X, BrowseAllContextRelease )
3587
3588 #define kBrowseAllOpenFileMin 4096
3589
3590 static void BrowseAllCmd( void )
3591 {
3592 OSStatus err;
3593 BrowseAllContext * context = NULL;
3594
3595 // Check command parameters.
3596
3597 if( gBrowseAll_BrowseTimeSecs <= 0 )
3598 {
3599 FPrintF( stdout, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs );
3600 err = kParamErr;
3601 goto exit;
3602 }
3603
3604 #if( TARGET_OS_POSIX )
3605 // Set open file minimum.
3606
3607 {
3608 struct rlimit fdLimits;
3609
3610 err = getrlimit( RLIMIT_NOFILE, &fdLimits );
3611 err = map_global_noerr_errno( err );
3612 require_noerr( err, exit );
3613
3614 if( fdLimits.rlim_cur < kBrowseAllOpenFileMin )
3615 {
3616 fdLimits.rlim_cur = kBrowseAllOpenFileMin;
3617 err = setrlimit( RLIMIT_NOFILE, &fdLimits );
3618 err = map_global_noerr_errno( err );
3619 require_noerr( err, exit );
3620 }
3621 }
3622 #endif
3623
3624 context = (BrowseAllContext *) calloc( 1, sizeof( *context ) );
3625 require_action( context, exit, err = kNoMemoryErr );
3626
3627 context->refCount = 1;
3628 context->domain = gBrowseAll_Domain;
3629 context->serviceTypes = gBrowseAll_ServiceTypes;
3630 context->serviceTypesCount = gBrowseAll_ServiceTypesCount;
3631 gBrowseAll_ServiceTypes = NULL;
3632 gBrowseAll_ServiceTypesCount = 0;
3633 context->browseTimeSecs = gBrowseAll_BrowseTimeSecs;
3634 context->maxConnectTimeSecs = gBrowseAll_MaxConnectTimeSecs;
3635 context->includeAWDL = gBrowseAll_IncludeAWDL ? true : false;
3636 #if( TARGET_OS_POSIX )
3637 context->useColoredText = isatty( STDOUT_FILENO ) ? true : false;
3638 #endif
3639
3640 err = DNSServiceCreateConnection( &context->mainRef );
3641 require_noerr( err, exit );
3642
3643 err = DNSServiceSetDispatchQueue( context->mainRef, dispatch_get_main_queue() );
3644 require_noerr( err, exit );
3645
3646 // Set interface index.
3647
3648 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3649 require_noerr_quiet( err, exit );
3650
3651 BrowseAllPrintPrologue( context );
3652
3653 if( context->domain )
3654 {
3655 err = BrowseAllAddDomain( context, context->domain );
3656 require_noerr( err, exit );
3657 }
3658 else
3659 {
3660 DNSServiceRef sdRef;
3661
3662 sdRef = context->mainRef;
3663 err = DNSServiceQueryRecord( &sdRef, kDNSServiceFlagsShareConnection, kDNSServiceInterfaceIndexLocalOnly,
3664 "b._dns-sd._udp.local.", kDNSServiceType_PTR, kDNSServiceClass_IN, BrowseAllQueryDomainsCallback, context );
3665 require_noerr( err, exit );
3666
3667 context->domainsQuery = sdRef;
3668 }
3669
3670 dispatch_after_f( dispatch_time_seconds( context->browseTimeSecs ), dispatch_get_main_queue(), context, BrowseAllStop );
3671 dispatch_main();
3672
3673 exit:
3674 if( context ) BrowseAllContextRelease( context );
3675 if( err ) exit( 1 );
3676 }
3677
3678 //===========================================================================================================================
3679 // BrowseAllPrintPrologue
3680 //===========================================================================================================================
3681
3682 static void BrowseAllPrintPrologue( const BrowseAllContext *inContext )
3683 {
3684 size_t i;
3685 char ifName[ kInterfaceNameBufLen ];
3686 char time[ kTimestampBufLen ];
3687
3688 InterfaceIndexToName( inContext->ifIndex, ifName );
3689
3690 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName );
3691 FPrintF( stdout, "Service types: ");
3692 if( inContext->serviceTypesCount > 0 )
3693 {
3694 FPrintF( stdout, "%s", inContext->serviceTypes[ 0 ] );
3695 for( i = 1; i < inContext->serviceTypesCount; ++i ) FPrintF( stdout, ", %s", inContext->serviceTypes[ i ] );
3696 FPrintF( stdout, "\n" );
3697 }
3698 else
3699 {
3700 FPrintF( stdout, "all services\n" );
3701 }
3702 FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : "default domains" );
3703 FPrintF( stdout, "Browse time: %d second%?c\n", inContext->browseTimeSecs, inContext->browseTimeSecs != 1, 's' );
3704 FPrintF( stdout, "Max connect time: %d second%?c\n",
3705 inContext->maxConnectTimeSecs, inContext->maxConnectTimeSecs != 1, 's' );
3706 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
3707 FPrintF( stdout, "---\n" );
3708 }
3709
3710 //===========================================================================================================================
3711 // BrowseAllQueryDomainsCallback
3712 //===========================================================================================================================
3713
3714 static void DNSSD_API
3715 BrowseAllQueryDomainsCallback(
3716 DNSServiceRef inSDRef,
3717 DNSServiceFlags inFlags,
3718 uint32_t inInterfaceIndex,
3719 DNSServiceErrorType inError,
3720 const char * inFullName,
3721 uint16_t inType,
3722 uint16_t inClass,
3723 uint16_t inRDataLen,
3724 const void * inRDataPtr,
3725 uint32_t inTTL,
3726 void * inContext )
3727 {
3728 OSStatus err;
3729 BrowseAllContext * const context = (BrowseAllContext *) inContext;
3730 char domainStr[ kDNSServiceMaxDomainName ];
3731
3732 Unused( inSDRef );
3733 Unused( inInterfaceIndex );
3734 Unused( inFullName );
3735 Unused( inType );
3736 Unused( inClass );
3737 Unused( inTTL );
3738
3739 err = inError;
3740 require_noerr( err, exit );
3741
3742 err = DomainNameToString( inRDataPtr, ( (const uint8_t *) inRDataPtr ) + inRDataLen, domainStr, NULL );
3743 require_noerr( err, exit );
3744
3745 if( inFlags & kDNSServiceFlagsAdd )
3746 {
3747 err = BrowseAllAddDomain( context, domainStr );
3748 if( err == kDuplicateErr ) err = kNoErr;
3749 require_noerr( err, exit );
3750 }
3751 else
3752 {
3753 err = BrowseAllRemoveDomain( context, domainStr );
3754 if( err == kNotFoundErr ) err = kNoErr;
3755 require_noerr( err, exit );
3756 }
3757
3758 exit:
3759 if( err ) exit( 1 );
3760 }
3761
3762 //===========================================================================================================================
3763 // BrowseAllQueryCallback
3764 //===========================================================================================================================
3765
3766 static void DNSSD_API
3767 BrowseAllQueryCallback(
3768 DNSServiceRef inSDRef,
3769 DNSServiceFlags inFlags,
3770 uint32_t inInterfaceIndex,
3771 DNSServiceErrorType inError,
3772 const char * inFullName,
3773 uint16_t inType,
3774 uint16_t inClass,
3775 uint16_t inRDataLen,
3776 const void * inRDataPtr,
3777 uint32_t inTTL,
3778 void * inContext )
3779 {
3780 OSStatus err;
3781 BrowseDomain * const domain = (BrowseDomain *) inContext;
3782 const uint8_t * firstLabel;
3783 const uint8_t * secondLabel;
3784 char * serviceTypeStr = NULL;
3785 const uint8_t * const end = ( (uint8_t * ) inRDataPtr ) + inRDataLen;
3786
3787 Unused( inSDRef );
3788 Unused( inFullName );
3789 Unused( inTTL );
3790 Unused( inType );
3791 Unused( inClass );
3792
3793 err = inError;
3794 require_noerr( err, exit );
3795
3796 check( inType == kDNSServiceType_PTR );
3797 check( inClass == kDNSServiceClass_IN );
3798 require_action( inRDataLen > 0, exit, err = kSizeErr );
3799
3800 firstLabel = inRDataPtr;
3801 require_action_quiet( ( firstLabel + 1 + firstLabel[ 0 ] ) < end , exit, err = kUnderrunErr );
3802
3803 secondLabel = firstLabel + 1 + firstLabel[ 0 ];
3804 require_action_quiet( ( secondLabel + 1 + secondLabel[ 0 ] ) < end , exit, err = kUnderrunErr );
3805
3806 ASPrintF( &serviceTypeStr, "%#s.%#s", firstLabel, secondLabel );
3807 require_action( serviceTypeStr, exit, err = kNoMemoryErr );
3808
3809 if( inFlags & kDNSServiceFlagsAdd )
3810 {
3811 err = BrowseAllAddServiceType( domain->context, domain, serviceTypeStr, inInterfaceIndex, false );
3812 if( err == kDuplicateErr ) err = kNoErr;
3813 require_noerr( err, exit );
3814 }
3815 else
3816 {
3817 err = BrowseAllRemoveServiceType( domain->context, domain, serviceTypeStr, inInterfaceIndex );
3818 if( err == kNotFoundErr ) err = kNoErr;
3819 require_noerr( err, exit );
3820 }
3821
3822 exit:
3823 FreeNullSafe( serviceTypeStr );
3824 }
3825
3826 //===========================================================================================================================
3827 // BrowseAllBrowseCallback
3828 //===========================================================================================================================
3829
3830 static void DNSSD_API
3831 BrowseAllBrowseCallback(
3832 DNSServiceRef inSDRef,
3833 DNSServiceFlags inFlags,
3834 uint32_t inInterfaceIndex,
3835 DNSServiceErrorType inError,
3836 const char * inName,
3837 const char * inRegType,
3838 const char * inDomain,
3839 void * inContext )
3840 {
3841 OSStatus err;
3842 BrowseOp * const browse = (BrowseOp *) inContext;
3843
3844 Unused( inSDRef );
3845
3846 err = inError;
3847 require_noerr( err, exit );
3848
3849 if( inFlags & kDNSServiceFlagsAdd )
3850 {
3851 err = BrowseAllAddServiceInstance( browse->context, browse, inName, inRegType, inDomain, inInterfaceIndex );
3852 if( err == kDuplicateErr ) err = kNoErr;
3853 require_noerr( err, exit );
3854 }
3855 else
3856 {
3857 err = BrowseAllRemoveServiceInstance( browse->context, browse, inName, inInterfaceIndex );
3858 if( err == kNotFoundErr ) err = kNoErr;
3859 require_noerr( err, exit );
3860 }
3861
3862 exit:
3863 return;
3864 }
3865
3866 //===========================================================================================================================
3867 // BrowseAllResolveCallback
3868 //===========================================================================================================================
3869
3870 static void DNSSD_API
3871 BrowseAllResolveCallback(
3872 DNSServiceRef inSDRef,
3873 DNSServiceFlags inFlags,
3874 uint32_t inInterfaceIndex,
3875 DNSServiceErrorType inError,
3876 const char * inFullName,
3877 const char * inHostname,
3878 uint16_t inPort,
3879 uint16_t inTXTLen,
3880 const unsigned char * inTXTPtr,
3881 void * inContext )
3882 {
3883 OSStatus err;
3884 const uint64_t nowTicks = UpTicks();
3885 BrowseInstance * const instance = (BrowseInstance *) inContext;
3886
3887 Unused( inSDRef );
3888 Unused( inFlags );
3889 Unused( inInterfaceIndex );
3890 Unused( inFullName );
3891
3892 err = inError;
3893 require_noerr( err, exit );
3894
3895 if( !MemEqual( instance->txtPtr, instance->txtLen, inTXTPtr, inTXTLen ) )
3896 {
3897 FreeNullSafe( instance->txtPtr );
3898 instance->txtPtr = malloc( inTXTLen );
3899 require_action( instance->txtPtr, exit, err = kNoMemoryErr );
3900
3901 memcpy( instance->txtPtr, inTXTPtr, inTXTLen );
3902 instance->txtLen = inTXTLen;
3903 }
3904
3905 instance->port = ntohs( inPort );
3906
3907 if( !instance->hostname || ( strcasecmp( instance->hostname, inHostname ) != 0 ) )
3908 {
3909 DNSServiceRef sdRef;
3910
3911 if( !instance->hostname ) instance->resolveDoneTicks = nowTicks;
3912 FreeNullSafe( instance->hostname );
3913 instance->hostname = strdup( inHostname );
3914 require_action( instance->hostname, exit, err = kNoMemoryErr );
3915
3916 DNSServiceForget( &instance->getAddr );
3917 ForgetIPAddressList( &instance->addrList );
3918
3919 sdRef = instance->context->mainRef;
3920 instance->getAddrStartTicks = UpTicks();
3921 err = DNSServiceGetAddrInfo( &sdRef, kDNSServiceFlagsShareConnection, instance->ifIndex,
3922 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, instance->hostname, BrowseAllGAICallback, instance );
3923 require_noerr( err, exit );
3924
3925 instance->getAddr = sdRef;
3926 }
3927
3928 exit:
3929 if( err ) exit( 1 );
3930 }
3931
3932 //===========================================================================================================================
3933 // BrowseAllGAICallback
3934 //===========================================================================================================================
3935
3936 static void DNSSD_API
3937 BrowseAllGAICallback(
3938 DNSServiceRef inSDRef,
3939 DNSServiceFlags inFlags,
3940 uint32_t inInterfaceIndex,
3941 DNSServiceErrorType inError,
3942 const char * inHostname,
3943 const struct sockaddr * inSockAddr,
3944 uint32_t inTTL,
3945 void * inContext )
3946 {
3947 OSStatus err;
3948 BrowseInstance * const instance = (BrowseInstance *) inContext;
3949
3950 Unused( inSDRef );
3951 Unused( inInterfaceIndex );
3952 Unused( inHostname );
3953 Unused( inTTL );
3954
3955 err = inError;
3956 require_noerr( err, exit );
3957
3958 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
3959 {
3960 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
3961 goto exit;
3962 }
3963
3964 if( inFlags & kDNSServiceFlagsAdd )
3965 {
3966 err = BrowseAllAddIPAddress( instance->context, instance, inSockAddr );
3967 if( err == kDuplicateErr ) err = kNoErr;
3968 require_noerr( err, exit );
3969 }
3970 else
3971 {
3972 err = BrowseAllRemoveIPAddress( instance->context, instance, inSockAddr );
3973 if( err == kNotFoundErr ) err = kNoErr;
3974 require_noerr( err, exit );
3975 }
3976
3977 exit:
3978 return;
3979 }
3980
3981 //===========================================================================================================================
3982 // BrowseAllConnectionProgress
3983 //===========================================================================================================================
3984
3985 static void BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg )
3986 {
3987 BrowseIPAddr * const addr = (BrowseIPAddr *) inArg;
3988
3989 if( inPhase == kAsyncConnectionPhase_Connected )
3990 {
3991 const AsyncConnectedInfo * const info = (AsyncConnectedInfo *) inDetails;
3992
3993 addr->connectTimeSecs = info->connectSecs;
3994 }
3995 }
3996
3997 //===========================================================================================================================
3998 // BrowseAllConnectionHandler
3999 //===========================================================================================================================
4000
4001 static void BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg )
4002 {
4003 BrowseIPAddr * const addr = (BrowseIPAddr *) inArg;
4004 BrowseAllContext * const context = addr->context;
4005
4006 if( inError )
4007 {
4008 addr->connectStatus = kConnectStatus_Failed;
4009 addr->connectError = inError;
4010 }
4011 else
4012 {
4013 addr->connectStatus = kConnectStatus_Succeeded;
4014 }
4015
4016 check( context->pendingConnectCount > 0 );
4017 if( --context->pendingConnectCount == 0 )
4018 {
4019 if( context->exitTimer )
4020 {
4021 dispatch_source_forget( &context->exitTimer );
4022 dispatch_async_f( dispatch_get_main_queue(), context, BrowseAllExit );
4023 }
4024 }
4025
4026 ForgetSocket( &inSock );
4027 BrowseIPAddrRelease( addr );
4028 }
4029
4030 //===========================================================================================================================
4031 // BrowseAllStop
4032 //===========================================================================================================================
4033
4034 static void BrowseAllStop( void *inContext )
4035 {
4036 OSStatus err;
4037 BrowseAllContext * const context = (BrowseAllContext *) inContext;
4038 BrowseDomain * domain;
4039 BrowseType * type;
4040 BrowseOp * browse;
4041 BrowseInstance * instance;
4042
4043 DNSServiceForget( &context->domainsQuery );
4044 for( domain = context->domainList; domain; domain = domain->next )
4045 {
4046 DNSServiceForget( &domain->servicesQuery );
4047 for( type = domain->typeList; type; type = type->next )
4048 {
4049 for( browse = type->browseList; browse; browse = browse->next )
4050 {
4051 DNSServiceForget( &browse->browse );
4052 for( instance = browse->instanceList; instance; instance = instance->next )
4053 {
4054 DNSServiceForget( &instance->resolve );
4055 DNSServiceForget( &instance->getAddr );
4056 }
4057 }
4058 }
4059 }
4060 DNSServiceForget( &context->mainRef );
4061
4062 if( ( context->pendingConnectCount > 0 ) && ( context->maxConnectTimeSecs > 0 ) )
4063 {
4064 check( !context->exitTimer );
4065 err = DispatchTimerCreate( dispatch_time_seconds( context->maxConnectTimeSecs ), DISPATCH_TIME_FOREVER,
4066 100 * kNanosecondsPerMillisecond, BrowseAllExit, NULL, context, &context->exitTimer );
4067 require_noerr( err, exit );
4068 dispatch_resume( context->exitTimer );
4069 }
4070 else
4071 {
4072 dispatch_async_f( dispatch_get_main_queue(), context, BrowseAllExit );
4073 }
4074 err = kNoErr;
4075
4076 exit:
4077 if( err ) exit( 1 );
4078 }
4079
4080 //===========================================================================================================================
4081 // BrowseAllExit
4082 //===========================================================================================================================
4083
4084 #define kStatusStr_CouldConnect "connected"
4085 #define kStatusStr_CouldConnectColored kANSIGreen kStatusStr_CouldConnect kANSINormal
4086 #define kStatusStr_CouldNotConnect "could not connect"
4087 #define kStatusStr_CouldNotConnectColored kANSIRed kStatusStr_CouldNotConnect kANSINormal
4088 #define kStatusStr_NoConnectionAttempted "no connection attempted"
4089 #define kStatusStr_Unknown "unknown"
4090
4091 #define Indent( X ) ( (X) * 4 ), ""
4092
4093 static void BrowseAllExit( void *inContext )
4094 {
4095 BrowseAllContext * const context = (BrowseAllContext *) inContext;
4096 BrowseDomain * domain;
4097 BrowseType * type;
4098 BrowseOp * browse;
4099 BrowseInstance * instance;
4100 BrowseIPAddr * addr;
4101
4102 dispatch_source_forget( &context->exitTimer );
4103
4104 for( domain = context->domainList; domain; domain = domain->next )
4105 {
4106 FPrintF( stdout, "%s\n\n", domain->name );
4107
4108 for( type = domain->typeList; type; type = type->next )
4109 {
4110 const char * desc;
4111
4112 desc = ServiceTypeDescription( type->name );
4113 if( desc ) FPrintF( stdout, "%*s" "%s (%s)\n\n", Indent( 1 ), desc, type->name );
4114 else FPrintF( stdout, "%*s" "%s\n\n", Indent( 1 ), type->name );
4115
4116 for( browse = type->browseList; browse; browse = browse->next )
4117 {
4118 for( instance = browse->instanceList; instance; instance = instance->next )
4119 {
4120 char ifname[ IF_NAMESIZE + 1 ];
4121
4122 FPrintF( stdout, "%*s" "%s via ", Indent( 2 ), instance->name );
4123 if( instance->ifIndex == 0 )
4124 {
4125 FPrintF( stdout, "the Internet" );
4126 }
4127 else if( if_indextoname( instance->ifIndex, ifname ) )
4128 {
4129 NetTransportType netType;
4130
4131 SocketGetInterfaceInfo( kInvalidSocketRef, ifname, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4132 &netType );
4133 FPrintF( stdout, "%s (%s)",
4134 ( netType == kNetTransportType_Ethernet ) ? "ethernet" : NetTransportTypeToString( netType ),
4135 ifname );
4136 }
4137 else
4138 {
4139 FPrintF( stdout, "interface index %u", instance->ifIndex );
4140 }
4141 FPrintF( stdout, "\n\n" );
4142
4143 if( instance->hostname )
4144 {
4145 char buffer[ 256 ];
4146
4147 SNPrintF( buffer, sizeof( buffer ), "%s:%u", instance->hostname, instance->port );
4148 FPrintF( stdout, "%*s" "%-51s %4llu ms\n", Indent( 3 ), buffer,
4149 UpTicksToMilliseconds( instance->resolveDoneTicks - instance->resolveStartTicks ) );
4150 }
4151 else
4152 {
4153 FPrintF( stdout, "%*s" "%s:%u\n", Indent( 3 ), instance->hostname, instance->port );
4154 }
4155
4156 for( addr = instance->addrList; addr; addr = addr->next )
4157 {
4158 AsyncConnection_Forget( &addr->connection );
4159
4160 if( addr->connectStatus == kConnectStatus_Pending )
4161 {
4162 addr->connectStatus = kConnectStatus_Failed;
4163 addr->connectError = kTimeoutErr;
4164 }
4165
4166 FPrintF( stdout, "%*s" "%-##47a %4llu ms", Indent( 4 ),
4167 &addr->sip.sa, UpTicksToMilliseconds( addr->foundTicks - instance->getAddrStartTicks ) );
4168 if( context->maxConnectTimeSecs <= 0 )
4169 {
4170 FPrintF( stdout, "\n" );
4171 continue;
4172 }
4173 switch( addr->connectStatus )
4174 {
4175 case kConnectStatus_None:
4176 FPrintF( stdout, " (%s)\n", kStatusStr_NoConnectionAttempted );
4177 break;
4178
4179 case kConnectStatus_Succeeded:
4180 FPrintF( stdout, " (%s in %.2f ms)\n",
4181 context->useColoredText ? kStatusStr_CouldConnectColored : kStatusStr_CouldConnect,
4182 addr->connectTimeSecs * 1000 );
4183 break;
4184
4185 case kConnectStatus_Failed:
4186 FPrintF( stdout, " (%s: %m)\n",
4187 context->useColoredText ? kStatusStr_CouldNotConnectColored : kStatusStr_CouldNotConnect,
4188 addr->connectError );
4189 break;
4190
4191 default:
4192 FPrintF( stdout, " (%s)\n", kStatusStr_Unknown );
4193 break;
4194 }
4195 }
4196
4197 FPrintF( stdout, "\n" );
4198 if( instance->txtLen == 0 ) continue;
4199
4200 FPrintF( stdout, "%*s" "TXT record:\n", Indent( 3 ) );
4201 if( instance->txtLen > 1 )
4202 {
4203 FPrintF( stdout, "%3{txt}", instance->txtPtr, instance->txtLen );
4204 }
4205 else
4206 {
4207 FPrintF( stdout, "%*s" "%#H\n", Indent( 3 ), instance->txtPtr, (int) instance->txtLen, INT_MAX );
4208 }
4209 FPrintF( stdout, "\n" );
4210 }
4211 }
4212 FPrintF( stdout, "\n" );
4213 }
4214 }
4215
4216 while( ( domain = context->domainList ) != NULL )
4217 {
4218 context->domainList = domain->next;
4219 BrowseDomainFree( domain );
4220 }
4221
4222 BrowseAllContextRelease( context );
4223 Exit( NULL );
4224 }
4225
4226 //===========================================================================================================================
4227 // BrowseAllAddDomain
4228 //===========================================================================================================================
4229
4230 static OSStatus BrowseAllAddDomain( BrowseAllContext *inContext, const char *inName )
4231 {
4232 OSStatus err;
4233 BrowseDomain * domain;
4234 BrowseDomain ** p;
4235 BrowseDomain * newDomain = NULL;
4236
4237 for( p = &inContext->domainList; ( domain = *p ) != NULL; p = &domain->next )
4238 {
4239 if( strcasecmp( domain->name, inName ) == 0 ) break;
4240 }
4241 require_action_quiet( !domain, exit, err = kDuplicateErr );
4242
4243 newDomain = (BrowseDomain *) calloc( 1, sizeof( *newDomain ) );
4244 require_action( newDomain, exit, err = kNoMemoryErr );
4245
4246 ++inContext->refCount;
4247 newDomain->context = inContext;
4248
4249 newDomain->name = strdup( inName );
4250 require_action( newDomain->name, exit, err = kNoMemoryErr );
4251
4252 if( inContext->serviceTypesCount > 0 )
4253 {
4254 size_t i;
4255
4256 for( i = 0; i < inContext->serviceTypesCount; ++i )
4257 {
4258 err = BrowseAllAddServiceType( inContext, newDomain, inContext->serviceTypes[ i ], inContext->ifIndex,
4259 inContext->includeAWDL );
4260 if( err == kDuplicateErr ) err = kNoErr;
4261 require_noerr( err, exit );
4262 }
4263 }
4264 else
4265 {
4266 char * recordName;
4267 DNSServiceFlags flags;
4268 DNSServiceRef sdRef;
4269
4270 ASPrintF( &recordName, "_services._dns-sd._udp.%s", newDomain->name );
4271 require_action( recordName, exit, err = kNoMemoryErr );
4272
4273 flags = kDNSServiceFlagsShareConnection;
4274 if( inContext->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
4275
4276 sdRef = newDomain->context->mainRef;
4277 err = DNSServiceQueryRecord( &sdRef, flags, inContext->ifIndex, recordName, kDNSServiceType_PTR, kDNSServiceClass_IN,
4278 BrowseAllQueryCallback, newDomain );
4279 free( recordName );
4280 require_noerr( err, exit );
4281
4282 newDomain->servicesQuery = sdRef;
4283 }
4284
4285 *p = newDomain;
4286 newDomain = NULL;
4287 err = kNoErr;
4288
4289 exit:
4290 if( newDomain ) BrowseDomainFree( newDomain );
4291 return( err );
4292 }
4293
4294 //===========================================================================================================================
4295 // BrowseAllRemoveDomain
4296 //===========================================================================================================================
4297
4298 static OSStatus BrowseAllRemoveDomain( BrowseAllContext *inContext, const char *inName )
4299 {
4300 OSStatus err;
4301 BrowseDomain * domain;
4302 BrowseDomain ** p;
4303
4304 for( p = &inContext->domainList; ( domain = *p ) != NULL; p = &domain->next )
4305 {
4306 if( strcasecmp( domain->name, inName ) == 0 ) break;
4307 }
4308
4309 if( domain )
4310 {
4311 *p = domain->next;
4312 BrowseDomainFree( domain );
4313 err = kNoErr;
4314 }
4315 else
4316 {
4317 err = kNotFoundErr;
4318 }
4319
4320 return( err );
4321 }
4322
4323 //===========================================================================================================================
4324 // BrowseAllContextRelease
4325 //===========================================================================================================================
4326
4327 static void BrowseAllContextRelease( BrowseAllContext *inContext )
4328 {
4329 if( --inContext->refCount == 0 )
4330 {
4331 check( !inContext->domainsQuery );
4332 check( !inContext->domainList );
4333 check( !inContext->exitTimer );
4334 check( !inContext->pendingConnectCount );
4335 DNSServiceForget( &inContext->mainRef );
4336 if( inContext->serviceTypes )
4337 {
4338 StringArray_Free( inContext->serviceTypes, inContext->serviceTypesCount );
4339 inContext->serviceTypes = NULL;
4340 inContext->serviceTypesCount = 0;
4341 }
4342 free( inContext );
4343 }
4344 }
4345
4346 //===========================================================================================================================
4347 // BrowseAllAddServiceType
4348 //===========================================================================================================================
4349
4350 static OSStatus
4351 BrowseAllAddServiceType(
4352 BrowseAllContext * inContext,
4353 BrowseDomain * inDomain,
4354 const char * inName,
4355 uint32_t inIfIndex,
4356 Boolean inIncludeAWDL )
4357 {
4358 OSStatus err;
4359 DNSServiceRef sdRef;
4360 DNSServiceFlags flags;
4361 BrowseType * type;
4362 BrowseType ** typePtr;
4363 BrowseType * newType = NULL;
4364 BrowseOp * browse;
4365 BrowseOp ** browsePtr;
4366 BrowseOp * newBrowse = NULL;
4367
4368 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
4369 {
4370 if( strcasecmp( type->name, inName ) == 0 ) break;
4371 }
4372 if( !type )
4373 {
4374 newType = (BrowseType *) calloc( 1, sizeof( *newType ) );
4375 require_action( newType, exit, err = kNoMemoryErr );
4376
4377 newType->name = strdup( inName );
4378 require_action( newType->name, exit, err = kNoMemoryErr );
4379
4380 type = newType;
4381 }
4382
4383 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
4384 {
4385 if( browse->ifIndex == inIfIndex ) break;
4386 }
4387 require_action_quiet( !browse, exit, err = kDuplicateErr );
4388
4389 newBrowse = (BrowseOp *) calloc( 1, sizeof( *newBrowse ) );
4390 require_action( newBrowse, exit, err = kNoMemoryErr );
4391
4392 ++inContext->refCount;
4393 newBrowse->context = inContext;
4394 newBrowse->ifIndex = inIfIndex;
4395 if( stricmp_suffix( inName, "._tcp" ) == 0 ) newBrowse->isTCP = true;
4396
4397 flags = kDNSServiceFlagsShareConnection;
4398 if( inIncludeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
4399
4400 newBrowse->startTicks = UpTicks();
4401
4402 sdRef = inContext->mainRef;
4403 err = DNSServiceBrowse( &sdRef, flags, newBrowse->ifIndex, type->name, inDomain->name, BrowseAllBrowseCallback,
4404 newBrowse );
4405 require_noerr( err, exit );
4406
4407 newBrowse->browse = sdRef;
4408 *browsePtr = newBrowse;
4409 newBrowse = NULL;
4410
4411 if( newType )
4412 {
4413 *typePtr = newType;
4414 newType = NULL;
4415 }
4416
4417 exit:
4418 if( newBrowse ) BrowseOpFree( newBrowse );
4419 if( newType ) BrowseTypeFree( newType );
4420 return( err );
4421 }
4422
4423 //===========================================================================================================================
4424 // BrowseAllRemoveServiceType
4425 //===========================================================================================================================
4426
4427 static OSStatus
4428 BrowseAllRemoveServiceType(
4429 BrowseAllContext * inContext,
4430 BrowseDomain * inDomain,
4431 const char * inName,
4432 uint32_t inIfIndex )
4433 {
4434 OSStatus err;
4435 BrowseType * type;
4436 BrowseType ** typePtr;
4437 BrowseOp * browse;
4438 BrowseOp ** browsePtr;
4439
4440 Unused( inContext );
4441
4442 for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
4443 {
4444 if( strcasecmp( type->name, inName ) == 0 ) break;
4445 }
4446 require_action_quiet( type, exit, err = kNotFoundErr );
4447
4448 for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
4449 {
4450 if( browse->ifIndex == inIfIndex ) break;
4451 }
4452 require_action_quiet( browse, exit, err = kNotFoundErr );
4453
4454 *browsePtr = browse->next;
4455 BrowseOpFree( browse );
4456 if( !type->browseList )
4457 {
4458 *typePtr = type->next;
4459 BrowseTypeFree( type );
4460 }
4461 err = kNoErr;
4462
4463 exit:
4464 return( err );
4465 }
4466
4467 //===========================================================================================================================
4468 // BrowseAllAddServiceInstance
4469 //===========================================================================================================================
4470
4471 static OSStatus
4472 BrowseAllAddServiceInstance(
4473 BrowseAllContext * inContext,
4474 BrowseOp * inBrowse,
4475 const char * inName,
4476 const char * inRegType,
4477 const char * inDomain,
4478 uint32_t inIfIndex )
4479 {
4480 OSStatus err;
4481 DNSServiceRef sdRef;
4482 BrowseInstance * instance;
4483 BrowseInstance ** p;
4484 const uint64_t nowTicks = UpTicks();
4485 BrowseInstance * newInstance = NULL;
4486
4487 for( p = &inBrowse->instanceList; ( instance = *p ) != NULL; p = &instance->next )
4488 {
4489 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
4490 }
4491 require_action_quiet( !instance, exit, err = kDuplicateErr );
4492
4493 newInstance = (BrowseInstance *) calloc( 1, sizeof( *newInstance ) );
4494 require_action( newInstance, exit, err = kNoMemoryErr );
4495
4496 ++inContext->refCount;
4497 newInstance->context = inContext;
4498 newInstance->foundTicks = nowTicks;
4499 newInstance->ifIndex = inIfIndex;
4500 newInstance->isTCP = inBrowse->isTCP;
4501
4502 newInstance->name = strdup( inName );
4503 require_action( newInstance->name, exit, err = kNoMemoryErr );
4504
4505 sdRef = inContext->mainRef;
4506 newInstance->resolveStartTicks = UpTicks();
4507 err = DNSServiceResolve( &sdRef, kDNSServiceFlagsShareConnection, newInstance->ifIndex, inName, inRegType, inDomain,
4508 BrowseAllResolveCallback, newInstance );
4509 require_noerr( err, exit );
4510
4511 newInstance->resolve = sdRef;
4512 *p = newInstance;
4513 newInstance = NULL;
4514
4515 exit:
4516 if( newInstance ) BrowseInstanceFree( newInstance );
4517 return( err );
4518 }
4519
4520 //===========================================================================================================================
4521 // BrowseAllRemoveServiceInstance
4522 //===========================================================================================================================
4523
4524 static OSStatus
4525 BrowseAllRemoveServiceInstance(
4526 BrowseAllContext * inContext,
4527 BrowseOp * inBrowse,
4528 const char * inName,
4529 uint32_t inIfIndex )
4530 {
4531 OSStatus err;
4532 BrowseInstance * instance;
4533 BrowseInstance ** p;
4534
4535 Unused( inContext );
4536
4537 for( p = &inBrowse->instanceList; ( instance = *p ) != NULL; p = &instance->next )
4538 {
4539 if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
4540 }
4541 require_action_quiet( instance, exit, err = kNotFoundErr );
4542
4543 *p = instance->next;
4544 BrowseInstanceFree( instance );
4545 err = kNoErr;
4546
4547 exit:
4548 return( err );
4549 }
4550
4551 //===========================================================================================================================
4552 // BrowseAllAddIPAddress
4553 //===========================================================================================================================
4554
4555 #define kDiscardProtocolPort 9
4556
4557 static OSStatus
4558 BrowseAllAddIPAddress(
4559 BrowseAllContext * inContext,
4560 BrowseInstance * inInstance,
4561 const struct sockaddr * inSockAddr )
4562 {
4563 OSStatus err;
4564 BrowseIPAddr * addr;
4565 BrowseIPAddr ** p;
4566 const uint64_t nowTicks = UpTicks();
4567 BrowseIPAddr * newAddr = NULL;
4568
4569 if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
4570 {
4571 dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
4572 err = kTypeErr;
4573 goto exit;
4574 }
4575
4576 for( p = &inInstance->addrList; ( addr = *p ) != NULL; p = &addr->next )
4577 {
4578 if( SockAddrCompareAddr( &addr->sip, inSockAddr ) == 0 ) break;
4579 }
4580 require_action_quiet( !addr, exit, err = kDuplicateErr );
4581
4582 newAddr = (BrowseIPAddr *) calloc( 1, sizeof( *newAddr ) );
4583 require_action( newAddr, exit, err = kNoMemoryErr );
4584
4585 ++inContext->refCount;
4586 newAddr->refCount = 1;
4587 newAddr->context = inContext;
4588 newAddr->foundTicks = nowTicks;
4589 SockAddrCopy( inSockAddr, &newAddr->sip.sa );
4590
4591 if( ( inContext->maxConnectTimeSecs > 0 ) && inInstance->isTCP && ( inInstance->port != kDiscardProtocolPort ) )
4592 {
4593 char destination[ kSockAddrStringMaxSize ];
4594
4595 err = SockAddrToString( &newAddr->sip, kSockAddrStringFlagsNoPort, destination );
4596 require_noerr( err, exit );
4597
4598 err = AsyncConnection_Connect( &newAddr->connection, destination, -inInstance->port, kAsyncConnectionFlag_P2P,
4599 kAsyncConnectionNoTimeout, kSocketBufferSize_DontSet, kSocketBufferSize_DontSet,
4600 BrowseAllConnectionProgress, newAddr, BrowseAllConnectionHandler, newAddr, dispatch_get_main_queue() );
4601 require_noerr( err, exit );
4602
4603 ++newAddr->refCount;
4604 newAddr->connectStatus = kConnectStatus_Pending;
4605 ++inContext->pendingConnectCount;
4606 }
4607
4608 *p = newAddr;
4609 newAddr = NULL;
4610 err = kNoErr;
4611
4612 exit:
4613 if( newAddr ) BrowseIPAddrRelease( newAddr );
4614 return( err );
4615 }
4616
4617 //===========================================================================================================================
4618 // BrowseAllRemoveIPAddress
4619 //===========================================================================================================================
4620
4621 static OSStatus
4622 BrowseAllRemoveIPAddress(
4623 BrowseAllContext * inContext,
4624 BrowseInstance * inInstance,
4625 const struct sockaddr * inSockAddr )
4626 {
4627 OSStatus err;
4628 BrowseIPAddr * addr;
4629 BrowseIPAddr ** p;
4630
4631 Unused( inContext );
4632
4633 for( p = &inInstance->addrList; ( addr = *p ) != NULL; p = &addr->next )
4634 {
4635 if( SockAddrCompareAddr( &addr->sip.sa, inSockAddr ) == 0 ) break;
4636 }
4637 require_action_quiet( addr, exit, err = kNotFoundErr );
4638
4639 *p = addr->next;
4640 BrowseIPAddrRelease( addr );
4641 err = kNoErr;
4642
4643 exit:
4644 return( err );
4645 }
4646
4647 //===========================================================================================================================
4648 // BrowseDomainFree
4649 //===========================================================================================================================
4650
4651 static void BrowseDomainFree( BrowseDomain *inDomain )
4652 {
4653 BrowseType * type;
4654
4655 ForgetBrowseAllContext( &inDomain->context );
4656 ForgetMem( &inDomain->name );
4657 DNSServiceForget( &inDomain->servicesQuery );
4658 while( ( type = inDomain->typeList ) != NULL )
4659 {
4660 inDomain->typeList = type->next;
4661 BrowseTypeFree( type );
4662 }
4663 free( inDomain );
4664 }
4665
4666 //===========================================================================================================================
4667 // BrowseTypeFree
4668 //===========================================================================================================================
4669
4670 static void BrowseTypeFree( BrowseType *inType )
4671 {
4672 BrowseOp * browse;
4673
4674 ForgetMem( &inType->name );
4675 while( ( browse = inType->browseList ) != NULL )
4676 {
4677 inType->browseList = browse->next;
4678 BrowseOpFree( browse );
4679 }
4680 free( inType );
4681 }
4682
4683 //===========================================================================================================================
4684 // BrowseOpFree
4685 //===========================================================================================================================
4686
4687 static void BrowseOpFree( BrowseOp *inBrowse )
4688 {
4689 BrowseInstance * instance;
4690
4691 ForgetBrowseAllContext( &inBrowse->context );
4692 DNSServiceForget( &inBrowse->browse );
4693 while( ( instance = inBrowse->instanceList ) != NULL )
4694 {
4695 inBrowse->instanceList = instance->next;
4696 BrowseInstanceFree( instance );
4697 }
4698 free( inBrowse );
4699 }
4700
4701 //===========================================================================================================================
4702 // BrowseInstanceFree
4703 //===========================================================================================================================
4704
4705 static void BrowseInstanceFree( BrowseInstance *inInstance )
4706 {
4707 ForgetBrowseAllContext( &inInstance->context );
4708 ForgetMem( &inInstance->name );
4709 DNSServiceForget( &inInstance->resolve );
4710 DNSServiceForget( &inInstance->getAddr );
4711 ForgetMem( &inInstance->txtPtr );
4712 ForgetMem( &inInstance->hostname );
4713 ForgetIPAddressList( &inInstance->addrList );
4714 free( inInstance );
4715 }
4716
4717 //===========================================================================================================================
4718 // BrowseIPAddrRelease
4719 //===========================================================================================================================
4720
4721 static void BrowseIPAddrRelease( BrowseIPAddr *inAddr )
4722 {
4723 AsyncConnection_Forget( &inAddr->connection );
4724 if( --inAddr->refCount == 0 )
4725 {
4726 ForgetBrowseAllContext( &inAddr->context );
4727 free( inAddr );
4728 }
4729 }
4730
4731 //===========================================================================================================================
4732 // BrowseIPAddrReleaseList
4733 //===========================================================================================================================
4734
4735 static void BrowseIPAddrReleaseList( BrowseIPAddr *inList )
4736 {
4737 BrowseIPAddr * addr;
4738
4739 while( ( addr = inList ) != NULL )
4740 {
4741 inList = addr->next;
4742 BrowseIPAddrRelease( addr );
4743 }
4744 }
4745
4746 //===========================================================================================================================
4747 // GetAddrInfoStressCmd
4748 //===========================================================================================================================
4749
4750 typedef struct
4751 {
4752 DNSServiceRef mainRef;
4753 DNSServiceRef sdRef;
4754 DNSServiceFlags flags;
4755 unsigned int interfaceIndex;
4756 unsigned int connectionNumber;
4757 unsigned int requestCount;
4758 unsigned int requestCountMax;
4759 unsigned int requestCountLimit;
4760 unsigned int durationMinMs;
4761 unsigned int durationMaxMs;
4762
4763 } GAIStressContext;
4764
4765 static void GetAddrInfoStressEvent( void *inContext );
4766 static void DNSSD_API
4767 GetAddrInfoStressCallback(
4768 DNSServiceRef inSDRef,
4769 DNSServiceFlags inFlags,
4770 uint32_t inInterfaceIndex,
4771 DNSServiceErrorType inError,
4772 const char * inHostname,
4773 const struct sockaddr * inSockAddr,
4774 uint32_t inTTL,
4775 void * inContext );
4776
4777 static void GetAddrInfoStressCmd( void )
4778 {
4779 OSStatus err;
4780 GAIStressContext * context = NULL;
4781 int i;
4782 DNSServiceFlags flags;
4783 uint32_t ifIndex;
4784 char ifName[ kInterfaceNameBufLen ];
4785 char time[ kTimestampBufLen ];
4786
4787 if( gGAIStress_TestDurationSecs < 0 )
4788 {
4789 FPrintF( stdout, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs );
4790 err = kParamErr;
4791 goto exit;
4792 }
4793 if( gGAIStress_ConnectionCount <= 0 )
4794 {
4795 FPrintF( stdout, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount );
4796 err = kParamErr;
4797 goto exit;
4798 }
4799 if( gGAIStress_DurationMinMs <= 0 )
4800 {
4801 FPrintF( stdout, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs );
4802 err = kParamErr;
4803 goto exit;
4804 }
4805 if( gGAIStress_DurationMaxMs <= 0 )
4806 {
4807 FPrintF( stdout, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs );
4808 err = kParamErr;
4809 goto exit;
4810 }
4811 if( gGAIStress_DurationMinMs > gGAIStress_DurationMaxMs )
4812 {
4813 FPrintF( stdout, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
4814 gGAIStress_DurationMinMs, gGAIStress_DurationMaxMs );
4815 err = kParamErr;
4816 goto exit;
4817 }
4818 if( gGAIStress_RequestCountMax <= 0 )
4819 {
4820 FPrintF( stdout, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax );
4821 err = kParamErr;
4822 goto exit;
4823 }
4824
4825 // Set flags.
4826
4827 flags = GetDNSSDFlagsFromOpts();
4828
4829 // Set interface index.
4830
4831 err = InterfaceIndexFromArgString( gInterface, &ifIndex );
4832 require_noerr_quiet( err, exit );
4833
4834 for( i = 0; i < gGAIStress_ConnectionCount; ++i )
4835 {
4836 context = (GAIStressContext *) calloc( 1, sizeof( *context ) );
4837 require_action( context, exit, err = kNoMemoryErr );
4838
4839 context->flags = flags;
4840 context->interfaceIndex = ifIndex;
4841 context->connectionNumber = (unsigned int)( i + 1 );
4842 context->requestCountMax = (unsigned int) gGAIStress_RequestCountMax;
4843 context->durationMinMs = (unsigned int) gGAIStress_DurationMinMs;
4844 context->durationMaxMs = (unsigned int) gGAIStress_DurationMaxMs;
4845
4846 dispatch_async_f( dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
4847 context = NULL;
4848 }
4849
4850 if( gGAIStress_TestDurationSecs > 0 )
4851 {
4852 dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs ), dispatch_get_main_queue(), NULL, Exit );
4853 }
4854
4855 FPrintF( stdout, "Flags: %#{flags}\n", flags, kDNSServiceFlagsDescriptors );
4856 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
4857 FPrintF( stdout, "Test duration: " );
4858 if( gGAIStress_TestDurationSecs == 0 )
4859 {
4860 FPrintF( stdout, "∞\n" );
4861 }
4862 else
4863 {
4864 FPrintF( stdout, "%d s\n", gGAIStress_TestDurationSecs );
4865 }
4866 FPrintF( stdout, "Connection count: %d\n", gGAIStress_ConnectionCount );
4867 FPrintF( stdout, "Request duration min: %d ms\n", gGAIStress_DurationMinMs );
4868 FPrintF( stdout, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs );
4869 FPrintF( stdout, "Request count max: %d\n", gGAIStress_RequestCountMax );
4870 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
4871 FPrintF( stdout, "---\n" );
4872
4873 dispatch_main();
4874
4875 exit:
4876 FreeNullSafe( context );
4877 if( err ) exit( 1 );
4878 }
4879
4880 //===========================================================================================================================
4881 // GetAddrInfoStressEvent
4882 //===========================================================================================================================
4883
4884 #define kStressRandStrLen 5
4885
4886 #define kLowercaseAlphaCharSet "abcdefghijklmnopqrstuvwxyz"
4887
4888 static void GetAddrInfoStressEvent( void *inContext )
4889 {
4890 GAIStressContext * const context = (GAIStressContext *) inContext;
4891 OSStatus err;
4892 DNSServiceRef sdRef;
4893 unsigned int nextMs;
4894 char randomStr[ kStressRandStrLen + 1 ];
4895 char hostname[ kStressRandStrLen + 4 + 1 ];
4896 char time[ kTimestampBufLen ];
4897 Boolean isConnectionNew = false;
4898 static Boolean printedHeader = false;
4899
4900 if( !context->mainRef || ( context->requestCount >= context->requestCountLimit ) )
4901 {
4902 DNSServiceForget( &context->mainRef );
4903 context->sdRef = NULL;
4904 context->requestCount = 0;
4905 context->requestCountLimit = RandomRange( 1, context->requestCountMax );
4906
4907 err = DNSServiceCreateConnection( &context->mainRef );
4908 require_noerr( err, exit );
4909
4910 err = DNSServiceSetDispatchQueue( context->mainRef, dispatch_get_main_queue() );
4911 require_noerr( err, exit );
4912
4913 isConnectionNew = true;
4914 }
4915
4916 RandomString( kLowercaseAlphaCharSet, sizeof_string( kLowercaseAlphaCharSet ), 2, kStressRandStrLen, randomStr );
4917 SNPrintF( hostname, sizeof( hostname ), "%s.com", randomStr );
4918
4919 nextMs = RandomRange( context->durationMinMs, context->durationMaxMs );
4920
4921 if( !printedHeader )
4922 {
4923 FPrintF( stdout, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" );
4924 printedHeader = true;
4925 }
4926 FPrintF( stdout, "%-26s %3u%c %9s %8u\n",
4927 GetTimestampStr( time ), context->connectionNumber, isConnectionNew ? '*': ' ', hostname, nextMs );
4928
4929 DNSServiceForget( &context->sdRef );
4930 sdRef = context->mainRef;
4931 err = DNSServiceGetAddrInfo( &sdRef, context->flags | kDNSServiceFlagsShareConnection, context->interfaceIndex,
4932 kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, hostname, GetAddrInfoStressCallback, NULL );
4933 require_noerr( err, exit );
4934 context->sdRef = sdRef;
4935
4936 context->requestCount++;
4937
4938 dispatch_after_f( dispatch_time_milliseconds( nextMs ), dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
4939
4940 exit:
4941 if( err ) exit( 1 );
4942 }
4943
4944 //===========================================================================================================================
4945 // GetAddrInfoStressCallback
4946 //===========================================================================================================================
4947
4948 static void DNSSD_API
4949 GetAddrInfoStressCallback(
4950 DNSServiceRef inSDRef,
4951 DNSServiceFlags inFlags,
4952 uint32_t inInterfaceIndex,
4953 DNSServiceErrorType inError,
4954 const char * inHostname,
4955 const struct sockaddr * inSockAddr,
4956 uint32_t inTTL,
4957 void * inContext )
4958 {
4959 Unused( inSDRef );
4960 Unused( inFlags );
4961 Unused( inInterfaceIndex );
4962 Unused( inError );
4963 Unused( inHostname );
4964 Unused( inSockAddr );
4965 Unused( inTTL );
4966 Unused( inContext );
4967 }
4968
4969 //===========================================================================================================================
4970 // DNSQueryCmd
4971 //===========================================================================================================================
4972
4973 #define kDNSPort 53
4974
4975 typedef struct
4976 {
4977 sockaddr_ip serverAddr;
4978 uint64_t sendTicks;
4979 uint8_t * msgPtr;
4980 size_t msgLen;
4981 size_t msgOffset;
4982 const char * name;
4983 dispatch_source_t readSource;
4984 SocketRef sock;
4985 int timeLimitSecs;
4986 uint16_t queryID;
4987 uint16_t type;
4988 Boolean haveTCPLen;
4989 Boolean useTCP;
4990 Boolean printRawRData; // True if RDATA results are not to be formatted.
4991 uint8_t msgBuf[ 512 ];
4992
4993 } DNSQueryContext;
4994
4995 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext );
4996 static void DNSQueryReadHandler( void *inContext );
4997 static void DNSQueryCancelHandler( void *inContext );
4998
4999 static void DNSQueryCmd( void )
5000 {
5001 OSStatus err;
5002 DNSQueryContext * context = NULL;
5003 uint8_t * msgPtr;
5004 size_t msgLen, sendLen;
5005
5006 // Check command parameters.
5007
5008 if( gDNSQuery_TimeLimitSecs < -1 )
5009 {
5010 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs );
5011 err = kParamErr;
5012 goto exit;
5013 }
5014 if( ( gDNSQuery_Flags < INT16_MIN ) || ( gDNSQuery_Flags > UINT16_MAX ) )
5015 {
5016 FPrintF( stdout, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags );
5017 err = kParamErr;
5018 goto exit;
5019 }
5020
5021 // Create context.
5022
5023 context = (DNSQueryContext *) calloc( 1, sizeof( *context ) );
5024 require_action( context, exit, err = kNoMemoryErr );
5025
5026 context->name = gDNSQuery_Name;
5027 context->sock = kInvalidSocketRef;
5028 context->timeLimitSecs = gDNSQuery_TimeLimitSecs;
5029 context->queryID = (uint16_t) Random32();
5030 context->useTCP = gDNSQuery_UseTCP ? true : false;
5031 context->printRawRData = gDNSQuery_RawRData ? true : false;
5032
5033 #if( TARGET_OS_DARWIN )
5034 if( gDNSQuery_Server )
5035 #endif
5036 {
5037 err = StringToSockAddr( gDNSQuery_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
5038 require_noerr( err, exit );
5039 }
5040 #if( TARGET_OS_DARWIN )
5041 else
5042 {
5043 err = GetDefaultDNSServer( &context->serverAddr );
5044 require_noerr( err, exit );
5045 }
5046 #endif
5047 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSPort );
5048
5049 err = RecordTypeFromArgString( gDNSQuery_Type, &context->type );
5050 require_noerr( err, exit );
5051
5052 // Write query message.
5053
5054 check_compile_time_code( sizeof( context->msgBuf ) >= ( kDNSQueryMessageMaxLen + 2 ) );
5055
5056 msgPtr = context->useTCP ? &context->msgBuf[ 2 ] : context->msgBuf;
5057 err = WriteDNSQueryMessage( msgPtr, context->queryID, (uint16_t) gDNSQuery_Flags, context->name, context->type,
5058 kDNSServiceClass_IN, &msgLen );
5059 require_noerr( err, exit );
5060 check( msgLen <= UINT16_MAX );
5061
5062 if( context->useTCP )
5063 {
5064 WriteBig16( context->msgBuf, msgLen );
5065 sendLen = 2 + msgLen;
5066 }
5067 else
5068 {
5069 sendLen = msgLen;
5070 }
5071
5072 DNSQueryPrintPrologue( context );
5073
5074 if( gDNSQuery_Verbose )
5075 {
5076 FPrintF( stdout, "DNS message to send:\n\n" );
5077 PrintUDNSMessage( msgPtr, msgLen, false );
5078 FPrintF( stdout, "---\n" );
5079 }
5080
5081 if( context->useTCP )
5082 {
5083 // Create TCP socket.
5084
5085 context->sock = socket( context->serverAddr.sa.sa_family, SOCK_STREAM, IPPROTO_TCP );
5086 err = map_socket_creation_errno( context->sock );
5087 require_noerr( err, exit );
5088
5089 err = SocketConnect( context->sock, &context->serverAddr, 5 );
5090 require_noerr( err, exit );
5091 }
5092 else
5093 {
5094 // Create UDP socket.
5095
5096 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &context->sock );
5097 require_noerr( err, exit );
5098 }
5099
5100 context->sendTicks = UpTicks();
5101 err = SocketWriteAll( context->sock, context->msgBuf, sendLen, 5 );
5102 require_noerr( err, exit );
5103
5104 if( context->timeLimitSecs == 0 ) goto exit;
5105
5106 err = DispatchReadSourceCreate( context->sock, DNSQueryReadHandler, DNSQueryCancelHandler, context,
5107 &context->readSource );
5108 require_noerr( err, exit );
5109 dispatch_resume( context->readSource );
5110
5111 if( context->timeLimitSecs > 0 )
5112 {
5113 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
5114 Exit );
5115 }
5116 dispatch_main();
5117
5118 exit:
5119 if( context )
5120 {
5121 dispatch_source_forget( &context->readSource );
5122 ForgetSocket( &context->sock );
5123 free( context );
5124 }
5125 if( err ) exit( 1 );
5126 }
5127
5128 //===========================================================================================================================
5129 // DNSQueryPrintPrologue
5130 //===========================================================================================================================
5131
5132 static void DNSQueryPrintPrologue( const DNSQueryContext *inContext )
5133 {
5134 const int timeLimitSecs = inContext->timeLimitSecs;
5135 char time[ kTimestampBufLen ];
5136
5137 FPrintF( stdout, "Name: %s\n", inContext->name );
5138 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->type ), inContext->type );
5139 FPrintF( stdout, "Server: %##a\n", &inContext->serverAddr );
5140 FPrintF( stdout, "Transport: %s\n", inContext->useTCP ? "TCP" : "UDP" );
5141 FPrintF( stdout, "Time limit: " );
5142 if( timeLimitSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
5143 else FPrintF( stdout, "∞\n" );
5144 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
5145 FPrintF( stdout, "---\n" );
5146 }
5147
5148 //===========================================================================================================================
5149 // DNSQueryReadHandler
5150 //===========================================================================================================================
5151
5152 static void DNSQueryReadHandler( void *inContext )
5153 {
5154 OSStatus err;
5155 const uint64_t nowTicks = UpTicks();
5156 DNSQueryContext * const context = (DNSQueryContext *) inContext;
5157 char time[ kTimestampBufLen ];
5158
5159 GetTimestampStr( time );
5160
5161 if( context->useTCP )
5162 {
5163 if( !context->haveTCPLen )
5164 {
5165 err = SocketReadData( context->sock, &context->msgBuf, 2, &context->msgOffset );
5166 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
5167 require_noerr( err, exit );
5168
5169 context->msgOffset = 0;
5170 context->msgLen = ReadBig16( context->msgBuf );
5171 context->haveTCPLen = true;
5172 if( context->msgLen <= sizeof( context->msgBuf ) )
5173 {
5174 context->msgPtr = context->msgBuf;
5175 }
5176 else
5177 {
5178 context->msgPtr = (uint8_t *) malloc( context->msgLen );
5179 require_action( context->msgPtr, exit, err = kNoMemoryErr );
5180 }
5181 }
5182
5183 err = SocketReadData( context->sock, context->msgPtr, context->msgLen, &context->msgOffset );
5184 if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
5185 require_noerr( err, exit );
5186 context->msgOffset = 0;
5187 context->haveTCPLen = false;
5188 }
5189 else
5190 {
5191 sockaddr_ip fromAddr;
5192
5193 context->msgPtr = context->msgBuf;
5194 err = SocketRecvFrom( context->sock, context->msgPtr, sizeof( context->msgBuf ), &context->msgLen, &fromAddr,
5195 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
5196 require_noerr( err, exit );
5197
5198 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
5199 }
5200
5201 FPrintF( stdout, "Receive time: %s\n", time );
5202 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
5203 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
5204 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
5205 PrintUDNSMessage( context->msgPtr, context->msgLen, context->printRawRData );
5206
5207 if( ( context->msgLen >= kDNSHeaderLength ) && ( DNSHeaderGetID( (DNSHeader *) context->msgPtr ) == context->queryID ) )
5208 {
5209 Exit( kExitReason_ReceivedResponse );
5210 }
5211
5212 exit:
5213 if( err ) dispatch_source_forget( &context->readSource );
5214 }
5215
5216 //===========================================================================================================================
5217 // DNSQueryCancelHandler
5218 //===========================================================================================================================
5219
5220 static void DNSQueryCancelHandler( void *inContext )
5221 {
5222 DNSQueryContext * const context = (DNSQueryContext *) inContext;
5223
5224 check( !context->readSource );
5225 ForgetSocket( &context->sock );
5226 if( context->msgPtr != context->msgBuf ) ForgetMem( &context->msgPtr );
5227 free( context );
5228 dispatch_async_f( dispatch_get_main_queue(), NULL, Exit );
5229 }
5230
5231 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
5232 //===========================================================================================================================
5233 // DNSCryptCmd
5234 //===========================================================================================================================
5235
5236 #define kDNSCryptPort 443
5237
5238 #define kDNSCryptMinPadLength 8
5239 #define kDNSCryptMaxPadLength 256
5240 #define kDNSCryptBlockSize 64
5241 #define kDNSCryptCertMinimumLength 124
5242 #define kDNSCryptClientMagicLength 8
5243 #define kDNSCryptResolverMagicLength 8
5244 #define kDNSCryptHalfNonceLength 12
5245 #define kDNSCryptCertMagicLength 4
5246
5247 check_compile_time( ( kDNSCryptHalfNonceLength * 2 ) == crypto_box_NONCEBYTES );
5248
5249 static const uint8_t kDNSCryptCertMagic[ kDNSCryptCertMagicLength ] = { 'D', 'N', 'S', 'C' };
5250 static const uint8_t kDNSCryptResolverMagic[ kDNSCryptResolverMagicLength ] =
5251 {
5252 0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
5253 };
5254
5255 typedef struct
5256 {
5257 uint8_t certMagic[ kDNSCryptCertMagicLength ];
5258 uint8_t esVersion[ 2 ];
5259 uint8_t minorVersion[ 2 ];
5260 uint8_t signature[ crypto_sign_BYTES ];
5261 uint8_t publicKey[ crypto_box_PUBLICKEYBYTES ];
5262 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5263 uint8_t serial[ 4 ];
5264 uint8_t startTime[ 4 ];
5265 uint8_t endTime[ 4 ];
5266 uint8_t extensions[ 1 ]; // Variably-sized extension data.
5267
5268 } DNSCryptCert;
5269
5270 check_compile_time( offsetof( DNSCryptCert, extensions ) == kDNSCryptCertMinimumLength );
5271
5272 typedef struct
5273 {
5274 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5275 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
5276 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5277 uint8_t poly1305MAC[ 16 ];
5278
5279 } DNSCryptQueryHeader;
5280
5281 check_compile_time( sizeof( DNSCryptQueryHeader ) == 68 );
5282 check_compile_time( sizeof( DNSCryptQueryHeader ) >= crypto_box_ZEROBYTES );
5283 check_compile_time( ( sizeof( DNSCryptQueryHeader ) - crypto_box_ZEROBYTES + crypto_box_BOXZEROBYTES ) ==
5284 offsetof( DNSCryptQueryHeader, poly1305MAC ) );
5285
5286 typedef struct
5287 {
5288 uint8_t resolverMagic[ kDNSCryptResolverMagicLength ];
5289 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5290 uint8_t resolverNonce[ kDNSCryptHalfNonceLength ];
5291 uint8_t poly1305MAC[ 16 ];
5292
5293 } DNSCryptResponseHeader;
5294
5295 check_compile_time( sizeof( DNSCryptResponseHeader ) == 48 );
5296 check_compile_time( offsetof( DNSCryptResponseHeader, poly1305MAC ) >= crypto_box_BOXZEROBYTES );
5297 check_compile_time( ( offsetof( DNSCryptResponseHeader, poly1305MAC ) - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES ) ==
5298 sizeof( DNSCryptResponseHeader ) );
5299
5300 typedef struct
5301 {
5302 sockaddr_ip serverAddr;
5303 uint64_t sendTicks;
5304 const char * providerName;
5305 const char * qname;
5306 const uint8_t * certPtr;
5307 size_t certLen;
5308 dispatch_source_t readSource;
5309 size_t msgLen;
5310 int timeLimitSecs;
5311 uint16_t queryID;
5312 uint16_t qtype;
5313 Boolean printRawRData;
5314 uint8_t serverPublicSignKey[ crypto_sign_PUBLICKEYBYTES ];
5315 uint8_t serverPublicKey[ crypto_box_PUBLICKEYBYTES ];
5316 uint8_t clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
5317 uint8_t clientSecretKey[ crypto_box_SECRETKEYBYTES ];
5318 uint8_t clientMagic[ kDNSCryptClientMagicLength ];
5319 uint8_t clientNonce[ kDNSCryptHalfNonceLength ];
5320 uint8_t nmKey[ crypto_box_BEFORENMBYTES ];
5321 uint8_t msgBuf[ 512 ];
5322
5323 } DNSCryptContext;
5324
5325 static void DNSCryptReceiveCertHandler( void *inContext );
5326 static void DNSCryptReceiveResponseHandler( void *inContext );
5327 static void DNSCryptProceed( void *inContext );
5328 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext );
5329 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext );
5330 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext );
5331 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen );
5332
5333 static void DNSCryptCmd( void )
5334 {
5335 OSStatus err;
5336 DNSCryptContext * context = NULL;
5337 size_t writtenBytes;
5338 size_t totalBytes;
5339 SocketContext * sockContext;
5340 SocketRef sock = kInvalidSocketRef;
5341 const char * ptr;
5342
5343 // Check command parameters.
5344
5345 if( gDNSCrypt_TimeLimitSecs < -1 )
5346 {
5347 FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs );
5348 err = kParamErr;
5349 goto exit;
5350 }
5351
5352 // Create context.
5353
5354 context = (DNSCryptContext *) calloc( 1, sizeof( *context ) );
5355 require_action( context, exit, err = kNoMemoryErr );
5356
5357 context->providerName = gDNSCrypt_ProviderName;
5358 context->qname = gDNSCrypt_Name;
5359 context->timeLimitSecs = gDNSCrypt_TimeLimitSecs;
5360 context->printRawRData = gDNSCrypt_RawRData ? true : false;
5361
5362 err = crypto_box_keypair( context->clientPublicKey, context->clientSecretKey );
5363 require_noerr( err, exit );
5364
5365 err = HexToData( gDNSCrypt_ProviderKey, kSizeCString, kHexToData_DefaultFlags,
5366 context->serverPublicSignKey, sizeof( context->serverPublicSignKey ), &writtenBytes, &totalBytes, &ptr );
5367 if( err || ( *ptr != '\0' ) )
5368 {
5369 FPrintF( stderr, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey );
5370 goto exit;
5371 }
5372 else if( totalBytes != sizeof( context->serverPublicSignKey ) )
5373 {
5374 FPrintF( stderr, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
5375 totalBytes, sizeof( context->serverPublicSignKey ) );
5376 err = kSizeErr;
5377 goto exit;
5378 }
5379 check( writtenBytes == totalBytes );
5380
5381 err = StringToSockAddr( gDNSCrypt_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
5382 require_noerr( err, exit );
5383 if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSCryptPort );
5384
5385 err = RecordTypeFromArgString( gDNSCrypt_Type, &context->qtype );
5386 require_noerr( err, exit );
5387
5388 // Write query message.
5389
5390 context->queryID = (uint16_t) Random32();
5391 err = WriteDNSQueryMessage( context->msgBuf, context->queryID, kDNSHeaderFlag_RecursionDesired, context->providerName,
5392 kDNSServiceType_TXT, kDNSServiceClass_IN, &context->msgLen );
5393 require_noerr( err, exit );
5394
5395 // Create UDP socket.
5396
5397 err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &sock );
5398 require_noerr( err, exit );
5399
5400 // Send DNS query.
5401
5402 context->sendTicks = UpTicks();
5403 err = SocketWriteAll( sock, context->msgBuf, context->msgLen, 5 );
5404 require_noerr( err, exit );
5405
5406 sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) );
5407 require_action( sockContext, exit, err = kNoMemoryErr );
5408
5409 err = DispatchReadSourceCreate( sock, DNSCryptReceiveCertHandler, SocketContextCancelHandler, sockContext,
5410 &context->readSource );
5411 if( err ) ForgetMem( &sockContext );
5412 require_noerr( err, exit );
5413
5414 sockContext->context = context;
5415 sockContext->sock = sock;
5416 sock = kInvalidSocketRef;
5417 dispatch_resume( context->readSource );
5418
5419 if( context->timeLimitSecs > 0 )
5420 {
5421 dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
5422 Exit );
5423 }
5424 dispatch_main();
5425
5426 exit:
5427 if( context ) free( context );
5428 ForgetSocket( &sock );
5429 if( err ) exit( 1 );
5430 }
5431
5432 //===========================================================================================================================
5433 // DNSCryptReceiveCertHandler
5434 //===========================================================================================================================
5435
5436 static void DNSCryptReceiveCertHandler( void *inContext )
5437 {
5438 OSStatus err;
5439 const uint64_t nowTicks = UpTicks();
5440 SocketContext * const sockContext = (SocketContext *) inContext;
5441 DNSCryptContext * const context = (DNSCryptContext *) sockContext->context;
5442 const DNSHeader * hdr;
5443 sockaddr_ip fromAddr;
5444 const uint8_t * ptr;
5445 const uint8_t * txtPtr;
5446 size_t txtLen;
5447 unsigned int answerCount, i;
5448 uint8_t targetName[ kDomainNameLengthMax ];
5449 char time[ kTimestampBufLen ];
5450
5451 GetTimestampStr( time );
5452
5453 dispatch_source_forget( &context->readSource );
5454
5455 err = SocketRecvFrom( sockContext->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
5456 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
5457 require_noerr( err, exit );
5458 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
5459
5460 FPrintF( stdout, "Receive time: %s\n", time );
5461 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
5462 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
5463 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
5464
5465 PrintUDNSMessage( context->msgBuf, context->msgLen, context->printRawRData );
5466
5467 require_action_quiet( context->msgLen >= kDNSHeaderLength, exit, err = kSizeErr );
5468
5469 hdr = (DNSHeader *) context->msgBuf;
5470 require_action_quiet( DNSHeaderGetID( hdr ) == context->queryID, exit, err = kMismatchErr );
5471
5472 err = DNSMessageGetAnswerSection( context->msgBuf, context->msgLen, &ptr );
5473 require_noerr( err, exit );
5474
5475 err = DomainNameFromString( targetName, context->providerName, NULL );
5476 require_noerr( err, exit );
5477
5478 answerCount = DNSHeaderGetAnswerCount( hdr );
5479 for( i = 0; i < answerCount; ++i )
5480 {
5481 uint16_t type;
5482 uint16_t class;
5483 uint8_t name[ kDomainNameLengthMax ];
5484
5485 err = DNSMessageExtractRecord( context->msgBuf, context->msgLen, ptr, name, &type, &class, NULL, &txtPtr, &txtLen,
5486 &ptr );
5487 require_noerr( err, exit );
5488
5489 if( ( type == kDNSServiceType_TXT ) && ( class == kDNSServiceClass_IN ) && DomainNameEqual( name, targetName ) )
5490 {
5491 break;
5492 }
5493 }
5494
5495 if( txtLen < ( 1 + kDNSCryptCertMinimumLength ) )
5496 {
5497 FPrintF( stderr, "TXT record length is too short (%u < %u)\n", txtLen, kDNSCryptCertMinimumLength + 1 );
5498 err = kSizeErr;
5499 goto exit;
5500 }
5501 if( txtPtr[ 0 ] < kDNSCryptCertMinimumLength )
5502 {
5503 FPrintF( stderr, "TXT record value length is too short (%u < %u)\n", txtPtr[ 0 ], kDNSCryptCertMinimumLength );
5504 err = kSizeErr;
5505 goto exit;
5506 }
5507
5508 context->certLen = txtPtr[ 0 ];
5509 context->certPtr = &txtPtr[ 1 ];
5510
5511 dispatch_async_f( dispatch_get_main_queue(), context, DNSCryptProceed );
5512
5513 exit:
5514 if( err ) Exit( NULL );
5515 }
5516
5517 //===========================================================================================================================
5518 // DNSCryptReceiveResponseHandler
5519 //===========================================================================================================================
5520
5521 static void DNSCryptReceiveResponseHandler( void *inContext )
5522 {
5523 OSStatus err;
5524 const uint64_t nowTicks = UpTicks();
5525 SocketContext * const sockContext = (SocketContext *) inContext;
5526 DNSCryptContext * const context = (DNSCryptContext *) sockContext->context;
5527 sockaddr_ip fromAddr;
5528 DNSCryptResponseHeader * hdr;
5529 const uint8_t * end;
5530 uint8_t * ciphertext;
5531 uint8_t * plaintext;
5532 const uint8_t * response;
5533 char time[ kTimestampBufLen ];
5534 uint8_t nonce[ crypto_box_NONCEBYTES ];
5535
5536 GetTimestampStr( time );
5537
5538 dispatch_source_forget( &context->readSource );
5539
5540 err = SocketRecvFrom( sockContext->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
5541 &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
5542 require_noerr( err, exit );
5543 check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
5544
5545 FPrintF( stdout, "Receive time: %s\n", time );
5546 FPrintF( stdout, "Source: %##a\n", &context->serverAddr );
5547 FPrintF( stdout, "Message size: %zu\n", context->msgLen );
5548 FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
5549
5550 if( context->msgLen < sizeof( DNSCryptResponseHeader ) )
5551 {
5552 FPrintF( stderr, "DNSCrypt response is too short.\n" );
5553 err = kSizeErr;
5554 goto exit;
5555 }
5556
5557 hdr = (DNSCryptResponseHeader *) context->msgBuf;
5558
5559 if( memcmp( hdr->resolverMagic, kDNSCryptResolverMagic, kDNSCryptResolverMagicLength ) != 0 )
5560 {
5561 FPrintF( stderr, "DNSCrypt response resolver magic %#H != %#H\n",
5562 hdr->resolverMagic, kDNSCryptResolverMagicLength, INT_MAX,
5563 kDNSCryptResolverMagic, kDNSCryptResolverMagicLength, INT_MAX );
5564 err = kValueErr;
5565 goto exit;
5566 }
5567
5568 if( memcmp( hdr->clientNonce, context->clientNonce, kDNSCryptHalfNonceLength ) != 0 )
5569 {
5570 FPrintF( stderr, "DNSCrypt response client nonce mismatch.\n" );
5571 err = kValueErr;
5572 goto exit;
5573 }
5574
5575 memcpy( nonce, hdr->clientNonce, crypto_box_NONCEBYTES );
5576
5577 ciphertext = hdr->poly1305MAC - crypto_box_BOXZEROBYTES;
5578 memset( ciphertext, 0, crypto_box_BOXZEROBYTES );
5579
5580 plaintext = (uint8_t *)( hdr + 1 ) - crypto_box_ZEROBYTES;
5581 check( plaintext == ciphertext );
5582
5583 end = context->msgBuf + context->msgLen;
5584
5585 err = crypto_box_open_afternm( plaintext, ciphertext, (size_t)( end - ciphertext ), nonce, context->nmKey );
5586 require_noerr( err, exit );
5587
5588 response = plaintext + crypto_box_ZEROBYTES;
5589 PrintUDNSMessage( response, (size_t)( end - response ), context->printRawRData );
5590 Exit( kExitReason_ReceivedResponse );
5591
5592 exit:
5593 if( err ) Exit( NULL );
5594 }
5595
5596 //===========================================================================================================================
5597 // DNSCryptProceed
5598 //===========================================================================================================================
5599
5600 static void DNSCryptProceed( void *inContext )
5601 {
5602 OSStatus err;
5603 DNSCryptContext * const context = (DNSCryptContext *) inContext;
5604
5605 err = DNSCryptProcessCert( context );
5606 require_noerr_quiet( err, exit );
5607
5608 err = DNSCryptBuildQuery( context );
5609 require_noerr_quiet( err, exit );
5610
5611 err = DNSCryptSendQuery( context );
5612 require_noerr_quiet( err, exit );
5613
5614 exit:
5615 if( err ) Exit( NULL );
5616 }
5617
5618 //===========================================================================================================================
5619 // DNSCryptProcessCert
5620 //===========================================================================================================================
5621
5622 static OSStatus DNSCryptProcessCert( DNSCryptContext *inContext )
5623 {
5624 OSStatus err;
5625 const DNSCryptCert * const cert = (DNSCryptCert *) inContext->certPtr;
5626 const uint8_t * const certEnd = inContext->certPtr + inContext->certLen;
5627 struct timeval now;
5628 time_t startTimeSecs, endTimeSecs;
5629 size_t signedLen;
5630 uint8_t * tempBuf;
5631 unsigned long long tempLen;
5632
5633 DNSCryptPrintCertificate( cert, inContext->certLen );
5634
5635 if( memcmp( cert->certMagic, kDNSCryptCertMagic, kDNSCryptCertMagicLength ) != 0 )
5636 {
5637 FPrintF( stderr, "DNSCrypt certificate magic %#H != %#H\n",
5638 cert->certMagic, kDNSCryptCertMagicLength, INT_MAX,
5639 kDNSCryptCertMagic, kDNSCryptCertMagicLength, INT_MAX );
5640 err = kValueErr;
5641 goto exit;
5642 }
5643
5644 startTimeSecs = (time_t) ReadBig32( cert->startTime );
5645 endTimeSecs = (time_t) ReadBig32( cert->endTime );
5646
5647 gettimeofday( &now, NULL );
5648 if( now.tv_sec < startTimeSecs )
5649 {
5650 FPrintF( stderr, "DNSCrypt certificate start time is in the future.\n" );
5651 err = kDateErr;
5652 goto exit;
5653 }
5654 if( now.tv_sec >= endTimeSecs )
5655 {
5656 FPrintF( stderr, "DNSCrypt certificate has expired.\n" );
5657 err = kDateErr;
5658 goto exit;
5659 }
5660
5661 signedLen = (size_t)( certEnd - cert->signature );
5662 tempBuf = (uint8_t *) malloc( signedLen );
5663 require_action( tempBuf, exit, err = kNoMemoryErr );
5664 err = crypto_sign_open( tempBuf, &tempLen, cert->signature, signedLen, inContext->serverPublicSignKey );
5665 free( tempBuf );
5666 if( err )
5667 {
5668 FPrintF( stderr, "DNSCrypt certificate failed verification.\n" );
5669 err = kAuthenticationErr;
5670 goto exit;
5671 }
5672
5673 memcpy( inContext->serverPublicKey, cert->publicKey, crypto_box_PUBLICKEYBYTES );
5674 memcpy( inContext->clientMagic, cert->clientMagic, kDNSCryptClientMagicLength );
5675
5676 err = crypto_box_beforenm( inContext->nmKey, inContext->serverPublicKey, inContext->clientSecretKey );
5677 require_noerr( err, exit );
5678
5679 inContext->certPtr = NULL;
5680 inContext->certLen = 0;
5681 inContext->msgLen = 0;
5682
5683 exit:
5684 return( err );
5685 }
5686
5687 //===========================================================================================================================
5688 // DNSCryptBuildQuery
5689 //===========================================================================================================================
5690
5691 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen );
5692
5693 static OSStatus DNSCryptBuildQuery( DNSCryptContext *inContext )
5694 {
5695 OSStatus err;
5696 DNSCryptQueryHeader * const hdr = (DNSCryptQueryHeader *) inContext->msgBuf;
5697 uint8_t * const queryPtr = (uint8_t *)( hdr + 1 );
5698 size_t queryLen;
5699 size_t paddedQueryLen;
5700 const uint8_t * const msgLimit = inContext->msgBuf + sizeof( inContext->msgBuf );
5701 const uint8_t * padLimit;
5702 uint8_t nonce[ crypto_box_NONCEBYTES ];
5703
5704 check_compile_time_code( sizeof( inContext->msgBuf ) >= ( sizeof( DNSCryptQueryHeader ) + kDNSQueryMessageMaxLen ) );
5705
5706 inContext->queryID = (uint16_t) Random32();
5707 err = WriteDNSQueryMessage( queryPtr, inContext->queryID, kDNSHeaderFlag_RecursionDesired, inContext->qname,
5708 inContext->qtype, kDNSServiceClass_IN, &queryLen );
5709 require_noerr( err, exit );
5710
5711 padLimit = &queryPtr[ queryLen + kDNSCryptMaxPadLength ];
5712 if( padLimit > msgLimit ) padLimit = msgLimit;
5713
5714 err = DNSCryptPadQuery( queryPtr, queryLen, (size_t)( padLimit - queryPtr ), &paddedQueryLen );
5715 require_noerr( err, exit );
5716
5717 memset( queryPtr - crypto_box_ZEROBYTES, 0, crypto_box_ZEROBYTES );
5718 RandomBytes( inContext->clientNonce, kDNSCryptHalfNonceLength );
5719 memcpy( nonce, inContext->clientNonce, kDNSCryptHalfNonceLength );
5720 memset( &nonce[ kDNSCryptHalfNonceLength ], 0, kDNSCryptHalfNonceLength );
5721
5722 err = crypto_box_afternm( queryPtr - crypto_box_ZEROBYTES, queryPtr - crypto_box_ZEROBYTES,
5723 paddedQueryLen + crypto_box_ZEROBYTES, nonce, inContext->nmKey );
5724 require_noerr( err, exit );
5725
5726 memcpy( hdr->clientMagic, inContext->clientMagic, kDNSCryptClientMagicLength );
5727 memcpy( hdr->clientPublicKey, inContext->clientPublicKey, crypto_box_PUBLICKEYBYTES );
5728 memcpy( hdr->clientNonce, nonce, kDNSCryptHalfNonceLength );
5729
5730 inContext->msgLen = (size_t)( &queryPtr[ paddedQueryLen ] - inContext->msgBuf );
5731
5732 exit:
5733 return( err );
5734 }
5735
5736 static OSStatus DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen )
5737 {
5738 OSStatus err;
5739 size_t paddedLen;
5740
5741 require_action_quiet( ( inMsgLen + kDNSCryptMinPadLength ) <= inMaxLen, exit, err = kSizeErr );
5742
5743 paddedLen = inMsgLen + kDNSCryptMinPadLength +
5744 arc4random_uniform( (uint32_t)( inMaxLen - ( inMsgLen + kDNSCryptMinPadLength ) + 1 ) );
5745 paddedLen += ( kDNSCryptBlockSize - ( paddedLen % kDNSCryptBlockSize ) );
5746 if( paddedLen > inMaxLen ) paddedLen = inMaxLen;
5747
5748 inMsgPtr[ inMsgLen ] = 0x80;
5749 memset( &inMsgPtr[ inMsgLen + 1 ], 0, paddedLen - ( inMsgLen + 1 ) );
5750
5751 if( outPaddedLen ) *outPaddedLen = paddedLen;
5752 err = kNoErr;
5753
5754 exit:
5755 return( err );
5756 }
5757
5758 //===========================================================================================================================
5759 // DNSCryptSendQuery
5760 //===========================================================================================================================
5761
5762 static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext )
5763 {
5764 OSStatus err;
5765 SocketContext * sockContext;
5766 SocketRef sock = kInvalidSocketRef;
5767
5768 check( inContext->msgLen > 0 );
5769 check( !inContext->readSource );
5770
5771 err = UDPClientSocketOpen( AF_UNSPEC, &inContext->serverAddr, 0, -1, NULL, &sock );
5772 require_noerr( err, exit );
5773
5774 inContext->sendTicks = UpTicks();
5775 err = SocketWriteAll( sock, inContext->msgBuf, inContext->msgLen, 5 );
5776 require_noerr( err, exit );
5777
5778 sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) );
5779 require_action( sockContext, exit, err = kNoMemoryErr );
5780
5781 err = DispatchReadSourceCreate( sock, DNSCryptReceiveResponseHandler, SocketContextCancelHandler, sockContext,
5782 &inContext->readSource );
5783 if( err ) ForgetMem( &sockContext );
5784 require_noerr( err, exit );
5785
5786 sockContext->context = inContext;
5787 sockContext->sock = sock;
5788 sock = kInvalidSocketRef;
5789
5790 dispatch_resume( inContext->readSource );
5791
5792 exit:
5793 ForgetSocket( &sock );
5794 return( err );
5795 }
5796
5797 //===========================================================================================================================
5798 // DNSCryptPrintCertificate
5799 //===========================================================================================================================
5800
5801 #define kCertTimeStrBufLen 32
5802
5803 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] );
5804
5805 static void DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen )
5806 {
5807 time_t startTime, endTime;
5808 int extLen;
5809 char timeBuf[ kCertTimeStrBufLen ];
5810
5811 check( inLen >= kDNSCryptCertMinimumLength );
5812
5813 startTime = (time_t) ReadBig32( inCert->startTime );
5814 endTime = (time_t) ReadBig32( inCert->endTime );
5815
5816 FPrintF( stdout, "DNSCrypt certificate (%zu bytes):\n", inLen );
5817 FPrintF( stdout, "Cert Magic: %#H\n", inCert->certMagic, kDNSCryptCertMagicLength, INT_MAX );
5818 FPrintF( stdout, "ES Version: %u\n", ReadBig16( inCert->esVersion ) );
5819 FPrintF( stdout, "Minor Version: %u\n", ReadBig16( inCert->minorVersion ) );
5820 FPrintF( stdout, "Signature: %H\n", inCert->signature, crypto_sign_BYTES / 2, INT_MAX );
5821 FPrintF( stdout, " %H\n", &inCert->signature[ crypto_sign_BYTES / 2 ], crypto_sign_BYTES / 2, INT_MAX );
5822 FPrintF( stdout, "Public Key: %H\n", inCert->publicKey, sizeof( inCert->publicKey ), INT_MAX );
5823 FPrintF( stdout, "Client Magic: %H\n", inCert->clientMagic, kDNSCryptClientMagicLength, INT_MAX );
5824 FPrintF( stdout, "Serial: %u\n", ReadBig32( inCert->serial ) );
5825 FPrintF( stdout, "Start Time: %u (%s)\n", (uint32_t) startTime, CertTimeStr( startTime, timeBuf ) );
5826 FPrintF( stdout, "End Time: %u (%s)\n", (uint32_t) endTime, CertTimeStr( endTime, timeBuf ) );
5827
5828 if( inLen > kDNSCryptCertMinimumLength )
5829 {
5830 extLen = (int)( inLen - kDNSCryptCertMinimumLength );
5831 FPrintF( stdout, "Extensions: %.1H\n", inCert->extensions, extLen, extLen );
5832 }
5833 FPrintF( stdout, "\n" );
5834 }
5835
5836 static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] )
5837 {
5838 struct tm * tm;
5839
5840 tm = localtime( &inTime );
5841 if( !tm )
5842 {
5843 dlogassert( "localtime() returned a NULL pointer.\n" );
5844 *inBuffer = '\0';
5845 }
5846 else
5847 {
5848 strftime( inBuffer, kCertTimeStrBufLen, "%a %b %d %H:%M:%S %Z %Y", tm );
5849 }
5850
5851 return( inBuffer );
5852 }
5853
5854 #endif // DNSSDUTIL_INCLUDE_DNSCRYPT
5855
5856 //===========================================================================================================================
5857 // MDNSQueryCmd
5858 //===========================================================================================================================
5859
5860 #define kMDNSPort 5353
5861
5862 #define kDefaultMDNSMessageID 0
5863 #define kDefaultMDNSQueryFlags 0
5864
5865 typedef struct
5866 {
5867 const char * qnameStr; // Name (QNAME) of the record being queried as a C string.
5868 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
5869 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
5870 int localPort; // The port number to which the sockets are bound.
5871 int receiveSecs; // After send, the amount of time to spend receiving.
5872 uint32_t ifIndex; // Index of the interface over which to send the query.
5873 uint16_t qtype; // The type (QTYPE) of the record being queried.
5874 Boolean isQU; // True if the query is QU, i.e., requests unicast responses.
5875 Boolean allResponses; // True if all mDNS messages received should be printed.
5876 Boolean printRawRData; // True if RDATA should be printed as hexdumps.
5877 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
5878 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
5879 char ifName[ IF_NAMESIZE + 1 ]; // Name of the interface over which to send the query.
5880 uint8_t qname[ kDomainNameLengthMax ]; // Buffer to hold the QNAME in DNS label format.
5881 uint8_t msgBuf[ 8940 ]; // Message buffer. 8940 is max size used by mDNSResponder.
5882
5883 } MDNSQueryContext;
5884
5885 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext );
5886 static void MDNSQueryReadHandler( void *inContext );
5887
5888 static void MDNSQueryCmd( void )
5889 {
5890 OSStatus err;
5891 MDNSQueryContext * context;
5892 struct sockaddr_in mcastAddr4;
5893 struct sockaddr_in6 mcastAddr6;
5894 SocketRef sockV4 = kInvalidSocketRef;
5895 SocketRef sockV6 = kInvalidSocketRef;
5896 ssize_t n;
5897 const char * ifNamePtr;
5898 size_t msgLen;
5899 unsigned int sendCount;
5900
5901 // Check command parameters.
5902
5903 if( gMDNSQuery_ReceiveSecs < -1 )
5904 {
5905 FPrintF( stdout, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs );
5906 err = kParamErr;
5907 goto exit;
5908 }
5909
5910 context = (MDNSQueryContext *) calloc( 1, sizeof( *context ) );
5911 require_action( context, exit, err = kNoMemoryErr );
5912
5913 context->qnameStr = gMDNSQuery_Name;
5914 context->receiveSecs = gMDNSQuery_ReceiveSecs;
5915 context->isQU = gMDNSQuery_IsQU ? true : false;
5916 context->allResponses = gMDNSQuery_AllResponses ? true : false;
5917 context->printRawRData = gMDNSQuery_RawRData ? true : false;
5918 context->useIPv4 = ( gMDNSQuery_UseIPv4 || !gMDNSQuery_UseIPv6 ) ? true : false;
5919 context->useIPv6 = ( gMDNSQuery_UseIPv6 || !gMDNSQuery_UseIPv4 ) ? true : false;
5920
5921 err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
5922 require_noerr_quiet( err, exit );
5923
5924 ifNamePtr = if_indextoname( context->ifIndex, context->ifName );
5925 require_action( ifNamePtr, exit, err = kNameErr );
5926
5927 err = RecordTypeFromArgString( gMDNSQuery_Type, &context->qtype );
5928 require_noerr( err, exit );
5929
5930 // Set up IPv4 socket.
5931
5932 if( context->useIPv4 )
5933 {
5934 err = ServerSocketOpen( AF_INET, SOCK_DGRAM, IPPROTO_UDP,
5935 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
5936 &context->localPort, kSocketBufferSize_DontSet, &sockV4 );
5937 require_noerr( err, exit );
5938
5939 err = SocketSetMulticastInterface( sockV4, ifNamePtr, context->ifIndex );
5940 require_noerr( err, exit );
5941
5942 err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
5943 err = map_socket_noerr_errno( sockV4, err );
5944 require_noerr( err, exit );
5945
5946 memset( &mcastAddr4, 0, sizeof( mcastAddr4 ) );
5947 SIN_LEN_SET( &mcastAddr4 );
5948 mcastAddr4.sin_family = AF_INET;
5949 mcastAddr4.sin_port = htons( kMDNSPort );
5950 mcastAddr4.sin_addr.s_addr = htonl( 0xE00000FB ); // The mDNS IPv4 multicast address is 224.0.0.251
5951
5952 if( !context->isQU && ( context->localPort == kMDNSPort ) )
5953 {
5954 err = SocketJoinMulticast( sockV4, &mcastAddr4, ifNamePtr, context->ifIndex );
5955 require_noerr( err, exit );
5956 }
5957 }
5958
5959 // Set up IPv6 socket.
5960
5961 if( context->useIPv6 )
5962 {
5963 err = ServerSocketOpen( AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
5964 gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
5965 &context->localPort, kSocketBufferSize_DontSet, &sockV6 );
5966 require_noerr( err, exit );
5967
5968 err = SocketSetMulticastInterface( sockV6, ifNamePtr, context->ifIndex );
5969 require_noerr( err, exit );
5970
5971 err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
5972 err = map_socket_noerr_errno( sockV6, err );
5973 require_noerr( err, exit );
5974
5975 memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) );
5976 SIN6_LEN_SET( &mcastAddr6 );
5977 mcastAddr6.sin6_family = AF_INET6;
5978 mcastAddr6.sin6_port = htons( kMDNSPort );
5979 mcastAddr6.sin6_addr.s6_addr[ 0 ] = 0xFF; // mDNS IPv6 multicast address FF02::FB
5980 mcastAddr6.sin6_addr.s6_addr[ 1 ] = 0x02;
5981 mcastAddr6.sin6_addr.s6_addr[ 15 ] = 0xFB;
5982
5983 if( !context->isQU && ( context->localPort == kMDNSPort ) )
5984 {
5985 err = SocketJoinMulticast( sockV6, &mcastAddr6, ifNamePtr, context->ifIndex );
5986 require_noerr( err, exit );
5987 }
5988 }
5989
5990 // Craft mDNS query message.
5991
5992 check_compile_time_code( sizeof( context->msgBuf ) >= kDNSQueryMessageMaxLen );
5993 err = WriteDNSQueryMessage( context->msgBuf, kDefaultMDNSMessageID, kDefaultMDNSQueryFlags, context->qnameStr,
5994 context->qtype, context->isQU ? ( kDNSServiceClass_IN | kQClassUnicastResponseBit ) : kDNSServiceClass_IN, &msgLen );
5995 require_noerr( err, exit );
5996
5997 // Print prologue.
5998
5999 MDNSQueryPrintPrologue( context );
6000
6001 // Send mDNS query message.
6002
6003 sendCount = 0;
6004 if( IsValidSocket( sockV4 ) )
6005 {
6006 n = sendto( sockV4, context->msgBuf, msgLen, 0, (struct sockaddr *) &mcastAddr4, (socklen_t) sizeof( mcastAddr4 ) );
6007 err = map_socket_value_errno( sockV4, n == (ssize_t) msgLen, n );
6008 if( err )
6009 {
6010 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
6011 ForgetSocket( &sockV4 );
6012 }
6013 else
6014 {
6015 ++sendCount;
6016 }
6017 }
6018 if( IsValidSocket( sockV6 ) )
6019 {
6020 n = sendto( sockV6, context->msgBuf, msgLen, 0, (struct sockaddr *) &mcastAddr6, (socklen_t) sizeof( mcastAddr6 ) );
6021 err = map_socket_value_errno( sockV6, n == (ssize_t) msgLen, n );
6022 if( err )
6023 {
6024 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
6025 ForgetSocket( &sockV6 );
6026 }
6027 else
6028 {
6029 ++sendCount;
6030 }
6031 }
6032 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
6033
6034 // If there's no wait period after the send, then exit.
6035
6036 if( context->receiveSecs == 0 ) goto exit;
6037
6038 // Create dispatch read sources for socket(s).
6039
6040 if( IsValidSocket( sockV4 ) )
6041 {
6042 SocketContext * sockContext;
6043
6044 sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) );
6045 require_action( sockContext, exit, err = kNoMemoryErr );
6046
6047 err = DispatchReadSourceCreate( sockV4, MDNSQueryReadHandler, SocketContextCancelHandler, sockContext,
6048 &context->readSourceV4 );
6049 if( err ) ForgetMem( &sockContext );
6050 require_noerr( err, exit );
6051
6052 sockContext->context = context;
6053 sockContext->sock = sockV4;
6054 sockV4 = kInvalidSocketRef;
6055 dispatch_resume( context->readSourceV4 );
6056 }
6057
6058 if( IsValidSocket( sockV6 ) )
6059 {
6060 SocketContext * sockContext;
6061
6062 sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) );
6063 require_action( sockContext, exit, err = kNoMemoryErr );
6064
6065 err = DispatchReadSourceCreate( sockV6, MDNSQueryReadHandler, SocketContextCancelHandler, sockContext,
6066 &context->readSourceV6 );
6067 if( err ) ForgetMem( &sockContext );
6068 require_noerr( err, exit );
6069
6070 sockContext->context = context;
6071 sockContext->sock = sockV6;
6072 sockV6 = kInvalidSocketRef;
6073 dispatch_resume( context->readSourceV6 );
6074 }
6075
6076 if( context->receiveSecs > 0 )
6077 {
6078 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6079 Exit );
6080 }
6081 dispatch_main();
6082
6083 exit:
6084 ForgetSocket( &sockV4 );
6085 ForgetSocket( &sockV6 );
6086 if( err ) exit( 1 );
6087 }
6088
6089 //===========================================================================================================================
6090 // MDNSQueryPrintPrologue
6091 //===========================================================================================================================
6092
6093 static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext )
6094 {
6095 const int receiveSecs = inContext->receiveSecs;
6096 char time[ kTimestampBufLen ];
6097
6098 FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, inContext->ifName );
6099 FPrintF( stdout, "Name: %s\n", inContext->qnameStr );
6100 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->qtype ), inContext->qtype );
6101 FPrintF( stdout, "Class: IN (%s)\n", inContext->isQU ? "QU" : "QM" );
6102 FPrintF( stdout, "Local port: %d\n", inContext->localPort );
6103 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
6104 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
6105 FPrintF( stdout, "Receive duration: " );
6106 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
6107 else FPrintF( stdout, "∞\n" );
6108 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
6109 }
6110
6111 //===========================================================================================================================
6112 // MDNSQueryReadHandler
6113 //===========================================================================================================================
6114
6115 static void MDNSQueryReadHandler( void *inContext )
6116 {
6117 OSStatus err;
6118 SocketContext * const sockContext = (SocketContext *) inContext;
6119 MDNSQueryContext * const context = (MDNSQueryContext *) sockContext->context;
6120 size_t msgLen;
6121 sockaddr_ip fromAddr;
6122 char time[ kTimestampBufLen ];
6123 Boolean foundAnswer = false;
6124
6125 GetTimestampStr( time );
6126
6127 err = SocketRecvFrom( sockContext->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &fromAddr,
6128 sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6129 require_noerr( err, exit );
6130
6131 if( !context->allResponses && ( msgLen >= kDNSHeaderLength ) )
6132 {
6133 const uint8_t * ptr;
6134 const DNSHeader * const hdr = (DNSHeader *) context->msgBuf;
6135 unsigned int rrCount, i;
6136 uint16_t type, class;
6137 uint8_t name[ kDomainNameLengthMax ];
6138
6139 err = DNSMessageGetAnswerSection( context->msgBuf, msgLen, &ptr );
6140 require_noerr( err, exit );
6141
6142 if( context->qname[ 0 ] == 0 )
6143 {
6144 err = DomainNameAppendString( context->qname, context->qnameStr, NULL );
6145 require_noerr( err, exit );
6146 }
6147
6148 rrCount = DNSHeaderGetAnswerCount( hdr ) + DNSHeaderGetAuthorityCount( hdr ) + DNSHeaderGetAdditionalCount( hdr );
6149 for( i = 0; i < rrCount; ++i )
6150 {
6151 err = DNSMessageExtractRecord( context->msgBuf, msgLen, ptr, name, &type, &class, NULL, NULL, NULL, &ptr );
6152 require_noerr( err, exit );
6153
6154 if( ( ( context->qtype == kDNSServiceType_ANY ) || ( type == context->qtype ) ) &&
6155 DomainNameEqual( name, context->qname ) )
6156 {
6157 foundAnswer = true;
6158 break;
6159 }
6160 }
6161 }
6162 if( context->allResponses || foundAnswer )
6163 {
6164 FPrintF( stdout, "---\n" );
6165 FPrintF( stdout, "Receive time: %s\n", time );
6166 FPrintF( stdout, "Source: %##a\n", &fromAddr );
6167 FPrintF( stdout, "Message size: %zu\n\n", msgLen );
6168
6169 PrintMDNSMessage( context->msgBuf, msgLen, context->printRawRData );
6170 }
6171
6172 exit:
6173 if( err ) exit( 1 );
6174 }
6175
6176 //===========================================================================================================================
6177 // PIDToUUIDCmd
6178 //===========================================================================================================================
6179
6180 static void PIDToUUIDCmd( void )
6181 {
6182 OSStatus err;
6183 int n;
6184 struct proc_uniqidentifierinfo info;
6185
6186 n = proc_pidinfo( gPIDToUUID_PID, PROC_PIDUNIQIDENTIFIERINFO, 1, &info, sizeof( info ) );
6187 require_action_quiet( n == (int) sizeof( info ), exit, err = kUnknownErr );
6188
6189 FPrintF( stdout, "%#U\n", info.p_uuid );
6190 err = kNoErr;
6191
6192 exit:
6193 if( err ) exit( 1 );
6194 }
6195
6196 //===========================================================================================================================
6197 // SSDPDiscoverCmd
6198 //===========================================================================================================================
6199
6200 #define kSSDPPort 1900
6201
6202 typedef struct
6203 {
6204 HTTPHeader header; // HTTP header object for sending and receiving.
6205 dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket.
6206 dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket.
6207 int receiveSecs; // After send, the amount of time to spend receiving.
6208 uint32_t ifindex; // Index of the interface over which to send the query.
6209 Boolean useIPv4; // True if the query should be sent via IPv4 multicast.
6210 Boolean useIPv6; // True if the query should be sent via IPv6 multicast.
6211
6212 } SSDPDiscoverContext;
6213
6214 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext );
6215 static void SSDPDiscoverReadHandler( void *inContext );
6216 static int SocketToPortNumber( SocketRef inSock );
6217 static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST );
6218
6219 static void SSDPDiscoverCmd( void )
6220 {
6221 OSStatus err;
6222 SSDPDiscoverContext * context;
6223 dispatch_source_t signalSource = NULL;
6224 SocketRef sockV4 = kInvalidSocketRef;
6225 SocketRef sockV6 = kInvalidSocketRef;
6226 ssize_t n;
6227 int sendCount;
6228 char time[ kTimestampBufLen ];
6229
6230 // Set up SIGINT handler.
6231
6232 signal( SIGINT, SIG_IGN );
6233 err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
6234 require_noerr( err, exit );
6235 dispatch_resume( signalSource );
6236
6237 // Check command parameters.
6238
6239 if( gSSDPDiscover_ReceiveSecs < -1 )
6240 {
6241 FPrintF( stdout, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs );
6242 err = kParamErr;
6243 goto exit;
6244 }
6245
6246 // Create context.
6247
6248 context = (SSDPDiscoverContext *) calloc( 1, sizeof( *context ) );
6249 require_action( context, exit, err = kNoMemoryErr );
6250
6251 context->receiveSecs = gSSDPDiscover_ReceiveSecs;
6252 context->useIPv4 = ( gSSDPDiscover_UseIPv4 || !gSSDPDiscover_UseIPv6 ) ? true : false;
6253 context->useIPv6 = ( gSSDPDiscover_UseIPv6 || !gSSDPDiscover_UseIPv4 ) ? true : false;
6254
6255 err = InterfaceIndexFromArgString( gInterface, &context->ifindex );
6256 require_noerr_quiet( err, exit );
6257
6258 // Set up IPv4 socket.
6259
6260 if( context->useIPv4 )
6261 {
6262 int port;
6263 err = UDPClientSocketOpen( AF_INET, NULL, 0, -1, &port, &sockV4 );
6264 require_noerr( err, exit );
6265
6266 err = SocketSetMulticastInterface( sockV4, NULL, context->ifindex );
6267 require_noerr( err, exit );
6268
6269 err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
6270 err = map_socket_noerr_errno( sockV4, err );
6271 require_noerr( err, exit );
6272 }
6273
6274 // Set up IPv6 socket.
6275
6276 if( context->useIPv6 )
6277 {
6278 err = UDPClientSocketOpen( AF_INET6, NULL, 0, -1, NULL, &sockV6 );
6279 require_noerr( err, exit );
6280
6281 err = SocketSetMulticastInterface( sockV6, NULL, context->ifindex );
6282 require_noerr( err, exit );
6283
6284 err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
6285 err = map_socket_noerr_errno( sockV6, err );
6286 require_noerr( err, exit );
6287 }
6288
6289 // Print prologue.
6290
6291 SSDPDiscoverPrintPrologue( context );
6292
6293 // Send mDNS query message.
6294
6295 sendCount = 0;
6296 if( IsValidSocket( sockV4 ) )
6297 {
6298 struct sockaddr_in mcastAddr4;
6299
6300 memset( &mcastAddr4, 0, sizeof( mcastAddr4 ) );
6301 SIN_LEN_SET( &mcastAddr4 );
6302 mcastAddr4.sin_family = AF_INET;
6303 mcastAddr4.sin_port = htons( kSSDPPort );
6304 mcastAddr4.sin_addr.s_addr = htonl( 0xEFFFFFFA ); // 239.255.255.250
6305
6306 err = WriteSSDPSearchRequest( &context->header, &mcastAddr4, gSSDPDiscover_MX, gSSDPDiscover_ST );
6307 require_noerr( err, exit );
6308
6309 n = sendto( sockV4, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr4,
6310 (socklen_t) sizeof( mcastAddr4 ) );
6311 err = map_socket_value_errno( sockV4, n == (ssize_t) context->header.len, n );
6312 if( err )
6313 {
6314 FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
6315 ForgetSocket( &sockV4 );
6316 }
6317 else
6318 {
6319 if( gSSDPDiscover_Verbose )
6320 {
6321 GetTimestampStr( time );
6322 FPrintF( stdout, "---\n" );
6323 FPrintF( stdout, "Send time: %s\n", time );
6324 FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV4 ) );
6325 FPrintF( stdout, "Destination: %##a\n", &mcastAddr4 );
6326 FPrintF( stdout, "Message size: %zu\n", context->header.len );
6327 FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len );
6328 }
6329 ++sendCount;
6330 }
6331 }
6332
6333 if( IsValidSocket( sockV6 ) )
6334 {
6335 struct sockaddr_in6 mcastAddr6;
6336
6337 memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) );
6338 SIN6_LEN_SET( &mcastAddr6 );
6339 mcastAddr6.sin6_family = AF_INET6;
6340 mcastAddr6.sin6_port = htons( kSSDPPort );
6341 mcastAddr6.sin6_addr.s6_addr[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C
6342 mcastAddr6.sin6_addr.s6_addr[ 1 ] = 0x02;
6343 mcastAddr6.sin6_addr.s6_addr[ 15 ] = 0x0C;
6344
6345 err = WriteSSDPSearchRequest( &context->header, &mcastAddr6, gSSDPDiscover_MX, gSSDPDiscover_ST );
6346 require_noerr( err, exit );
6347
6348 n = sendto( sockV6, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr6,
6349 (socklen_t) sizeof( mcastAddr6 ) );
6350 err = map_socket_value_errno( sockV6, n == (ssize_t) context->header.len, n );
6351 if( err )
6352 {
6353 FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
6354 ForgetSocket( &sockV6 );
6355 }
6356 else
6357 {
6358 if( gSSDPDiscover_Verbose )
6359 {
6360 GetTimestampStr( time );
6361 FPrintF( stdout, "---\n" );
6362 FPrintF( stdout, "Send time: %s\n", time );
6363 FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV6 ) );
6364 FPrintF( stdout, "Destination: %##a\n", &mcastAddr6 );
6365 FPrintF( stdout, "Message size: %zu\n", context->header.len );
6366 FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len );
6367 }
6368 ++sendCount;
6369 }
6370 }
6371 require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
6372
6373 // If there's no wait period after the send, then exit.
6374
6375 if( context->receiveSecs == 0 ) goto exit;
6376
6377 // Create dispatch read sources for socket(s).
6378
6379 if( IsValidSocket( sockV4 ) )
6380 {
6381 SocketContext * sockContext;
6382
6383 sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) );
6384 require_action( sockContext, exit, err = kNoMemoryErr );
6385
6386 err = DispatchReadSourceCreate( sockV4, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockContext,
6387 &context->readSourceV4 );
6388 if( err ) ForgetMem( &sockContext );
6389 require_noerr( err, exit );
6390
6391 sockContext->context = context;
6392 sockContext->sock = sockV4;
6393 sockV4 = kInvalidSocketRef;
6394 dispatch_resume( context->readSourceV4 );
6395 }
6396
6397 if( IsValidSocket( sockV6 ) )
6398 {
6399 SocketContext * sockContext;
6400
6401 sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) );
6402 require_action( sockContext, exit, err = kNoMemoryErr );
6403
6404 err = DispatchReadSourceCreate( sockV6, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockContext,
6405 &context->readSourceV6 );
6406 if( err ) ForgetMem( &sockContext );
6407 require_noerr( err, exit );
6408
6409 sockContext->context = context;
6410 sockContext->sock = sockV6;
6411 sockV6 = kInvalidSocketRef;
6412 dispatch_resume( context->readSourceV6 );
6413 }
6414
6415 if( context->receiveSecs > 0 )
6416 {
6417 dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6418 Exit );
6419 }
6420 dispatch_main();
6421
6422 exit:
6423 ForgetSocket( &sockV4 );
6424 ForgetSocket( &sockV6 );
6425 dispatch_source_forget( &signalSource );
6426 if( err ) exit( 1 );
6427 }
6428
6429 static int SocketToPortNumber( SocketRef inSock )
6430 {
6431 OSStatus err;
6432 sockaddr_ip sip;
6433 socklen_t len;
6434
6435 len = (socklen_t) sizeof( sip );
6436 err = getsockname( inSock, &sip.sa, &len );
6437 err = map_socket_noerr_errno( inSock, err );
6438 check_noerr( err );
6439 return( err ? -1 : SockAddrGetPort( &sip ) );
6440 }
6441
6442 static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST )
6443 {
6444 OSStatus err;
6445
6446 err = HTTPHeader_InitRequest( inHeader, "M-SEARCH", "*", "HTTP/1.1" );
6447 require_noerr( err, exit );
6448
6449 err = HTTPHeader_SetField( inHeader, "Host", "%##a", inHostSA );
6450 require_noerr( err, exit );
6451
6452 err = HTTPHeader_SetField( inHeader, "ST", "%s", inST ? inST : "ssdp:all" );
6453 require_noerr( err, exit );
6454
6455 err = HTTPHeader_SetField( inHeader, "Man", "\"ssdp:discover\"" );
6456 require_noerr( err, exit );
6457
6458 err = HTTPHeader_SetField( inHeader, "MX", "%d", inMX );
6459 require_noerr( err, exit );
6460
6461 err = HTTPHeader_Commit( inHeader );
6462 require_noerr( err, exit );
6463
6464 exit:
6465 return( err );
6466 }
6467
6468 //===========================================================================================================================
6469 // SSDPDiscoverPrintPrologue
6470 //===========================================================================================================================
6471
6472 static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext )
6473 {
6474 const int receiveSecs = inContext->receiveSecs;
6475 const char * ifName;
6476 char ifNameBuf[ IF_NAMESIZE + 1 ];
6477 NetTransportType ifType;
6478 char time[ kTimestampBufLen ];
6479
6480 ifName = if_indextoname( inContext->ifindex, ifNameBuf );
6481
6482 ifType = kNetTransportType_Undefined;
6483 if( ifName ) SocketGetInterfaceInfo( kInvalidSocketRef, ifName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &ifType );
6484
6485 FPrintF( stdout, "Interface: %s/%d/%s\n",
6486 ifName ? ifName : "?", inContext->ifindex, NetTransportTypeToString( ifType ) );
6487 FPrintF( stdout, "IP protocols: %?s%?s%?s\n",
6488 inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
6489 FPrintF( stdout, "Receive duration: " );
6490 if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
6491 else FPrintF( stdout, "∞\n" );
6492 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
6493 }
6494
6495 //===========================================================================================================================
6496 // SSDPDiscoverReadHandler
6497 //===========================================================================================================================
6498
6499 static void SSDPDiscoverReadHandler( void *inContext )
6500 {
6501 OSStatus err;
6502 SocketContext * const sockContext = (SocketContext *) inContext;
6503 SSDPDiscoverContext * const context = (SSDPDiscoverContext *) sockContext->context;
6504 HTTPHeader * const header = &context->header;
6505 sockaddr_ip fromAddr;
6506 size_t msgLen;
6507 char time[ kTimestampBufLen ];
6508
6509 GetTimestampStr( time );
6510
6511 err = SocketRecvFrom( sockContext->sock, header->buf, sizeof( header->buf ), &msgLen, &fromAddr, sizeof( fromAddr ),
6512 NULL, NULL, NULL, NULL );
6513 require_noerr( err, exit );
6514
6515 FPrintF( stdout, "---\n" );
6516 FPrintF( stdout, "Receive time: %s\n", time );
6517 FPrintF( stdout, "Source: %##a\n", &fromAddr );
6518 FPrintF( stdout, "Message size: %zu\n", msgLen );
6519 header->len = msgLen;
6520 if( HTTPHeader_Validate( header ) )
6521 {
6522 FPrintF( stdout, "HTTP header:\n%1{text}", header->buf, header->len );
6523 if( header->extraDataLen > 0 )
6524 {
6525 FPrintF( stdout, "HTTP body: %1.1H", header->extraDataPtr, (int) header->extraDataLen, INT_MAX );
6526 }
6527 }
6528 else
6529 {
6530 FPrintF( stdout, "Invalid HTTP message:\n%1.1H", header->buf, (int) msgLen, INT_MAX );
6531 goto exit;
6532 }
6533
6534 exit:
6535 if( err ) exit( 1 );
6536 }
6537
6538 //===========================================================================================================================
6539 // HTTPHeader_Validate
6540 //
6541 // Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid.
6542 // This assumes the "buf" and "len" fields are set. The other fields are set by this function.
6543 //
6544 // Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework.
6545 //===========================================================================================================================
6546
6547 Boolean HTTPHeader_Validate( HTTPHeader *inHeader )
6548 {
6549 const char * src;
6550 const char * end;
6551
6552 // Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
6553
6554 require( inHeader->len < sizeof( inHeader->buf ), exit );
6555 src = inHeader->buf;
6556 end = src + inHeader->len;
6557 if( ( ( end - src ) >= 4 ) && ( src[ 0 ] == '$' ) )
6558 {
6559 src += 4;
6560 }
6561 else
6562 {
6563 // Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted.
6564 // $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over.
6565
6566 for( ;; )
6567 {
6568 while( ( src < end ) && ( src[ 0 ] != '\n' ) ) ++src;
6569 if( src >= end ) goto exit;
6570 ++src;
6571 if( ( ( end - src ) >= 2 ) && ( src[ 0 ] == '\r' ) && ( src[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF
6572 {
6573 src += 2;
6574 break;
6575 }
6576 else if( ( ( end - src ) >= 1 ) && ( src[ 0 ] == '\n' ) ) // LFLF
6577 {
6578 src += 1;
6579 break;
6580 }
6581 }
6582 }
6583 inHeader->extraDataPtr = src;
6584 inHeader->extraDataLen = (size_t)( end - src );
6585 inHeader->len = (size_t)( src - inHeader->buf );
6586 return( true );
6587
6588 exit:
6589 return( false );
6590 }
6591
6592 #if( TARGET_OS_DARWIN )
6593 //===========================================================================================================================
6594 // ResQueryCmd
6595 //===========================================================================================================================
6596
6597 // res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h).
6598
6599 SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv );
6600 SOFT_LINK_FUNCTION_EX( resolv, res_9_query,
6601 int,
6602 ( const char *dname, int class, int type, u_char *answer, int anslen ),
6603 ( dname, class, type, answer, anslen ) );
6604
6605 // res_query() from libinfo
6606
6607 SOFT_LINK_LIBRARY_EX( "/usr/lib", info );
6608 SOFT_LINK_FUNCTION_EX( info, res_query,
6609 int,
6610 ( const char *dname, int class, int type, u_char *answer, int anslen ),
6611 ( dname, class, type, answer, anslen ) );
6612
6613 typedef int ( *res_query_f )( const char *dname, int class, int type, u_char *answer, int anslen );
6614
6615 static void ResQueryCmd( void )
6616 {
6617 OSStatus err;
6618 res_query_f res_query_ptr;
6619 int n;
6620 uint16_t type, class;
6621 char time[ kTimestampBufLen ];
6622 uint8_t answer[ 1024 ];
6623
6624 // Get pointer to one of the res_query() functions.
6625
6626 if( gResQuery_UseLibInfo )
6627 {
6628 if( !SOFT_LINK_HAS_FUNCTION( info, res_query ) )
6629 {
6630 FPrintF( stderr, "Failed to soft link res_query from libinfo.\n" );
6631 err = kNotFoundErr;
6632 goto exit;
6633 }
6634 res_query_ptr = soft_res_query;
6635 }
6636 else
6637 {
6638 if( !SOFT_LINK_HAS_FUNCTION( resolv, res_9_query ) )
6639 {
6640 FPrintF( stderr, "Failed to soft link res_query from libresolv.\n" );
6641 err = kNotFoundErr;
6642 goto exit;
6643 }
6644 res_query_ptr = soft_res_9_query;
6645 }
6646
6647 // Get record type.
6648
6649 err = RecordTypeFromArgString( gResQuery_Type, &type );
6650 require_noerr( err, exit );
6651
6652 // Get record class.
6653
6654 if( gResQuery_Class )
6655 {
6656 err = RecordClassFromArgString( gResQuery_Class, &class );
6657 require_noerr( err, exit );
6658 }
6659 else
6660 {
6661 class = kDNSServiceClass_IN;
6662 }
6663
6664 // Print prologue.
6665
6666 FPrintF( stdout, "Name: %s\n", gResQuery_Name );
6667 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
6668 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
6669 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
6670 FPrintF( stdout, "---\n" );
6671
6672 // Call res_query().
6673
6674 n = res_query_ptr( gResQuery_Name, class, type, (u_char *) answer, (int) sizeof( answer ) );
6675 if( n < 0 )
6676 {
6677 FPrintF( stderr, "res_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
6678 err = kUnknownErr;
6679 goto exit;
6680 }
6681
6682 // Print result.
6683
6684 FPrintF( stdout, "Message size: %d\n\n", n );
6685 PrintUDNSMessage( answer, (size_t) n, false );
6686
6687 exit:
6688 if( err ) exit( 1 );
6689 }
6690
6691 //===========================================================================================================================
6692 // ResolvDNSQueryCmd
6693 //===========================================================================================================================
6694
6695 // dns_handle_t is defined as a pointer to a privately-defined struct in /usr/include/dns.h. It's defined as a void * here to
6696 // avoid including the header file.
6697
6698 typedef void * dns_handle_t;
6699
6700 SOFT_LINK_FUNCTION_EX( resolv, dns_open, dns_handle_t, ( const char *path ), ( path ) );
6701 SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv, dns_free, ( dns_handle_t *dns ), ( dns ) );
6702 SOFT_LINK_FUNCTION_EX( resolv, dns_query,
6703 int32_t, (
6704 dns_handle_t dns,
6705 const char * name,
6706 uint32_t dnsclass,
6707 uint32_t dnstype,
6708 char * buf,
6709 uint32_t len,
6710 struct sockaddr * from,
6711 uint32_t * fromlen ),
6712 ( dns, name, dnsclass, dnstype, buf, len, from, fromlen ) );
6713
6714 static void ResolvDNSQueryCmd( void )
6715 {
6716 OSStatus err;
6717 int n;
6718 dns_handle_t dns = NULL;
6719 uint16_t type, class;
6720 sockaddr_ip from;
6721 uint32_t fromLen;
6722 char time[ kTimestampBufLen ];
6723 uint8_t answer[ 1024 ];
6724
6725 // Make sure that the required symbols are available.
6726
6727 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_open ) )
6728 {
6729 FPrintF( stderr, "Failed to soft link dns_open from libresolv.\n" );
6730 err = kNotFoundErr;
6731 goto exit;
6732 }
6733
6734 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_free ) )
6735 {
6736 FPrintF( stderr, "Failed to soft link dns_free from libresolv.\n" );
6737 err = kNotFoundErr;
6738 goto exit;
6739 }
6740
6741 if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_query ) )
6742 {
6743 FPrintF( stderr, "Failed to soft link dns_query from libresolv.\n" );
6744 err = kNotFoundErr;
6745 goto exit;
6746 }
6747
6748 // Get record type.
6749
6750 err = RecordTypeFromArgString( gResolvDNSQuery_Type, &type );
6751 require_noerr( err, exit );
6752
6753 // Get record class.
6754
6755 if( gResolvDNSQuery_Class )
6756 {
6757 err = RecordClassFromArgString( gResolvDNSQuery_Class, &class );
6758 require_noerr( err, exit );
6759 }
6760 else
6761 {
6762 class = kDNSServiceClass_IN;
6763 }
6764
6765 // Get dns handle.
6766
6767 dns = soft_dns_open( gResolvDNSQuery_Path );
6768 if( !dns )
6769 {
6770 FPrintF( stderr, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path );
6771 err = kUnknownErr;
6772 goto exit;
6773 }
6774
6775 // Print prologue.
6776
6777 FPrintF( stdout, "Name: %s\n", gResolvDNSQuery_Name );
6778 FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type );
6779 FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
6780 FPrintF( stdout, "Path: %s\n", gResolvDNSQuery_Path ? gResolvDNSQuery_Name : "<NULL>" );
6781 FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) );
6782 FPrintF( stdout, "---\n" );
6783
6784 // Call dns_query().
6785
6786 memset( &from, 0, sizeof( from ) );
6787 fromLen = (uint32_t) sizeof( from );
6788 n = soft_dns_query( dns, gResolvDNSQuery_Name, class, type, (char *) answer, (uint32_t) sizeof( answer ), &from.sa,
6789 &fromLen );
6790 if( n < 0 )
6791 {
6792 FPrintF( stderr, "dns_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
6793 err = kUnknownErr;
6794 goto exit;
6795 }
6796
6797 // Print result.
6798
6799 FPrintF( stdout, "From: %##a\n", &from );
6800 FPrintF( stdout, "Message size: %d\n\n", n );
6801 PrintUDNSMessage( answer, (size_t) n, false );
6802
6803 exit:
6804 if( dns ) soft_dns_free( dns );
6805 if( err ) exit( 1 );
6806 }
6807 #endif // TARGET_OS_DARWIN
6808
6809 //===========================================================================================================================
6810 // DaemonVersionCmd
6811 //===========================================================================================================================
6812
6813 static void DaemonVersionCmd( void )
6814 {
6815 OSStatus err;
6816 uint32_t size, version;
6817 char strBuf[ 16 ];
6818
6819 size = (uint32_t) sizeof( version );
6820 err = DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion, &version, &size );
6821 require_noerr( err, exit );
6822
6823 FPrintF( stdout, "Daemon version: %s\n", SourceVersionToCString( version, strBuf ) );
6824
6825 exit:
6826 if( err ) exit( 1 );
6827 }
6828
6829 //===========================================================================================================================
6830 // Exit
6831 //===========================================================================================================================
6832
6833 static void Exit( void *inContext )
6834 {
6835 const char * const reason = (const char *) inContext;
6836 char time[ kTimestampBufLen ];
6837
6838 FPrintF( stdout, "---\n" );
6839 FPrintF( stdout, "End time: %s\n", GetTimestampStr( time ) );
6840 if( reason ) FPrintF( stdout, "End reason: %s\n", reason );
6841 exit( gExitCode );
6842 }
6843
6844 //===========================================================================================================================
6845 // GetTimestampStr
6846 //===========================================================================================================================
6847
6848 static char * GetTimestampStr( char inBuffer[ kTimestampBufLen ] )
6849 {
6850 struct timeval now;
6851 struct tm * tm;
6852 size_t len;
6853
6854 gettimeofday( &now, NULL );
6855 tm = localtime( &now.tv_sec );
6856 require_action( tm, exit, *inBuffer = '\0' );
6857
6858 len = strftime( inBuffer, kTimestampBufLen, "%Y-%m-%d %H:%M:%S", tm );
6859 SNPrintF( &inBuffer[ len ], kTimestampBufLen - len, ".%06u", (unsigned int) now.tv_usec );
6860
6861 exit:
6862 return( inBuffer );
6863 }
6864
6865 //===========================================================================================================================
6866 // GetDNSSDFlagsFromOpts
6867 //===========================================================================================================================
6868
6869 static DNSServiceFlags GetDNSSDFlagsFromOpts( void )
6870 {
6871 DNSServiceFlags flags;
6872
6873 flags = (DNSServiceFlags) gDNSSDFlags;
6874 if( flags & kDNSServiceFlagsShareConnection )
6875 {
6876 FPrintF( stderr, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
6877 kDNSServiceFlagsShareConnection );
6878 }
6879
6880 if( gDNSSDFlag_BrowseDomains ) flags |= kDNSServiceFlagsBrowseDomains;
6881 if( gDNSSDFlag_DenyCellular ) flags |= kDNSServiceFlagsDenyCellular;
6882 if( gDNSSDFlag_DenyExpensive ) flags |= kDNSServiceFlagsDenyExpensive;
6883 if( gDNSSDFlag_ForceMulticast ) flags |= kDNSServiceFlagsForceMulticast;
6884 if( gDNSSDFlag_IncludeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
6885 if( gDNSSDFlag_NoAutoRename ) flags |= kDNSServiceFlagsNoAutoRename;
6886 if( gDNSSDFlag_PathEvaluationDone ) flags |= kDNSServiceFlagsPathEvaluationDone;
6887 if( gDNSSDFlag_RegistrationDomains ) flags |= kDNSServiceFlagsRegistrationDomains;
6888 if( gDNSSDFlag_ReturnIntermediates ) flags |= kDNSServiceFlagsReturnIntermediates;
6889 if( gDNSSDFlag_Shared ) flags |= kDNSServiceFlagsShared;
6890 if( gDNSSDFlag_SuppressUnusable ) flags |= kDNSServiceFlagsSuppressUnusable;
6891 if( gDNSSDFlag_Timeout ) flags |= kDNSServiceFlagsTimeout;
6892 if( gDNSSDFlag_UnicastResponse ) flags |= kDNSServiceFlagsUnicastResponse;
6893 if( gDNSSDFlag_Unique ) flags |= kDNSServiceFlagsUnique;
6894 if( gDNSSDFlag_WakeOnResolve ) flags |= kDNSServiceFlagsWakeOnResolve;
6895
6896 return( flags );
6897 }
6898
6899 //===========================================================================================================================
6900 // CreateConnectionFromArgString
6901 //===========================================================================================================================
6902
6903 static OSStatus
6904 CreateConnectionFromArgString(
6905 const char * inString,
6906 dispatch_queue_t inQueue,
6907 DNSServiceRef * outSDRef,
6908 ConnectionDesc * outDesc )
6909 {
6910 OSStatus err;
6911 DNSServiceRef sdRef = NULL;
6912 ConnectionType type;
6913 int32_t pid = -1; // Initializing because the analyzer claims pid may be used uninitialized.
6914 uint8_t uuid[ 16 ];
6915
6916 if( strcasecmp( inString, kConnectionArg_Normal ) == 0 )
6917 {
6918 err = DNSServiceCreateConnection( &sdRef );
6919 require_noerr( err, exit );
6920 type = kConnectionType_Normal;
6921 }
6922 else if( stricmp_prefix( inString, kConnectionArgPrefix_PID ) == 0 )
6923 {
6924 const char * const pidStr = inString + sizeof_string( kConnectionArgPrefix_PID );
6925
6926 err = StringToInt32( pidStr, &pid );
6927 if( err )
6928 {
6929 FPrintF( stderr, "Invalid delegate connection PID value: %s\n", pidStr );
6930 err = kParamErr;
6931 goto exit;
6932 }
6933
6934 memset( uuid, 0, sizeof( uuid ) );
6935 err = DNSServiceCreateDelegateConnection( &sdRef, pid, uuid );
6936 if( err )
6937 {
6938 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err, pid );
6939 goto exit;
6940 }
6941 type = kConnectionType_DelegatePID;
6942 }
6943 else if( stricmp_prefix( inString, kConnectionArgPrefix_UUID ) == 0 )
6944 {
6945 const char * const uuidStr = inString + sizeof_string( kConnectionArgPrefix_UUID );
6946
6947 check_compile_time_code( sizeof( uuid ) == sizeof( uuid_t ) );
6948
6949 err = StringToUUID( uuidStr, kSizeCString, false, uuid );
6950 if( err )
6951 {
6952 FPrintF( stderr, "Invalid delegate connection UUID value: %s\n", uuidStr );
6953 err = kParamErr;
6954 goto exit;
6955 }
6956
6957 err = DNSServiceCreateDelegateConnection( &sdRef, 0, uuid );
6958 if( err )
6959 {
6960 FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err, uuid );
6961 goto exit;
6962 }
6963 type = kConnectionType_DelegateUUID;
6964 }
6965 else
6966 {
6967 FPrintF( stderr, "Unrecognized connection string \"%s\".\n", inString );
6968 err = kParamErr;
6969 goto exit;
6970 }
6971
6972 err = DNSServiceSetDispatchQueue( sdRef, inQueue );
6973 require_noerr( err, exit );
6974
6975 *outSDRef = sdRef;
6976 if( outDesc )
6977 {
6978 outDesc->type = type;
6979 if( type == kConnectionType_DelegatePID ) outDesc->delegate.pid = pid;
6980 else if( type == kConnectionType_DelegateUUID ) memcpy( outDesc->delegate.uuid, uuid, 16 );
6981 }
6982 sdRef = NULL;
6983
6984 exit:
6985 if( sdRef ) DNSServiceRefDeallocate( sdRef );
6986 return( err );
6987 }
6988
6989 //===========================================================================================================================
6990 // InterfaceIndexFromArgString
6991 //===========================================================================================================================
6992
6993 static OSStatus InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex )
6994 {
6995 OSStatus err;
6996 uint32_t ifIndex;
6997
6998 if( inString )
6999 {
7000 ifIndex = if_nametoindex( inString );
7001 if( ifIndex == 0 )
7002 {
7003 err = StringToUInt32( inString, &ifIndex );
7004 if( err )
7005 {
7006 FPrintF( stderr, "Invalid interface value: %s\n", inString );
7007 err = kParamErr;
7008 goto exit;
7009 }
7010 }
7011 }
7012 else
7013 {
7014 ifIndex = 0;
7015 }
7016
7017 *outIndex = ifIndex;
7018 err = kNoErr;
7019
7020 exit:
7021 return( err );
7022 }
7023
7024 //===========================================================================================================================
7025 // RecordDataFromArgString
7026 //===========================================================================================================================
7027
7028 #define kRDataMaxLen UINT16_C( 0xFFFF )
7029
7030 static OSStatus StringToSRVRData( const char *inString, uint8_t **outPtr, size_t *outLen );
7031 static OSStatus StringToTXTRData( const char *inString, char inDelimiter, uint8_t **outPtr, size_t *outLen );
7032
7033 static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen )
7034 {
7035 OSStatus err;
7036 uint8_t * dataPtr = NULL;
7037 size_t dataLen;
7038
7039 if( 0 ) {}
7040
7041 // Domain name
7042
7043 else if( stricmp_prefix( inString, kRDataArgPrefix_Domain ) == 0 )
7044 {
7045 const char * const str = inString + sizeof_string( kRDataArgPrefix_Domain );
7046 uint8_t * end;
7047 uint8_t dname[ kDomainNameLengthMax ];
7048
7049 err = DomainNameFromString( dname, str, &end );
7050 require_noerr( err, exit );
7051
7052 dataLen = (size_t)( end - dname );
7053 dataPtr = malloc( dataLen );
7054 require_action( dataPtr, exit, err = kNoMemoryErr );
7055
7056 memcpy( dataPtr, dname, dataLen );
7057 }
7058
7059 // File path
7060
7061 else if( stricmp_prefix( inString, kRDataArgPrefix_File ) == 0 )
7062 {
7063 const char * const path = inString + sizeof_string( kRDataArgPrefix_File );
7064
7065 err = CopyFileDataByPath( path, (char **) &dataPtr, &dataLen );
7066 require_noerr( err, exit );
7067 require_action( dataLen <= kRDataMaxLen, exit, err = kSizeErr );
7068 }
7069
7070 // Hexadecimal string
7071
7072 else if( stricmp_prefix( inString, kRDataArgPrefix_HexString ) == 0 )
7073 {
7074 const char * const str = inString + sizeof_string( kRDataArgPrefix_HexString );
7075
7076 err = HexToDataCopy( str, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL );
7077 require_noerr( err, exit );
7078 require_action( dataLen <= kRDataMaxLen, exit, err = kSizeErr );
7079 }
7080
7081 // IPv4 address string
7082
7083 else if( stricmp_prefix( inString, kRDataArgPrefix_IPv4 ) == 0 )
7084 {
7085 const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv4 );
7086 const char * end;
7087
7088 dataLen = 4;
7089 dataPtr = (uint8_t *) malloc( dataLen );
7090 require_action( dataPtr, exit, err = kNoMemoryErr );
7091
7092 err = StringToIPv4Address( str, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix,
7093 (uint32_t *) dataPtr, NULL, NULL, NULL, &end );
7094 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
7095 require_noerr( err, exit );
7096
7097 *( (uint32_t *) dataPtr ) = HostToBig32( *( (uint32_t *) dataPtr ) );
7098 }
7099
7100 // IPv6 address string
7101
7102 else if( stricmp_prefix( inString, kRDataArgPrefix_IPv6 ) == 0 )
7103 {
7104 const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv6 );
7105 const char * end;
7106
7107 dataLen = 16;
7108 dataPtr = (uint8_t *) malloc( dataLen );
7109 require_action( dataPtr, exit, err = kNoMemoryErr );
7110
7111 err = StringToIPv6Address( str,
7112 kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
7113 dataPtr, NULL, NULL, NULL, &end );
7114 if( !err && ( *end != '\0' ) ) err = kMalformedErr;
7115 require_noerr( err, exit );
7116 }
7117
7118 // SRV record
7119
7120 else if( stricmp_prefix( inString, kRDataArgPrefix_SRV ) == 0 )
7121 {
7122 const char * const str = inString + sizeof_string( kRDataArgPrefix_SRV );
7123
7124 err = StringToSRVRData( str, &dataPtr, &dataLen );
7125 require_noerr( err, exit );
7126 }
7127
7128 // String with escaped hex and octal bytes
7129
7130 else if( stricmp_prefix( inString, kRDataArgPrefix_String ) == 0 )
7131 {
7132 const char * const str = inString + sizeof_string( kRDataArgPrefix_String );
7133 const char * const end = str + strlen( str );
7134 size_t copiedLen;
7135 size_t totalLen;
7136 Boolean success;
7137
7138 if( str < end )
7139 {
7140 success = ParseQuotedEscapedString( str, end, "", NULL, 0, NULL, &totalLen, NULL );
7141 require_action( success, exit, err = kParamErr );
7142 require_action( totalLen <= kRDataMaxLen, exit, err = kSizeErr );
7143
7144 dataLen = totalLen;
7145 dataPtr = (uint8_t *) malloc( dataLen );
7146 require_action( dataPtr, exit, err = kNoMemoryErr );
7147
7148 success = ParseQuotedEscapedString( str, end, "", (char *) dataPtr, dataLen, &copiedLen, NULL, NULL );
7149 require_action( success, exit, err = kParamErr );
7150 check( copiedLen == dataLen );
7151 }
7152 else
7153 {
7154 dataPtr = NULL;
7155 dataLen = 0;
7156 }
7157 }
7158
7159 // TXT record
7160
7161 else if( stricmp_prefix( inString, kRDataArgPrefix_TXT ) == 0 )
7162 {
7163 const char * const str = inString + sizeof_string( kRDataArgPrefix_TXT );
7164
7165 err = StringToTXTRData( str, ',', &dataPtr, &dataLen );
7166 require_noerr( err, exit );
7167 }
7168
7169 // Unrecognized format
7170
7171 else
7172 {
7173 FPrintF( stderr, "Unrecognized record data string \"%s\".\n", inString );
7174 err = kParamErr;
7175 goto exit;
7176 }
7177
7178 err = kNoErr;
7179 *outDataLen = dataLen;
7180 *outDataPtr = dataPtr;
7181 dataPtr = NULL;
7182
7183 exit:
7184 FreeNullSafe( dataPtr );
7185 return( err );
7186 }
7187
7188 static OSStatus StringToSRVRData( const char *inString, uint8_t **outPtr, size_t *outLen )
7189 {
7190 OSStatus err;
7191 DataBuffer dataBuf;
7192 const char * ptr;
7193 int i;
7194 uint8_t * end;
7195 uint8_t target[ kDomainNameLengthMax ];
7196
7197 DataBuffer_Init( &dataBuf, NULL, 0, ( 3 * 2 ) + kDomainNameLengthMax );
7198
7199 // Parse and set the priority, weight, and port values (all three are unsigned 16-bit values).
7200
7201 ptr = inString;
7202 for( i = 0; i < 3; ++i )
7203 {
7204 char * next;
7205 long value;
7206 uint8_t buf[ 2 ];
7207
7208 value = strtol( ptr, &next, 0 );
7209 require_action_quiet( ( next != ptr ) && ( *next == ',' ), exit, err = kMalformedErr );
7210 require_action_quiet( ( value >= 0 ) && ( value <= UINT16_MAX ), exit, err = kRangeErr );
7211 ptr = next + 1;
7212
7213 WriteBig16( buf, value );
7214
7215 err = DataBuffer_Append( &dataBuf, buf, sizeof( buf ) );
7216 require_noerr( err, exit );
7217 }
7218
7219 // Set the target domain name.
7220
7221 err = DomainNameFromString( target, ptr, &end );
7222 require_noerr_quiet( err, exit );
7223
7224 err = DataBuffer_Append( &dataBuf, target, (size_t)( end - target ) );
7225 require_noerr( err, exit );
7226
7227 err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
7228 require_noerr( err, exit );
7229
7230 exit:
7231 DataBuffer_Free( &dataBuf );
7232 return( err );
7233 }
7234
7235 static OSStatus StringToTXTRData( const char *inString, char inDelimiter, uint8_t **outPtr, size_t *outLen )
7236 {
7237 OSStatus err;
7238 DataBuffer dataBuf;
7239 const char * src;
7240 uint8_t txtStr[ 256 ]; // Buffer for single TXT string: 1 length byte + up to 255 bytes of data.
7241
7242 DataBuffer_Init( &dataBuf, NULL, 0, kRDataMaxLen );
7243
7244 src = inString;
7245 for( ;; )
7246 {
7247 uint8_t * dst = &txtStr[ 1 ];
7248 const uint8_t * const lim = &txtStr[ 256 ];
7249 int c;
7250
7251 while( *src && ( *src != inDelimiter ) )
7252 {
7253 if( ( c = *src++ ) == '\\' )
7254 {
7255 require_action_quiet( *src != '\0', exit, err = kUnderrunErr );
7256 c = *src++;
7257 }
7258 require_action_quiet( dst < lim, exit, err = kOverrunErr );
7259 *dst++ = (uint8_t) c;
7260 }
7261 txtStr[ 0 ] = (uint8_t)( dst - &txtStr[ 1 ] );
7262 err = DataBuffer_Append( &dataBuf, txtStr, 1 + txtStr[ 0 ] );
7263 require_noerr( err, exit );
7264
7265 if( *src == '\0' ) break;
7266 ++src;
7267 }
7268
7269 err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
7270 require_noerr( err, exit );
7271
7272 exit:
7273 DataBuffer_Free( &dataBuf );
7274 return( err );
7275 }
7276
7277 //===========================================================================================================================
7278 // RecordTypeFromArgString
7279 //===========================================================================================================================
7280
7281 typedef struct
7282 {
7283 uint16_t value; // Record type's numeric value.
7284 const char * name; // Record type's name as a string (e.g., "A", "PTR", "SRV").
7285
7286 } RecordType;
7287
7288 static const RecordType kRecordTypes[] =
7289 {
7290 // Common types.
7291
7292 { kDNSServiceType_A, "A" },
7293 { kDNSServiceType_AAAA, "AAAA" },
7294 { kDNSServiceType_PTR, "PTR" },
7295 { kDNSServiceType_SRV, "SRV" },
7296 { kDNSServiceType_TXT, "TXT" },
7297 { kDNSServiceType_CNAME, "CNAME" },
7298 { kDNSServiceType_SOA, "SOA" },
7299 { kDNSServiceType_NSEC, "NSEC" },
7300 { kDNSServiceType_NS, "NS" },
7301 { kDNSServiceType_MX, "MX" },
7302 { kDNSServiceType_ANY, "ANY" },
7303 { kDNSServiceType_OPT, "OPT" },
7304
7305 // Less common types.
7306
7307 { kDNSServiceType_MD, "MD" },
7308 { kDNSServiceType_NS, "NS" },
7309 { kDNSServiceType_MD, "MD" },
7310 { kDNSServiceType_MF, "MF" },
7311 { kDNSServiceType_MB, "MB" },
7312 { kDNSServiceType_MG, "MG" },
7313 { kDNSServiceType_MR, "MR" },
7314 { kDNSServiceType_NULL, "NULL" },
7315 { kDNSServiceType_WKS, "WKS" },
7316 { kDNSServiceType_HINFO, "HINFO" },
7317 { kDNSServiceType_MINFO, "MINFO" },
7318 { kDNSServiceType_RP, "RP" },
7319 { kDNSServiceType_AFSDB, "AFSDB" },
7320 { kDNSServiceType_X25, "X25" },
7321 { kDNSServiceType_ISDN, "ISDN" },
7322 { kDNSServiceType_RT, "RT" },
7323 { kDNSServiceType_NSAP, "NSAP" },
7324 { kDNSServiceType_NSAP_PTR, "NSAP_PTR" },
7325 { kDNSServiceType_SIG, "SIG" },
7326 { kDNSServiceType_KEY, "KEY" },
7327 { kDNSServiceType_PX, "PX" },
7328 { kDNSServiceType_GPOS, "GPOS" },
7329 { kDNSServiceType_LOC, "LOC" },
7330 { kDNSServiceType_NXT, "NXT" },
7331 { kDNSServiceType_EID, "EID" },
7332 { kDNSServiceType_NIMLOC, "NIMLOC" },
7333 { kDNSServiceType_ATMA, "ATMA" },
7334 { kDNSServiceType_NAPTR, "NAPTR" },
7335 { kDNSServiceType_KX, "KX" },
7336 { kDNSServiceType_CERT, "CERT" },
7337 { kDNSServiceType_A6, "A6" },
7338 { kDNSServiceType_DNAME, "DNAME" },
7339 { kDNSServiceType_SINK, "SINK" },
7340 { kDNSServiceType_APL, "APL" },
7341 { kDNSServiceType_DS, "DS" },
7342 { kDNSServiceType_SSHFP, "SSHFP" },
7343 { kDNSServiceType_IPSECKEY, "IPSECKEY" },
7344 { kDNSServiceType_RRSIG, "RRSIG" },
7345 { kDNSServiceType_DNSKEY, "DNSKEY" },
7346 { kDNSServiceType_DHCID, "DHCID" },
7347 { kDNSServiceType_NSEC3, "NSEC3" },
7348 { kDNSServiceType_NSEC3PARAM, "NSEC3PARAM" },
7349 { kDNSServiceType_HIP, "HIP" },
7350 { kDNSServiceType_SPF, "SPF" },
7351 { kDNSServiceType_UINFO, "UINFO" },
7352 { kDNSServiceType_UID, "UID" },
7353 { kDNSServiceType_GID, "GID" },
7354 { kDNSServiceType_UNSPEC, "UNSPEC" },
7355 { kDNSServiceType_TKEY, "TKEY" },
7356 { kDNSServiceType_TSIG, "TSIG" },
7357 { kDNSServiceType_IXFR, "IXFR" },
7358 { kDNSServiceType_AXFR, "AXFR" },
7359 { kDNSServiceType_MAILB, "MAILB" },
7360 { kDNSServiceType_MAILA, "MAILA" }
7361 };
7362
7363 static OSStatus RecordTypeFromArgString( const char *inString, uint16_t *outValue )
7364 {
7365 OSStatus err;
7366 int32_t i32;
7367 const RecordType * type;
7368 const RecordType * const end = kRecordTypes + countof( kRecordTypes );
7369
7370 for( type = kRecordTypes; type < end; ++type )
7371 {
7372 if( strcasecmp( type->name, inString ) == 0 )
7373 {
7374 *outValue = type->value;
7375 return( kNoErr );
7376 }
7377 }
7378
7379 err = StringToInt32( inString, &i32 );
7380 require_noerr_quiet( err, exit );
7381 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
7382
7383 *outValue = (uint16_t) i32;
7384
7385 exit:
7386 return( err );
7387 }
7388
7389 //===========================================================================================================================
7390 // RecordClassFromArgString
7391 //===========================================================================================================================
7392
7393 static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outValue )
7394 {
7395 OSStatus err;
7396 int32_t i32;
7397
7398 if( strcasecmp( inString, "IN" ) == 0 )
7399 {
7400 *outValue = kDNSServiceClass_IN;
7401 err = kNoErr;
7402 goto exit;
7403 }
7404
7405 err = StringToInt32( inString, &i32 );
7406 require_noerr_quiet( err, exit );
7407 require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
7408
7409 *outValue = (uint16_t) i32;
7410
7411 exit:
7412 return( err );
7413 }
7414
7415 //===========================================================================================================================
7416 // InterfaceIndexToName
7417 //===========================================================================================================================
7418
7419 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] )
7420 {
7421 switch( inIfIndex )
7422 {
7423 case kDNSServiceInterfaceIndexAny:
7424 strlcpy( inNameBuf, "Any", kInterfaceNameBufLen );
7425 break;
7426
7427 case kDNSServiceInterfaceIndexLocalOnly:
7428 strlcpy( inNameBuf, "LocalOnly", kInterfaceNameBufLen );
7429 break;
7430
7431 case kDNSServiceInterfaceIndexUnicast:
7432 strlcpy( inNameBuf, "Unicast", kInterfaceNameBufLen );
7433 break;
7434
7435 case kDNSServiceInterfaceIndexP2P:
7436 strlcpy( inNameBuf, "P2P", kInterfaceNameBufLen );
7437 break;
7438
7439 #if( defined( kDNSServiceInterfaceIndexBLE ) )
7440 case kDNSServiceInterfaceIndexBLE:
7441 strlcpy( inNameBuf, "BLE", kInterfaceNameBufLen );
7442 break;
7443 #endif
7444
7445 default:
7446 {
7447 const char * name;
7448
7449 name = if_indextoname( inIfIndex, inNameBuf );
7450 if( !name ) strlcpy( inNameBuf, "NO NAME", kInterfaceNameBufLen );
7451 break;
7452 }
7453 }
7454
7455 return( inNameBuf );
7456 }
7457
7458 //===========================================================================================================================
7459 // RecordTypeToString
7460 //===========================================================================================================================
7461
7462 static const char * RecordTypeToString( unsigned int inValue )
7463 {
7464 const RecordType * type;
7465 const RecordType * const end = kRecordTypes + countof( kRecordTypes );
7466
7467 for( type = kRecordTypes; type < end; ++type )
7468 {
7469 if( type->value == inValue ) return( type->name );
7470 }
7471 return( "???" );
7472 }
7473
7474 //===========================================================================================================================
7475 // DNSMessageExtractDomainName
7476 //===========================================================================================================================
7477
7478 #define IsCompressionByte( X ) ( ( ( X ) & 0xC0 ) == 0xC0 )
7479
7480 static OSStatus
7481 DNSMessageExtractDomainName(
7482 const uint8_t * inMsgPtr,
7483 size_t inMsgLen,
7484 const uint8_t * inNamePtr,
7485 uint8_t inBuf[ kDomainNameLengthMax ],
7486 const uint8_t ** outNextPtr )
7487 {
7488 OSStatus err;
7489 const uint8_t * label;
7490 uint8_t labelLen;
7491 const uint8_t * nextLabel;
7492 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
7493 uint8_t * dst = inBuf;
7494 const uint8_t * const dstLim = inBuf ? ( inBuf + kDomainNameLengthMax ) : NULL;
7495 const uint8_t * nameEnd = NULL;
7496
7497 require_action( ( inNamePtr >= inMsgPtr ) && ( inNamePtr < msgEnd ), exit, err = kRangeErr );
7498
7499 for( label = inNamePtr; ( labelLen = label[ 0 ] ) != 0; label = nextLabel )
7500 {
7501 if( labelLen <= kDomainLabelLengthMax )
7502 {
7503 nextLabel = label + 1 + labelLen;
7504 require_action( nextLabel < msgEnd, exit, err = kUnderrunErr );
7505 if( dst )
7506 {
7507 require_action( ( dstLim - dst ) > ( 1 + labelLen ), exit, err = kOverrunErr );
7508 memcpy( dst, label, 1 + labelLen );
7509 dst += ( 1 + labelLen );
7510 }
7511 }
7512 else if( IsCompressionByte( labelLen ) )
7513 {
7514 uint16_t offset;
7515
7516 require_action( ( msgEnd - label ) >= 2, exit, err = kUnderrunErr );
7517 if( !nameEnd )
7518 {
7519 nameEnd = label + 2;
7520 if( !dst ) break;
7521 }
7522 offset = (uint16_t)( ( ( label[ 0 ] & 0x3F ) << 8 ) | label[ 1 ] );
7523 nextLabel = inMsgPtr + offset;
7524 require_action( nextLabel < msgEnd, exit, err = kUnderrunErr );
7525 require_action( !IsCompressionByte( nextLabel[ 0 ] ), exit, err = kMalformedErr );
7526 }
7527 else
7528 {
7529 dlogassert( "Unhandled label length 0x%02X\n", labelLen );
7530 err = kMalformedErr;
7531 goto exit;
7532 }
7533 }
7534
7535 if( dst ) *dst = 0;
7536 if( !nameEnd ) nameEnd = label + 1;
7537
7538 if( outNextPtr ) *outNextPtr = nameEnd;
7539 err = kNoErr;
7540
7541 exit:
7542 return( err );
7543 }
7544
7545 //===========================================================================================================================
7546 // DNSMessageExtractDomainNameString
7547 //===========================================================================================================================
7548
7549 static OSStatus
7550 DNSMessageExtractDomainNameString(
7551 const void * inMsgPtr,
7552 size_t inMsgLen,
7553 const void * inNamePtr,
7554 char inBuf[ kDNSServiceMaxDomainName ],
7555 const uint8_t ** outNextPtr )
7556 {
7557 OSStatus err;
7558 const uint8_t * nextPtr;
7559 uint8_t domainName[ kDomainNameLengthMax ];
7560
7561 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inNamePtr, domainName, &nextPtr );
7562 require_noerr( err, exit );
7563
7564 err = DomainNameToString( domainName, NULL, inBuf, NULL );
7565 require_noerr( err, exit );
7566
7567 if( outNextPtr ) *outNextPtr = nextPtr;
7568
7569 exit:
7570 return( err );
7571 }
7572
7573 //===========================================================================================================================
7574 // DNSMessageExtractRecord
7575 //===========================================================================================================================
7576
7577 typedef struct
7578 {
7579 uint8_t type[ 2 ];
7580 uint8_t class[ 2 ];
7581 uint8_t ttl[ 4 ];
7582 uint8_t rdLength[ 2 ];
7583 uint8_t rdata[ 1 ];
7584
7585 } DNSRecordFields;
7586
7587 check_compile_time( offsetof( DNSRecordFields, rdata ) == 10 );
7588
7589 static OSStatus
7590 DNSMessageExtractRecord(
7591 const uint8_t * inMsgPtr,
7592 size_t inMsgLen,
7593 const uint8_t * inPtr,
7594 uint8_t inNameBuf[ kDomainNameLengthMax ],
7595 uint16_t * outType,
7596 uint16_t * outClass,
7597 uint32_t * outTTL,
7598 const uint8_t ** outRDataPtr,
7599 size_t * outRDataLen,
7600 const uint8_t ** outPtr )
7601 {
7602 OSStatus err;
7603 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
7604 const uint8_t * ptr;
7605 const DNSRecordFields * record;
7606 size_t rdLength;
7607
7608 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, inPtr, inNameBuf, &ptr );
7609 require_noerr_quiet( err, exit );
7610 require_action_quiet( (size_t)( msgEnd - ptr ) >= offsetof( DNSRecordFields, rdata ), exit, err = kUnderrunErr );
7611
7612 record = (DNSRecordFields *) ptr;
7613 rdLength = ReadBig16( record->rdLength );
7614 require_action_quiet( (size_t)( msgEnd - record->rdata ) >= rdLength , exit, err = kUnderrunErr );
7615
7616 if( outType ) *outType = ReadBig16( record->type );
7617 if( outClass ) *outClass = ReadBig16( record->class );
7618 if( outTTL ) *outTTL = ReadBig32( record->ttl );
7619 if( outRDataPtr ) *outRDataPtr = record->rdata;
7620 if( outRDataLen ) *outRDataLen = rdLength;
7621 if( outPtr ) *outPtr = record->rdata + rdLength;
7622
7623 exit:
7624 return( err );
7625 }
7626
7627 //===========================================================================================================================
7628 // DNSMessageGetAnswerSection
7629 //===========================================================================================================================
7630
7631 static OSStatus DNSMessageGetAnswerSection( const uint8_t *inMsgPtr, size_t inMsgLen, const uint8_t **outPtr )
7632 {
7633 OSStatus err;
7634 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
7635 unsigned int questionCount, i;
7636 const DNSHeader * hdr;
7637 const uint8_t * ptr;
7638
7639 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
7640
7641 hdr = (DNSHeader *) inMsgPtr;
7642 questionCount = DNSHeaderGetQuestionCount( hdr );
7643
7644 ptr = (uint8_t *)( hdr + 1 );
7645 for( i = 0; i < questionCount; ++i )
7646 {
7647 err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, ptr, NULL, &ptr );
7648 require_noerr( err, exit );
7649 require_action_quiet( ( msgEnd - ptr ) >= 4, exit, err = kUnderrunErr );
7650 ptr += 4;
7651 }
7652
7653 if( outPtr ) *outPtr = ptr;
7654 err = kNoErr;
7655
7656 exit:
7657 return( err );
7658 }
7659
7660 //===========================================================================================================================
7661 // DNSRecordDataToString
7662 //===========================================================================================================================
7663
7664 static OSStatus
7665 DNSRecordDataToString(
7666 const void * inRDataPtr,
7667 size_t inRDataLen,
7668 unsigned int inRDataType,
7669 const void * inMsgPtr,
7670 size_t inMsgLen,
7671 char ** outString )
7672 {
7673 OSStatus err;
7674 const uint8_t * const rdataPtr = (uint8_t *) inRDataPtr;
7675 const uint8_t * const rdataEnd = rdataPtr + inRDataLen;
7676 char * rdataStr;
7677 const uint8_t * ptr;
7678 int n;
7679 char domainNameStr[ kDNSServiceMaxDomainName ];
7680
7681 rdataStr = NULL;
7682 if( inRDataType == kDNSServiceType_A )
7683 {
7684 require_action_quiet( inRDataLen == 4, exit, err = kMalformedErr );
7685
7686 ASPrintF( &rdataStr, "%.4a", rdataPtr );
7687 require_action( rdataStr, exit, err = kNoMemoryErr );
7688 }
7689 else if( inRDataType == kDNSServiceType_AAAA )
7690 {
7691 require_action_quiet( inRDataLen == 16, exit, err = kMalformedErr );
7692
7693 ASPrintF( &rdataStr, "%.16a", rdataPtr );
7694 require_action( rdataStr, exit, err = kNoMemoryErr );
7695 }
7696 else if( ( inRDataType == kDNSServiceType_PTR ) || ( inRDataType == kDNSServiceType_CNAME ) ||
7697 ( inRDataType == kDNSServiceType_NS ) )
7698 {
7699 if( inMsgPtr )
7700 {
7701 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, NULL );
7702 require_noerr( err, exit );
7703 }
7704 else
7705 {
7706 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, NULL );
7707 require_noerr( err, exit );
7708 }
7709
7710 rdataStr = strdup( domainNameStr );
7711 require_action( rdataStr, exit, err = kNoMemoryErr );
7712 }
7713 else if( inRDataType == kDNSServiceType_SRV )
7714 {
7715 uint16_t priority, weight, port;
7716 const uint8_t * target;
7717
7718 require_action_quiet( ( rdataPtr + 6 ) < rdataEnd, exit, err = kMalformedErr );
7719
7720 priority = ReadBig16( rdataPtr );
7721 weight = ReadBig16( rdataPtr + 2 );
7722 port = ReadBig16( rdataPtr + 4 );
7723 target = rdataPtr + 6;
7724
7725 if( inMsgPtr )
7726 {
7727 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, target, domainNameStr, NULL );
7728 require_noerr( err, exit );
7729 }
7730 else
7731 {
7732 err = DomainNameToString( target, rdataEnd, domainNameStr, NULL );
7733 require_noerr( err, exit );
7734 }
7735
7736 ASPrintF( &rdataStr, "%u %u %u %s", priority, weight, port, domainNameStr );
7737 require_action( rdataStr, exit, err = kNoMemoryErr );
7738 }
7739 else if( inRDataType == kDNSServiceType_TXT )
7740 {
7741 require_action_quiet( inRDataLen > 0, exit, err = kMalformedErr );
7742
7743 if( inRDataLen == 1 )
7744 {
7745 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) inRDataLen, INT_MAX );
7746 require_action( rdataStr, exit, err = kNoMemoryErr );
7747 }
7748 else
7749 {
7750 ASPrintF( &rdataStr, "%#{txt}", rdataPtr, inRDataLen );
7751 require_action( rdataStr, exit, err = kNoMemoryErr );
7752 }
7753 }
7754 else if( inRDataType == kDNSServiceType_SOA )
7755 {
7756 uint32_t serial, refresh, retry, expire, minimum;
7757
7758 if( inMsgPtr )
7759 {
7760 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
7761 require_noerr( err, exit );
7762
7763 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
7764
7765 rdataStr = strdup( domainNameStr );
7766 require_action( rdataStr, exit, err = kNoMemoryErr );
7767
7768 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, ptr, domainNameStr, &ptr );
7769 require_noerr( err, exit );
7770 }
7771 else
7772 {
7773 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
7774 require_noerr( err, exit );
7775
7776 rdataStr = strdup( domainNameStr );
7777 require_action( rdataStr, exit, err = kNoMemoryErr );
7778
7779 err = DomainNameToString( ptr, rdataEnd, domainNameStr, &ptr );
7780 require_noerr( err, exit );
7781 }
7782
7783 require_action_quiet( ( ptr + 20 ) == rdataEnd, exit, err = kMalformedErr );
7784
7785 serial = ReadBig32( ptr );
7786 refresh = ReadBig32( ptr + 4 );
7787 retry = ReadBig32( ptr + 8 );
7788 expire = ReadBig32( ptr + 12 );
7789 minimum = ReadBig32( ptr + 16 );
7790
7791 n = AppendPrintF( &rdataStr, " %s %u %u %u %u %u\n", domainNameStr, serial, refresh, retry, expire, minimum );
7792 require_action( n > 0, exit, err = kUnknownErr );
7793 }
7794 else if( inRDataType == kDNSServiceType_NSEC )
7795 {
7796 unsigned int windowBlock, bitmapLen, i, recordType;
7797 const uint8_t * bitmapPtr;
7798
7799 if( inMsgPtr )
7800 {
7801 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, rdataPtr, domainNameStr, &ptr );
7802 require_noerr( err, exit );
7803 }
7804 else
7805 {
7806 err = DomainNameToString( rdataPtr, rdataEnd, domainNameStr, &ptr );
7807 require_noerr( err, exit );
7808 }
7809
7810 require_action_quiet( ptr < rdataEnd, exit, err = kMalformedErr );
7811
7812 rdataStr = strdup( domainNameStr );
7813 require_action( rdataStr, exit, err = kNoMemoryErr );
7814
7815 for( ; ptr < rdataEnd; ptr += ( 2 + bitmapLen ) )
7816 {
7817 require_action_quiet( ( ptr + 2 ) < rdataEnd, exit, err = kMalformedErr );
7818
7819 windowBlock = ptr[ 0 ];
7820 bitmapLen = ptr[ 1 ];
7821 bitmapPtr = &ptr[ 2 ];
7822
7823 require_action_quiet( ( bitmapLen >= 1 ) && ( bitmapLen <= 32 ) , exit, err = kMalformedErr );
7824 require_action_quiet( ( bitmapPtr + bitmapLen ) <= rdataEnd, exit, err = kMalformedErr );
7825
7826 for( i = 0; i < BitArray_MaxBits( bitmapLen ); ++i )
7827 {
7828 if( BitArray_GetBit( bitmapPtr, bitmapLen, i ) )
7829 {
7830 recordType = ( windowBlock * 256 ) + i;
7831 n = AppendPrintF( &rdataStr, " %s", RecordTypeToString( recordType ) );
7832 require_action( n > 0, exit, err = kUnknownErr );
7833 }
7834 }
7835 }
7836 }
7837 else if( inRDataType == kDNSServiceType_MX )
7838 {
7839 uint16_t preference;
7840 const uint8_t * exchange;
7841
7842 require_action_quiet( ( rdataPtr + 2 ) < rdataEnd, exit, err = kMalformedErr );
7843
7844 preference = ReadBig16( rdataPtr );
7845 exchange = &rdataPtr[ 2 ];
7846
7847 if( inMsgPtr )
7848 {
7849 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, exchange, domainNameStr, NULL );
7850 require_noerr( err, exit );
7851 }
7852 else
7853 {
7854 err = DomainNameToString( exchange, rdataEnd, domainNameStr, NULL );
7855 require_noerr( err, exit );
7856 }
7857
7858 n = ASPrintF( &rdataStr, "%u %s", preference, domainNameStr );
7859 require_action( n > 0, exit, err = kUnknownErr );
7860 }
7861 else
7862 {
7863 err = kNotHandledErr;
7864 goto exit;
7865 }
7866
7867 check( rdataStr );
7868 *outString = rdataStr;
7869 rdataStr = NULL;
7870 err = kNoErr;
7871
7872 exit:
7873 FreeNullSafe( rdataStr );
7874 return( err );
7875 }
7876
7877 //===========================================================================================================================
7878 // DomainNameAppendString
7879 //===========================================================================================================================
7880
7881 static OSStatus
7882 DomainNameAppendString(
7883 uint8_t inDomainName[ kDomainNameLengthMax ],
7884 const char * inString,
7885 uint8_t ** outEndPtr )
7886 {
7887 OSStatus err;
7888 const char * src;
7889 uint8_t * root;
7890 const uint8_t * const nameLim = inDomainName + kDomainNameLengthMax;
7891
7892 for( root = inDomainName; ( root < nameLim ) && *root; root += ( 1 + *root ) ) {}
7893 require_action_quiet( root < nameLim, exit, err = kMalformedErr );
7894
7895 // If the string is a single dot, denoting the root domain, then there are no non-empty labels.
7896
7897 src = inString;
7898 if( ( src[ 0 ] == '.' ) && ( src[ 1 ] == '\0' ) ) ++src;
7899 while( *src )
7900 {
7901 uint8_t * const label = root;
7902 const uint8_t * const labelLim = Min( &label[ 1 + kDomainLabelLengthMax ], nameLim - 1 );
7903 uint8_t * dst;
7904 int c;
7905 size_t labelLen;
7906
7907 dst = &label[ 1 ];
7908 while( *src && ( ( c = *src++ ) != '.' ) )
7909 {
7910 if( c == '\\' )
7911 {
7912 require_action_quiet( *src != '\0', exit, err = kUnderrunErr );
7913 c = *src++;
7914 if( isdigit_safe( c ) && isdigit_safe( src[ 0 ] ) && isdigit_safe( src[ 1 ] ) )
7915 {
7916 const int decimal = ( ( c - '0' ) * 100 ) + ( ( src[ 0 ] - '0' ) * 10 ) + ( src[ 1 ] - '0' );
7917
7918 if( decimal <= 255 )
7919 {
7920 c = decimal;
7921 src += 2;
7922 }
7923 }
7924 }
7925 require_action_quiet( dst < labelLim, exit, err = kOverrunErr );
7926 *dst++ = (uint8_t) c;
7927 }
7928
7929 labelLen = (size_t)( dst - &label[ 1 ] );
7930 require_action_quiet( labelLen > 0, exit, err = kMalformedErr );
7931
7932 label[ 0 ] = (uint8_t) labelLen;
7933 root = dst;
7934 *root = 0;
7935 }
7936
7937 if( outEndPtr ) *outEndPtr = root + 1;
7938 err = kNoErr;
7939
7940 exit:
7941 return( err );
7942 }
7943
7944 //===========================================================================================================================
7945 // DomainNameEqual
7946 //===========================================================================================================================
7947
7948 static Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 )
7949 {
7950 const uint8_t * p1 = inName1;
7951 const uint8_t * p2 = inName2;
7952 unsigned int len;
7953
7954 for( ;; )
7955 {
7956 if( ( len = *p1++ ) != *p2++ ) return( false );
7957 if( len == 0 ) break;
7958 for( ; len > 0; ++p1, ++p2, --len )
7959 {
7960 if( tolower_safe( *p1 ) != tolower_safe( *p2 ) ) return( false );
7961 }
7962 }
7963 return( true );
7964 }
7965
7966 //===========================================================================================================================
7967 // DomainNameFromString
7968 //===========================================================================================================================
7969
7970 static OSStatus
7971 DomainNameFromString(
7972 uint8_t inDomainName[ kDomainNameLengthMax ],
7973 const char * inString,
7974 uint8_t ** outEndPtr )
7975 {
7976 inDomainName[ 0 ] = 0;
7977 return( DomainNameAppendString( inDomainName, inString, outEndPtr ) );
7978 }
7979
7980 //===========================================================================================================================
7981 // DomainNameToString
7982 //===========================================================================================================================
7983
7984 static OSStatus
7985 DomainNameToString(
7986 const uint8_t * inDomainName,
7987 const uint8_t * inEnd,
7988 char inBuf[ kDNSServiceMaxDomainName ],
7989 const uint8_t ** outNextPtr )
7990 {
7991 OSStatus err;
7992 const uint8_t * label;
7993 uint8_t labelLen;
7994 const uint8_t * nextLabel;
7995 char * dst;
7996 const uint8_t * src;
7997
7998 require_action( !inEnd || ( inDomainName < inEnd ), exit, err = kUnderrunErr );
7999
8000 // Convert each label up until the root label, i.e., the zero-length label.
8001
8002 dst = inBuf;
8003 for( label = inDomainName; ( labelLen = label[ 0 ] ) != 0; label = nextLabel )
8004 {
8005 require_action( labelLen <= kDomainLabelLengthMax, exit, err = kMalformedErr );
8006
8007 nextLabel = &label[ 1 ] + labelLen;
8008 require_action( ( nextLabel - inDomainName ) < kDomainNameLengthMax, exit, err = kMalformedErr );
8009 require_action( !inEnd || ( nextLabel < inEnd ), exit, err = kUnderrunErr );
8010
8011 for( src = &label[ 1 ]; src < nextLabel; ++src )
8012 {
8013 if( isprint_safe( *src ) )
8014 {
8015 if( ( *src == '.' ) || ( *src == '\\' ) || ( *src == ' ' ) ) *dst++ = '\\';
8016 *dst++ = (char) *src;
8017 }
8018 else
8019 {
8020 *dst++ = '\\';
8021 *dst++ = '0' + ( *src / 100 );
8022 *dst++ = '0' + ( ( *src / 10 ) % 10 );
8023 *dst++ = '0' + ( *src % 10 );
8024 }
8025 }
8026 *dst++ = '.';
8027 }
8028
8029 // At this point, label points to the root label.
8030 // If the root label was the only label, then write a dot for it.
8031
8032 if( label == inDomainName ) *dst++ = '.';
8033 *dst = '\0';
8034 if( outNextPtr ) *outNextPtr = label + 1;
8035 err = kNoErr;
8036
8037 exit:
8038 return( err );
8039 }
8040
8041 //===========================================================================================================================
8042 // PrintDNSMessage
8043 //===========================================================================================================================
8044
8045 #define DNSFlagsOpCodeToString( X ) ( \
8046 ( (X) == kDNSOpCode_Query ) ? "Query" : \
8047 ( (X) == kDNSOpCode_InverseQuery ) ? "IQuery" : \
8048 ( (X) == kDNSOpCode_Status ) ? "Status" : \
8049 ( (X) == kDNSOpCode_Notify ) ? "Notify" : \
8050 ( (X) == kDNSOpCode_Update ) ? "Update" : \
8051 "Unassigned" )
8052
8053 #define DNSFlagsRCodeToString( X ) ( \
8054 ( (X) == kDNSRCode_NoError ) ? "NoError" : \
8055 ( (X) == kDNSRCode_FormatError ) ? "FormErr" : \
8056 ( (X) == kDNSRCode_ServerFailure ) ? "ServFail" : \
8057 ( (X) == kDNSRCode_NXDomain ) ? "NXDomain" : \
8058 ( (X) == kDNSRCode_NotImplemented ) ? "NotImp" : \
8059 ( (X) == kDNSRCode_Refused ) ? "Refused" : \
8060 "???" )
8061
8062 #define DNSFlagsGetOpCode( X ) ( ( (X) >> 11 ) & 0x0F )
8063 #define DNSFlagsGetRCode( X ) ( (X) & 0x0F )
8064
8065 static OSStatus PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, const Boolean inIsMDNS, const Boolean inPrintRaw )
8066 {
8067 OSStatus err;
8068 const DNSHeader * hdr;
8069 const uint8_t * const msgEnd = inMsgPtr + inMsgLen;
8070 const uint8_t * ptr;
8071 unsigned int id, flags, opcode, rcode;
8072 unsigned int questionCount, answerCount, authorityCount, additionalCount, i, totalRRCount;
8073 char nameStr[ kDNSServiceMaxDomainName ];
8074
8075 require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
8076
8077 hdr = (DNSHeader *) inMsgPtr;
8078 id = DNSHeaderGetID( hdr );
8079 flags = DNSHeaderGetFlags( hdr );
8080 questionCount = DNSHeaderGetQuestionCount( hdr );
8081 answerCount = DNSHeaderGetAnswerCount( hdr );
8082 authorityCount = DNSHeaderGetAuthorityCount( hdr );
8083 additionalCount = DNSHeaderGetAdditionalCount( hdr );
8084 opcode = DNSFlagsGetOpCode( flags );
8085 rcode = DNSFlagsGetRCode( flags );
8086
8087 FPrintF( stdout, "ID: 0x%04X (%u)\n", id, id );
8088 FPrintF( stdout, "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA %s\n",
8089 flags,
8090 ( flags & kDNSHeaderFlag_Response ) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode ),
8091 ( flags & kDNSHeaderFlag_AuthAnswer ) ? ' ' : '!',
8092 ( flags & kDNSHeaderFlag_Truncation ) ? ' ' : '!',
8093 ( flags & kDNSHeaderFlag_RecursionDesired ) ? ' ' : '!',
8094 ( flags & kDNSHeaderFlag_RecursionAvailable ) ? ' ' : '!',
8095 DNSFlagsRCodeToString( rcode ) );
8096 FPrintF( stdout, "Question count: %u\n", questionCount );
8097 FPrintF( stdout, "Answer count: %u\n", answerCount );
8098 FPrintF( stdout, "Authority count: %u\n", authorityCount );
8099 FPrintF( stdout, "Additional count: %u\n", additionalCount );
8100
8101 ptr = (uint8_t *)( hdr + 1 );
8102 for( i = 0; i < questionCount; ++i )
8103 {
8104 unsigned int qType, qClass;
8105 Boolean isQU;
8106
8107 err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, ptr, nameStr, &ptr );
8108 require_noerr( err, exit );
8109
8110 if( ( msgEnd - ptr ) < 4 )
8111 {
8112 err = kUnderrunErr;
8113 goto exit;
8114 }
8115
8116 qType = ReadBig16( ptr );
8117 ptr += 2;
8118 qClass = ReadBig16( ptr );
8119 ptr += 2;
8120
8121 isQU = ( inIsMDNS && ( qClass & kQClassUnicastResponseBit ) ) ? true : false;
8122 if( inIsMDNS ) qClass &= ~kQClassUnicastResponseBit;
8123
8124 if( i == 0 ) FPrintF( stdout, "\nQUESTION SECTION\n" );
8125
8126 FPrintF( stdout, "%s %2s %?2s%?2u %-5s\n",
8127 nameStr, inIsMDNS ? ( isQU ? "QU" : "QM" ) : "",
8128 ( qClass == kDNSServiceClass_IN ), "IN", ( qClass != kDNSServiceClass_IN ), qClass,
8129 RecordTypeToString( qType ) );
8130 }
8131
8132 totalRRCount = answerCount + authorityCount + additionalCount;
8133 for( i = 0; i < totalRRCount; ++i )
8134 {
8135 uint16_t type;
8136 uint16_t class;
8137 uint32_t ttl;
8138 const uint8_t * rdataPtr;
8139 size_t rdataLen;
8140 char * rdataStr;
8141 Boolean cacheFlush;
8142 uint8_t name[ kDomainNameLengthMax ];
8143
8144 err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, &ttl, &rdataPtr, &rdataLen, &ptr );
8145 require_noerr( err, exit );
8146
8147 err = DomainNameToString( name, NULL, nameStr, NULL );
8148 require_noerr( err, exit );
8149
8150 cacheFlush = ( inIsMDNS && ( class & kRRClassCacheFlushBit ) ) ? true : false;
8151 if( inIsMDNS ) class &= ~kRRClassCacheFlushBit;
8152
8153 rdataStr = NULL;
8154 if( !inPrintRaw ) DNSRecordDataToString( rdataPtr, rdataLen, type, inMsgPtr, inMsgLen, &rdataStr );
8155 if( !rdataStr )
8156 {
8157 ASPrintF( &rdataStr, "%#H", rdataPtr, (int) rdataLen, INT_MAX );
8158 require_action( rdataStr, exit, err = kNoMemoryErr );
8159 }
8160
8161 if( answerCount && ( i == 0 ) ) FPrintF( stdout, "\nANSWER SECTION\n" );
8162 else if( authorityCount && ( i == answerCount ) ) FPrintF( stdout, "\nAUTHORITY SECTION\n" );
8163 else if( additionalCount && ( i == ( answerCount + authorityCount ) ) ) FPrintF( stdout, "\nADDITIONAL SECTION\n" );
8164
8165 FPrintF( stdout, "%-42s %6u %2s %?2s%?2u %-5s %s\n",
8166 nameStr, ttl, cacheFlush ? "CF" : "",
8167 ( class == kDNSServiceClass_IN ), "IN", ( class != kDNSServiceClass_IN ), class,
8168 RecordTypeToString( type ), rdataStr );
8169 free( rdataStr );
8170 }
8171 FPrintF( stdout, "\n" );
8172 err = kNoErr;
8173
8174 exit:
8175 return( err );
8176 }
8177
8178 //===========================================================================================================================
8179 // WriteDNSQueryMessage
8180 //===========================================================================================================================
8181
8182 static OSStatus
8183 WriteDNSQueryMessage(
8184 uint8_t inMsg[ kDNSQueryMessageMaxLen ],
8185 uint16_t inMsgID,
8186 uint16_t inFlags,
8187 const char * inQName,
8188 uint16_t inQType,
8189 uint16_t inQClass,
8190 size_t * outMsgLen )
8191 {
8192 OSStatus err;
8193 DNSHeader * const hdr = (DNSHeader *) inMsg;
8194 uint8_t * ptr;
8195 size_t msgLen;
8196
8197 WriteBig16( hdr->id, inMsgID );
8198 WriteBig16( hdr->flags, inFlags );
8199 WriteBig16( hdr->questionCount, 1 );
8200 WriteBig16( hdr->answerCount, 0 );
8201 WriteBig16( hdr->authorityCount, 0 );
8202 WriteBig16( hdr->additionalCount, 0 );
8203
8204 ptr = (uint8_t *)( hdr + 1 );
8205 err = DomainNameFromString( ptr, inQName, &ptr );
8206 require_noerr_quiet( err, exit );
8207
8208 WriteBig16( ptr, inQType );
8209 ptr += 2;
8210 WriteBig16( ptr, inQClass );
8211 ptr += 2;
8212 msgLen = (size_t)( ptr - inMsg );
8213 check( msgLen <= kDNSQueryMessageMaxLen );
8214
8215 if( outMsgLen ) *outMsgLen = msgLen;
8216
8217 exit:
8218 return( err );
8219 }
8220
8221 //===========================================================================================================================
8222 // DispatchSignalSourceCreate
8223 //===========================================================================================================================
8224
8225 static OSStatus
8226 DispatchSignalSourceCreate(
8227 int inSignal,
8228 DispatchHandler inEventHandler,
8229 void * inContext,
8230 dispatch_source_t * outSource )
8231 {
8232 OSStatus err;
8233 dispatch_source_t source;
8234
8235 source = dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) inSignal, 0, dispatch_get_main_queue() );
8236 require_action( source, exit, err = kUnknownErr );
8237
8238 dispatch_set_context( source, inContext );
8239 dispatch_source_set_event_handler_f( source, inEventHandler );
8240
8241 *outSource = source;
8242 err = kNoErr;
8243
8244 exit:
8245 return( err );
8246 }
8247
8248 //===========================================================================================================================
8249 // DispatchReadSourceCreate
8250 //===========================================================================================================================
8251
8252 static OSStatus
8253 DispatchReadSourceCreate(
8254 SocketRef inSock,
8255 DispatchHandler inEventHandler,
8256 DispatchHandler inCancelHandler,
8257 void * inContext,
8258 dispatch_source_t * outSource )
8259 {
8260 OSStatus err;
8261 dispatch_source_t source;
8262
8263 source = dispatch_source_create( DISPATCH_SOURCE_TYPE_READ, (uintptr_t) inSock, 0, dispatch_get_main_queue() );
8264 require_action( source, exit, err = kUnknownErr );
8265
8266 dispatch_set_context( source, inContext );
8267 dispatch_source_set_event_handler_f( source, inEventHandler );
8268 dispatch_source_set_cancel_handler_f( source, inCancelHandler );
8269
8270 *outSource = source;
8271 err = kNoErr;
8272
8273 exit:
8274 return( err );
8275 }
8276
8277 //===========================================================================================================================
8278 // DispatchTimerCreate
8279 //===========================================================================================================================
8280
8281 static OSStatus
8282 DispatchTimerCreate(
8283 dispatch_time_t inStart,
8284 uint64_t inIntervalNs,
8285 uint64_t inLeewayNs,
8286 DispatchHandler inEventHandler,
8287 DispatchHandler inCancelHandler,
8288 void * inContext,
8289 dispatch_source_t * outTimer )
8290 {
8291 OSStatus err;
8292 dispatch_source_t timer;
8293
8294 timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue() );
8295 require_action( timer, exit, err = kUnknownErr );
8296
8297 dispatch_source_set_timer( timer, inStart, inIntervalNs, inLeewayNs );
8298 dispatch_set_context( timer, inContext );
8299 dispatch_source_set_event_handler_f( timer, inEventHandler );
8300 dispatch_source_set_cancel_handler_f( timer, inCancelHandler );
8301
8302 *outTimer = timer;
8303 err = kNoErr;
8304
8305 exit:
8306 return( err );
8307 }
8308
8309 //===========================================================================================================================
8310 // ServiceTypeDescription
8311 //===========================================================================================================================
8312
8313 typedef struct
8314 {
8315 const char * name; // Name of the service type in two-label "_service._proto" format.
8316 const char * description; // Description of the service type.
8317
8318 } ServiceType;
8319
8320 // A Non-comprehensive table of DNS-SD service types
8321
8322 static const ServiceType kServiceTypes[] =
8323 {
8324 { "_acp-sync._tcp", "AirPort Base Station Sync" },
8325 { "_adisk._tcp", "Automatic Disk Discovery" },
8326 { "_afpovertcp._tcp", "Apple File Sharing" },
8327 { "_airdrop._tcp", "AirDrop" },
8328 { "_airplay._tcp", "AirPlay" },
8329 { "_airport._tcp", "AirPort Base Station" },
8330 { "_daap._tcp", "Digital Audio Access Protocol (iTunes)" },
8331 { "_eppc._tcp", "Remote AppleEvents" },
8332 { "_ftp._tcp", "File Transfer Protocol" },
8333 { "_home-sharing._tcp", "Home Sharing" },
8334 { "_homekit._tcp", "HomeKit" },
8335 { "_http._tcp", "World Wide Web HTML-over-HTTP" },
8336 { "_https._tcp", "HTTP over SSL/TLS" },
8337 { "_ipp._tcp", "Internet Printing Protocol" },
8338 { "_ldap._tcp", "Lightweight Directory Access Protocol" },
8339 { "_mediaremotetv._tcp", "Media Remote" },
8340 { "_net-assistant._tcp", "Apple Remote Desktop" },
8341 { "_od-master._tcp", "OpenDirectory Master" },
8342 { "_nfs._tcp", "Network File System" },
8343 { "_presence._tcp", "Peer-to-peer messaging / Link-Local Messaging" },
8344 { "_pdl-datastream._tcp", "Printer Page Description Language Data Stream" },
8345 { "_raop._tcp", "Remote Audio Output Protocol" },
8346 { "_rfb._tcp", "Remote Frame Buffer" },
8347 { "_scanner._tcp", "Bonjour Scanning" },
8348 { "_smb._tcp", "Server Message Block over TCP/IP" },
8349 { "_sftp-ssh._tcp", "Secure File Transfer Protocol over SSH" },
8350 { "_sleep-proxy._udp", "Sleep Proxy Server" },
8351 { "_ssh._tcp", "SSH Remote Login Protocol" },
8352 { "_teleport._tcp", "teleport" },
8353 { "_tftp._tcp", "Trivial File Transfer Protocol" },
8354 { "_workstation._tcp", "Workgroup Manager" },
8355 { "_webdav._tcp", "World Wide Web Distributed Authoring and Versioning (WebDAV)" },
8356 { "_webdavs._tcp", "WebDAV over SSL/TLS" }
8357 };
8358
8359 static const char * ServiceTypeDescription( const char *inName )
8360 {
8361 const ServiceType * serviceType;
8362 const ServiceType * const end = kServiceTypes + countof( kServiceTypes );
8363
8364 for( serviceType = kServiceTypes; serviceType < end; ++serviceType )
8365 {
8366 if( strcasecmp( inName, serviceType->name ) == 0 ) return( serviceType->description );
8367 }
8368 return( NULL );
8369 }
8370
8371 //===========================================================================================================================
8372 // SocketContextCancelHandler
8373 //===========================================================================================================================
8374
8375 static void SocketContextCancelHandler( void *inContext )
8376 {
8377 SocketContext * const context = (SocketContext *) inContext;
8378
8379 ForgetSocket( &context->sock );
8380 free( context );
8381 }
8382
8383 //===========================================================================================================================
8384 // StringToInt32
8385 //===========================================================================================================================
8386
8387 static OSStatus StringToInt32( const char *inString, int32_t *outValue )
8388 {
8389 OSStatus err;
8390 long value;
8391 char * endPtr;
8392
8393 value = strtol( inString, &endPtr, 0 );
8394 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
8395 require_action_quiet( ( value >= INT32_MIN ) && ( value <= INT32_MAX ), exit, err = kRangeErr );
8396
8397 *outValue = (int32_t) value;
8398 err = kNoErr;
8399
8400 exit:
8401 return( err );
8402 }
8403
8404 //===========================================================================================================================
8405 // StringToUInt32
8406 //===========================================================================================================================
8407
8408 static OSStatus StringToUInt32( const char *inString, uint32_t *outValue )
8409 {
8410 OSStatus err;
8411 uint32_t value;
8412 char * endPtr;
8413
8414 value = (uint32_t) strtol( inString, &endPtr, 0 );
8415 require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
8416
8417 *outValue = value;
8418 err = kNoErr;
8419
8420 exit:
8421 return( err );
8422 }
8423
8424 #if( TARGET_OS_DARWIN )
8425 //===========================================================================================================================
8426 // GetDefaultDNSServer
8427 //===========================================================================================================================
8428
8429 static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr )
8430 {
8431 OSStatus err;
8432 dns_config_t * config;
8433 struct sockaddr * addr;
8434 int32_t i;
8435
8436 config = dns_configuration_copy();
8437 require_action( config, exit, err = kUnknownErr );
8438
8439 addr = NULL;
8440 for( i = 0; i < config->n_resolver; ++i )
8441 {
8442 const dns_resolver_t * const resolver = config->resolver[ i ];
8443
8444 if( !resolver->domain && ( resolver->n_nameserver > 0 ) )
8445 {
8446 addr = resolver->nameserver[ 0 ];
8447 break;
8448 }
8449 }
8450 require_action_quiet( addr, exit, err = kNotFoundErr );
8451
8452 SockAddrCopy( addr, outAddr );
8453 err = kNoErr;
8454
8455 exit:
8456 if( config ) dns_configuration_free( config );
8457 return( err );
8458 }
8459 #endif
8460
8461 //===========================================================================================================================
8462 // SocketWriteAll
8463 //
8464 // Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
8465 //===========================================================================================================================
8466
8467 OSStatus SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs )
8468 {
8469 OSStatus err;
8470 const uint8_t * src;
8471 const uint8_t * end;
8472 fd_set writeSet;
8473 struct timeval timeout;
8474 ssize_t n;
8475
8476 FD_ZERO( &writeSet );
8477 src = (const uint8_t *) inData;
8478 end = src + inSize;
8479 while( src < end )
8480 {
8481 FD_SET( inSock, &writeSet );
8482 timeout.tv_sec = inTimeoutSecs;
8483 timeout.tv_usec = 0;
8484 n = select( (int)( inSock + 1 ), NULL, &writeSet, NULL, &timeout );
8485 if( n == 0 ) { err = kTimeoutErr; goto exit; }
8486 err = map_socket_value_errno( inSock, n > 0, n );
8487 require_noerr( err, exit );
8488
8489 n = send( inSock, (char *) src, (size_t)( end - src ), 0 );
8490 err = map_socket_value_errno( inSock, n >= 0, n );
8491 if( err == EINTR ) continue;
8492 require_noerr( err, exit );
8493
8494 src += n;
8495 }
8496 err = kNoErr;
8497
8498 exit:
8499 return( err );
8500 }
8501
8502 //===========================================================================================================================
8503 // ParseIPv4Address
8504 //
8505 // Warning: "inBuffer" may be modified even in error cases.
8506 //
8507 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
8508 //===========================================================================================================================
8509
8510 static OSStatus ParseIPv4Address( const char *inStr, uint8_t inBuffer[ 4 ], const char **outStr )
8511 {
8512 OSStatus err;
8513 uint8_t * dst;
8514 int segments;
8515 int sawDigit;
8516 int c;
8517 int v;
8518
8519 check( inBuffer );
8520 check( outStr );
8521
8522 dst = inBuffer;
8523 *dst = 0;
8524 sawDigit = 0;
8525 segments = 0;
8526 for( ; ( c = *inStr ) != '\0'; ++inStr )
8527 {
8528 if( isdigit_safe( c ) )
8529 {
8530 v = ( *dst * 10 ) + ( c - '0' );
8531 require_action_quiet( v <= 255, exit, err = kRangeErr );
8532 *dst = (uint8_t) v;
8533 if( !sawDigit )
8534 {
8535 ++segments;
8536 require_action_quiet( segments <= 4, exit, err = kOverrunErr );
8537 sawDigit = 1;
8538 }
8539 }
8540 else if( ( c == '.' ) && sawDigit )
8541 {
8542 require_action_quiet( segments < 4, exit, err = kMalformedErr );
8543 *++dst = 0;
8544 sawDigit = 0;
8545 }
8546 else
8547 {
8548 break;
8549 }
8550 }
8551 require_action_quiet( segments == 4, exit, err = kUnderrunErr );
8552
8553 *outStr = inStr;
8554 err = kNoErr;
8555
8556 exit:
8557 return( err );
8558 }
8559
8560 //===========================================================================================================================
8561 // StringToIPv4Address
8562 //
8563 // Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
8564 //===========================================================================================================================
8565
8566 OSStatus
8567 StringToIPv4Address(
8568 const char * inStr,
8569 StringToIPAddressFlags inFlags,
8570 uint32_t * outIP,
8571 int * outPort,
8572 uint32_t * outSubnet,
8573 uint32_t * outRouter,
8574 const char ** outStr )
8575 {
8576 OSStatus err;
8577 uint8_t buf[ 4 ];
8578 int c;
8579 uint32_t ip;
8580 int hasPort;
8581 int port;
8582 int hasPrefix;
8583 int prefix;
8584 uint32_t subnetMask;
8585 uint32_t router;
8586
8587 require_action( inStr, exit, err = kParamErr );
8588
8589 // Parse the address-only part of the address (e.g. "1.2.3.4").
8590
8591 err = ParseIPv4Address( inStr, buf, &inStr );
8592 require_noerr_quiet( err, exit );
8593 ip = (uint32_t)( ( buf[ 0 ] << 24 ) | ( buf[ 1 ] << 16 ) | ( buf[ 2 ] << 8 ) | buf[ 3 ] );
8594 c = *inStr;
8595
8596 // Parse the port (if any).
8597
8598 hasPort = 0;
8599 port = 0;
8600 if( c == ':' )
8601 {
8602 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
8603 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
8604 require_action_quiet( port <= 65535, exit, err = kRangeErr );
8605 hasPort = 1;
8606 }
8607
8608 // Parse the prefix length (if any).
8609
8610 hasPrefix = 0;
8611 prefix = 0;
8612 subnetMask = 0;
8613 router = 0;
8614 if( c == '/' )
8615 {
8616 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
8617 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
8618 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 32 ), exit, err = kRangeErr );
8619 hasPrefix = 1;
8620
8621 subnetMask = ( prefix > 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix ) ) : 0;
8622 router = ( ip & subnetMask ) | 1;
8623 }
8624
8625 // Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
8626
8627 if( outIP ) *outIP = ip;
8628 if( outPort && hasPort ) *outPort = port;
8629 if( outSubnet && hasPrefix ) *outSubnet = subnetMask;
8630 if( outRouter && hasPrefix ) *outRouter = router;
8631 if( outStr ) *outStr = inStr;
8632 err = kNoErr;
8633
8634 exit:
8635 return( err );
8636 }
8637
8638 //===========================================================================================================================
8639 // ParseIPv6Address
8640 //
8641 // Note: Parsed according to the rules specified in RFC 3513.
8642 // Warning: "inBuffer" may be modified even in error cases.
8643 //
8644 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
8645 //===========================================================================================================================
8646
8647 static OSStatus ParseIPv6Address( const char *inStr, int inAllowV4Mapped, uint8_t inBuffer[ 16 ], const char **outStr )
8648 {
8649 // Table to map uppercase hex characters - '0' to their numeric values.
8650 // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F
8651 static const uint8_t kASCIItoHexTable[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15 };
8652 OSStatus err;
8653 const char * ptr;
8654 uint8_t * dst;
8655 uint8_t * lim;
8656 uint8_t * colonPtr;
8657 int c;
8658 int sawDigit;
8659 unsigned int v;
8660 int i;
8661 int n;
8662
8663 // Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
8664
8665 for( i = 0; i < 16; ++i ) inBuffer[ i ] = 0;
8666
8667 // Special case leading :: (e.g. "::1") to simplify processing later.
8668
8669 if( *inStr == ':' )
8670 {
8671 ++inStr;
8672 require_action_quiet( *inStr == ':', exit, err = kMalformedErr );
8673 }
8674
8675 // Parse the address.
8676
8677 ptr = inStr;
8678 dst = inBuffer;
8679 lim = dst + 16;
8680 colonPtr = NULL;
8681 sawDigit = 0;
8682 v = 0;
8683 while( ( ( c = *inStr++ ) != '\0' ) && ( c != '%' ) && ( c != '/' ) && ( c != ']' ) )
8684 {
8685 if( ( c >= 'a' ) && ( c <= 'f' ) ) c -= ( 'a' - 'A' );
8686 if( ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c >= 'A' ) && ( c <= 'F' ) ) )
8687 {
8688 c -= '0';
8689 check( c < (int) countof( kASCIItoHexTable ) );
8690 v = ( v << 4 ) | kASCIItoHexTable[ c ];
8691 require_action_quiet( v <= 0xFFFF, exit, err = kRangeErr );
8692 sawDigit = 1;
8693 continue;
8694 }
8695 if( c == ':' )
8696 {
8697 ptr = inStr;
8698 if( !sawDigit )
8699 {
8700 require_action_quiet( !colonPtr, exit, err = kMalformedErr );
8701 colonPtr = dst;
8702 continue;
8703 }
8704 require_action_quiet( *inStr != '\0', exit, err = kUnderrunErr );
8705 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
8706 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
8707 *dst++ = (uint8_t)( v & 0xFF );
8708 sawDigit = 0;
8709 v = 0;
8710 continue;
8711 }
8712
8713 // Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
8714
8715 if( inAllowV4Mapped && ( c == '.' ) && ( ( dst + 4 ) <= lim ) )
8716 {
8717 err = ParseIPv4Address( ptr, dst, &inStr );
8718 require_noerr_quiet( err, exit );
8719 dst += 4;
8720 sawDigit = 0;
8721 ++inStr; // Increment because the code below expects the end to be at "inStr - 1".
8722 }
8723 break;
8724 }
8725 if( sawDigit )
8726 {
8727 require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
8728 *dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
8729 *dst++ = (uint8_t)( v & 0xFF );
8730 }
8731 check( dst <= lim );
8732 if( colonPtr )
8733 {
8734 require_action_quiet( dst < lim, exit, err = kOverrunErr );
8735 n = (int)( dst - colonPtr );
8736 for( i = 1; i <= n; ++i )
8737 {
8738 lim[ -i ] = colonPtr[ n - i ];
8739 colonPtr[ n - i ] = 0;
8740 }
8741 dst = lim;
8742 }
8743 require_action_quiet( dst == lim, exit, err = kUnderrunErr );
8744
8745 *outStr = inStr - 1;
8746 err = kNoErr;
8747
8748 exit:
8749 return( err );
8750 }
8751
8752 //===========================================================================================================================
8753 // ParseIPv6Scope
8754 //
8755 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
8756 //===========================================================================================================================
8757
8758 static OSStatus ParseIPv6Scope( const char *inStr, uint32_t *outScope, const char **outStr )
8759 {
8760 #if( TARGET_OS_POSIX )
8761 OSStatus err;
8762 char scopeStr[ 64 ];
8763 char * dst;
8764 char * lim;
8765 int c;
8766 uint32_t scope;
8767 const char * ptr;
8768
8769 // Copy into a local NULL-terminated string since that is what if_nametoindex expects.
8770
8771 dst = scopeStr;
8772 lim = dst + ( countof( scopeStr ) - 1 );
8773 while( ( ( c = *inStr ) != '\0' ) && ( c != ':' ) && ( c != '/' ) && ( c != ']' ) && ( dst < lim ) )
8774 {
8775 *dst++ = *inStr++;
8776 }
8777 *dst = '\0';
8778 check( dst <= lim );
8779
8780 // First try to map as a name and if that fails, treat it as a numeric scope.
8781
8782 scope = if_nametoindex( scopeStr );
8783 if( scope == 0 )
8784 {
8785 for( ptr = scopeStr; ( ( c = *ptr ) >= '0' ) && ( c <= '9' ); ++ptr )
8786 {
8787 scope = ( scope * 10 ) + ( ( (uint8_t) c ) - '0' );
8788 }
8789 require_action_quiet( c == '\0', exit, err = kMalformedErr );
8790 require_action_quiet( ( ptr != scopeStr ) && ( ( (int)( ptr - scopeStr ) ) <= 10 ), exit, err = kMalformedErr );
8791 }
8792
8793 *outScope = scope;
8794 *outStr = inStr;
8795 err = kNoErr;
8796
8797 exit:
8798 return( err );
8799 #else
8800 OSStatus err;
8801 uint32_t scope;
8802 const char * start;
8803 int c;
8804
8805 scope = 0;
8806 for( start = inStr; ( ( c = *inStr ) >= '0' ) && ( c <= '9' ); ++inStr )
8807 {
8808 scope = ( scope * 10 ) + ( c - '0' );
8809 }
8810 require_action_quiet( ( inStr != start ) && ( ( (int)( inStr - start ) ) <= 10 ), exit, err = kMalformedErr );
8811
8812 *outScope = scope;
8813 *outStr = inStr;
8814 err = kNoErr;
8815
8816 exit:
8817 return( err );
8818 #endif
8819 }
8820
8821 //===========================================================================================================================
8822 // StringToIPv6Address
8823 //
8824 // Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
8825 //===========================================================================================================================
8826
8827 OSStatus
8828 StringToIPv6Address(
8829 const char * inStr,
8830 StringToIPAddressFlags inFlags,
8831 uint8_t outIPv6[ 16 ],
8832 uint32_t * outScope,
8833 int * outPort,
8834 int * outPrefix,
8835 const char ** outStr )
8836 {
8837 OSStatus err;
8838 uint8_t ipv6[ 16 ];
8839 int c;
8840 int hasScope;
8841 uint32_t scope;
8842 int hasPort;
8843 int port;
8844 int hasPrefix;
8845 int prefix;
8846 int hasBracket;
8847 int i;
8848
8849 require_action( inStr, exit, err = kParamErr );
8850
8851 if( *inStr == '[' ) ++inStr; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
8852
8853 // Parse the address-only part of the address (e.g. "1::1").
8854
8855 err = ParseIPv6Address( inStr, !( inFlags & kStringToIPAddressFlagsNoIPv4Mapped ), ipv6, &inStr );
8856 require_noerr_quiet( err, exit );
8857 c = *inStr;
8858
8859 // Parse the scope, port, or prefix length.
8860
8861 hasScope = 0;
8862 scope = 0;
8863 hasPort = 0;
8864 port = 0;
8865 hasPrefix = 0;
8866 prefix = 0;
8867 hasBracket = 0;
8868 for( ;; )
8869 {
8870 if( c == '%' ) // Scope (e.g. "%en0" or "%5")
8871 {
8872 require_action_quiet( !hasScope, exit, err = kMalformedErr );
8873 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoScope ), exit, err = kUnexpectedErr );
8874 ++inStr;
8875 err = ParseIPv6Scope( inStr, &scope, &inStr );
8876 require_noerr_quiet( err, exit );
8877 hasScope = 1;
8878 c = *inStr;
8879 }
8880 else if( c == ':' ) // Port (e.g. ":80")
8881 {
8882 require_action_quiet( !hasPort, exit, err = kMalformedErr );
8883 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
8884 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
8885 require_action_quiet( port <= 65535, exit, err = kRangeErr );
8886 hasPort = 1;
8887 }
8888 else if( c == '/' ) // Prefix Length (e.g. "/64")
8889 {
8890 require_action_quiet( !hasPrefix, exit, err = kMalformedErr );
8891 require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
8892 while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
8893 require_action_quiet( ( prefix >= 0 ) && ( prefix <= 128 ), exit, err = kRangeErr );
8894 hasPrefix = 1;
8895 }
8896 else if( c == ']' )
8897 {
8898 require_action_quiet( !hasBracket, exit, err = kMalformedErr );
8899 hasBracket = 1;
8900 c = *( ++inStr );
8901 }
8902 else
8903 {
8904 break;
8905 }
8906 }
8907
8908 // Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
8909
8910 if( outIPv6 ) for( i = 0; i < 16; ++i ) outIPv6[ i ] = ipv6[ i ];
8911 if( outScope && hasScope ) *outScope = scope;
8912 if( outPort && hasPort ) *outPort = port;
8913 if( outPrefix && hasPrefix ) *outPrefix = prefix;
8914 if( outStr ) *outStr = inStr;
8915 err = kNoErr;
8916
8917 exit:
8918 return( err );
8919 }
8920
8921 //===========================================================================================================================
8922 // StringArray_Append
8923 //
8924 // Note: This was copied from CoreUtils because the StringArray_Append function is currently not exported in the framework.
8925 //===========================================================================================================================
8926
8927 OSStatus StringArray_Append( char ***ioArray, size_t *ioCount, const char *inStr )
8928 {
8929 OSStatus err;
8930 char * newStr;
8931 size_t oldCount;
8932 size_t newCount;
8933 char ** oldArray;
8934 char ** newArray;
8935
8936 newStr = strdup( inStr );
8937 require_action( newStr, exit, err = kNoMemoryErr );
8938
8939 oldCount = *ioCount;
8940 newCount = oldCount + 1;
8941 newArray = (char **) malloc( newCount * sizeof( *newArray ) );
8942 require_action( newArray, exit, err = kNoMemoryErr );
8943
8944 if( oldCount > 0 )
8945 {
8946 oldArray = *ioArray;
8947 memcpy( newArray, oldArray, oldCount * sizeof( *oldArray ) );
8948 free( oldArray );
8949 }
8950 newArray[ oldCount ] = newStr;
8951 newStr = NULL;
8952
8953 *ioArray = newArray;
8954 *ioCount = newCount;
8955 err = kNoErr;
8956
8957 exit:
8958 if( newStr ) free( newStr );
8959 return( err );
8960 }
8961
8962 //===========================================================================================================================
8963 // StringArray_Free
8964 //
8965 // Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
8966 //===========================================================================================================================
8967
8968 void StringArray_Free( char **inArray, size_t inCount )
8969 {
8970 size_t i;
8971
8972 for( i = 0; i < inCount; ++i )
8973 {
8974 free( inArray[ i ] );
8975 }
8976 if( inCount > 0 ) free( inArray );
8977 }
8978
8979 //===========================================================================================================================
8980 // ParseQuotedEscapedString
8981 //
8982 // Note: This was copied from CoreUtils because it's currently not exported in the framework.
8983 //===========================================================================================================================
8984
8985 Boolean
8986 ParseQuotedEscapedString(
8987 const char * inSrc,
8988 const char * inEnd,
8989 const char * inDelimiters,
8990 char * inBuf,
8991 size_t inMaxLen,
8992 size_t * outCopiedLen,
8993 size_t * outTotalLen,
8994 const char ** outSrc )
8995 {
8996 const unsigned char * src;
8997 const unsigned char * end;
8998 unsigned char * dst;
8999 unsigned char * lim;
9000 unsigned char c;
9001 unsigned char c2;
9002 size_t totalLen;
9003 Boolean singleQuote;
9004 Boolean doubleQuote;
9005
9006 if( inEnd == NULL ) inEnd = inSrc + strlen( inSrc );
9007 src = (const unsigned char *) inSrc;
9008 end = (const unsigned char *) inEnd;
9009 dst = (unsigned char *) inBuf;
9010 lim = dst + inMaxLen;
9011 while( ( src < end ) && isspace_safe( *src ) ) ++src; // Skip leading spaces.
9012 if( src >= end ) return( false );
9013
9014 // Parse each argument from the string.
9015 //
9016 // See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
9017
9018 totalLen = 0;
9019 singleQuote = false;
9020 doubleQuote = false;
9021 while( src < end )
9022 {
9023 c = *src++;
9024 if( singleQuote )
9025 {
9026 // Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
9027
9028 if( c == '\'' )
9029 {
9030 singleQuote = false;
9031 continue;
9032 }
9033 }
9034 else if( doubleQuote )
9035 {
9036 // Double quotes protect everything except double quotes and backslashes. A backslash can be
9037 // used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
9038 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
9039 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
9040 // A backslash that does not precede ", \, x, X, or a newline is taken literally.
9041
9042 if( c == '"' )
9043 {
9044 doubleQuote = false;
9045 continue;
9046 }
9047 else if( c == '\\' )
9048 {
9049 if( src < end )
9050 {
9051 c2 = *src;
9052 if( ( c2 == '"' ) || ( c2 == '\\' ) )
9053 {
9054 ++src;
9055 c = c2;
9056 }
9057 else if( c2 == '\n' )
9058 {
9059 ++src;
9060 continue;
9061 }
9062 else if( ( c2 == 'x' ) || ( c2 == 'X' ) )
9063 {
9064 ++src;
9065 c = c2;
9066 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
9067 {
9068 c = HexPairToByte( src );
9069 src += 2;
9070 }
9071 }
9072 else if( isoctal_safe( c2 ) )
9073 {
9074 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
9075 {
9076 c = OctalTripleToByte( src );
9077 src += 3;
9078 }
9079 }
9080 }
9081 }
9082 }
9083 else if( strchr( inDelimiters, c ) )
9084 {
9085 break;
9086 }
9087 else if( c == '\\' )
9088 {
9089 // A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
9090 // A backslash followed by a newline disappears completely.
9091 // A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
9092 // A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
9093
9094 if( src < end )
9095 {
9096 c = *src;
9097 if( c == '\n' )
9098 {
9099 ++src;
9100 continue;
9101 }
9102 else if( ( c == 'x' ) || ( c == 'X' ) )
9103 {
9104 ++src;
9105 if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
9106 {
9107 c = HexPairToByte( src );
9108 src += 2;
9109 }
9110 }
9111 else if( isoctal_safe( c ) )
9112 {
9113 if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
9114 {
9115 c = OctalTripleToByte( src );
9116 src += 3;
9117 }
9118 else
9119 {
9120 ++src;
9121 }
9122 }
9123 else
9124 {
9125 ++src;
9126 }
9127 }
9128 }
9129 else if( c == '\'' )
9130 {
9131 singleQuote = true;
9132 continue;
9133 }
9134 else if( c == '"' )
9135 {
9136 doubleQuote = true;
9137 continue;
9138 }
9139
9140 if( dst < lim )
9141 {
9142 if( inBuf ) *dst = c;
9143 ++dst;
9144 }
9145 ++totalLen;
9146 }
9147
9148 if( outCopiedLen ) *outCopiedLen = (size_t)( dst - ( (unsigned char *) inBuf ) );
9149 if( outTotalLen ) *outTotalLen = totalLen;
9150 if( outSrc ) *outSrc = (const char *) src;
9151 return( true );
9152 }