Imported Upstream version 878.70.2
[platform/upstream/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 }