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