1 /*
2  * Copyright (c) 2016-2020 Apple Inc. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "DNSMessage.h"
18 #include "DNSServerDNSSEC.h"
19 
20 #include <CoreUtils/CoreUtils.h>
21 #include <dns_sd.h>
22 #include <dns_sd_private.h>
23 #include <pcap.h>
24 
25 #include CF_RUNTIME_HEADER
26 
27 #if( TARGET_OS_DARWIN )
28 	#include <CFNetwork/CFHost.h>
29 	#include <CoreFoundation/CoreFoundation.h>
30 	#include <SystemConfiguration/SCPrivate.h>
31 	#include <dnsinfo.h>
32 	#include <libproc.h>
33 	#include <netdb.h>
34 	#include <netinet6/in6_var.h>
35 	#include <netinet6/nd6.h>
36 	#include <spawn.h>
37 	#include <sys/proc_info.h>
38 	#include <xpc/xpc.h>
39 #endif
40 
41 #if( TARGET_OS_POSIX )
42 	#include <sys/resource.h>
43 	#include <spawn.h>
44 #endif
45 
46 #if( !defined( DNSSDUTIL_INCLUDE_DNSCRYPT ) )
47 	#define DNSSDUTIL_INCLUDE_DNSCRYPT		0
48 #endif
49 
50 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
51 	#include "tweetnacl.h"	// TweetNaCl from <https://tweetnacl.cr.yp.to/software.html>.
52 #endif
53 
54 #if( !defined( MDNSRESPONDER_PROJECT ) )
55 	#define MDNSRESPONDER_PROJECT		0
56 #endif
57 
58 #if( MDNSRESPONDER_PROJECT )
59 	#include <CoreFoundation/CFXPCBridge.h>
60 	#include <dns_services.h>
61 	#include "dnssd_private.h"
62 	#include "mdns_private.h"
63 	#include "TestUtils.h"
64 	// Set ENABLE_DNSSDUTIL_DNSSEC_TEST to 1 to enable DNSSEC test functionality.
65 	#define ENABLE_DNSSDUTIL_DNSSEC_TEST 0
66 #endif
67 
68 //===========================================================================================================================
69 //	Versioning
70 //===========================================================================================================================
71 
72 #define kDNSSDUtilNumVersion	NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 )
73 
74 #if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) )
75 	#define DNSSDUTIL_SOURCE_VERSION	"0.0.0"
76 #endif
77 
78 #define kDNSSDUtilIdentifier		"com.apple.dnssdutil"
79 
80 //===========================================================================================================================
81 //	DNS-SD
82 //===========================================================================================================================
83 
84 // DNS-SD API flag descriptors
85 
86 #define kDNSServiceFlagsDescriptors		\
87 	"\x00" "AutoTrigger\0"				\
88 	"\x01" "Add\0"						\
89 	"\x02" "Default\0"					\
90 	"\x03" "NoAutoRename\0"				\
91 	"\x04" "Shared\0"					\
92 	"\x05" "Unique\0"					\
93 	"\x06" "BrowseDomains\0"			\
94 	"\x07" "RegistrationDomains\0"		\
95 	"\x08" "LongLivedQuery\0"			\
96 	"\x09" "AllowRemoteQuery\0"			\
97 	"\x0A" "ForceMulticast\0"			\
98 	"\x0B" "KnownUnique\0"				\
99 	"\x0C" "ReturnIntermediates\0"		\
100 	"\x0D" "DenyConstrained\0"			\
101 	"\x0E" "ShareConnection\0"			\
102 	"\x0F" "SuppressUnusable\0"			\
103 	"\x10" "Timeout\0"					\
104 	"\x11" "IncludeP2P\0"				\
105 	"\x12" "WakeOnResolve\0"			\
106 	"\x13" "BackgroundTrafficClass\0"	\
107 	"\x14" "IncludeAWDL\0"				\
108 	"\x15" "EnableDNSSEC\0"				\
109 	"\x16" "UnicastResponse\0"			\
110 	"\x17" "ValidateOptional\0"			\
111 	"\x18" "WakeOnlyService\0"			\
112 	"\x19" "ThresholdOne\0"				\
113 	"\x1A" "ThresholdFinder\0"			\
114 	"\x1B" "DenyCellular\0"				\
115 	"\x1C" "ServiceIndex\0"				\
116 	"\x1D" "DenyExpensive\0"			\
117 	"\x1E" "PathEvaluationDone\0"		\
118 	"\x1F" "AllowExpiredAnswers\0"		\
119 	"\x00"
120 
121 #define DNSServiceFlagsToAddRmvStr( FLAGS )		( ( (FLAGS) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" )
122 
123 #define kDNSServiceProtocolDescriptors	\
124 	"\x00" "IPv4\0"						\
125 	"\x01" "IPv6\0"						\
126 	"\x04" "UDP\0"						\
127 	"\x05" "TCP\0"						\
128 	"\x00"
129 
130 #define kBadDNSServiceRef		( (DNSServiceRef)(intptr_t) -1 )
131 
132 //===========================================================================================================================
133 //	DNS
134 //===========================================================================================================================
135 
136 #define kDNSPort					53
137 #define kDNSMaxUDPMessageSize		512
138 #define kDNSMaxTCPMessageSize		UINT16_MAX
139 
140 #define kDNSRecordDataLengthMax		UINT16_MAX
141 
142 //===========================================================================================================================
143 //	mDNS
144 //===========================================================================================================================
145 
146 #define kMDNSPort		5353
147 
148 #define kDefaultMDNSMessageID		0
149 #define kDefaultMDNSQueryFlags		0
150 
151 // Recommended Resource Record TTL values. See <https://tools.ietf.org/html/rfc6762#section-10>.
152 
153 #define kMDNSRecordTTL_Host			120		// TTL for resource records related to a host name, e.g., A, AAAA, SRV, etc.
154 #define kMDNSRecordTTL_Other		4500	// TTL for other resource records.
155 
156 // Maximum mDNS Message Size. See <https://tools.ietf.org/html/rfc6762#section-17>.
157 
158 #define kMDNSMessageSizeMax		8952	// 9000 B (Ethernet jumbo frame max size) - 40 B (IPv6 header) - 8 B (UDP header)
159 
160 #define kLocalStr			"\x05" "local"
161 #define kLocalLabel			( (const uint8_t *) kLocalStr )
162 #define kLocalName			( (const uint8_t *) kLocalStr )
163 #define kLocalNameLen		sizeof( kLocalStr )
164 
165 //===========================================================================================================================
166 //	Test Address Blocks
167 //===========================================================================================================================
168 
169 // IPv4 address block 203.0.113.0/24 (TEST-NET-3) is reserved for documentation. See <https://tools.ietf.org/html/rfc5737>.
170 
171 #define kDNSServerBaseAddrV4		UINT32_C( 0xCB007100 )	// 203.0.113.0/24
172 
173 #define kDNSServerReverseIPv4DomainStr		"113.0.203.in-addr.arpa."
174 #define kDNSServerReverseIPv4DomainName \
175 	( (const uint8_t *) "\x3" "113" "\x1" "0" "\x3" "203" "\x7" "in-addr" "\x4" "arpa" )
176 
177 // IPv6 address block 2001:db8::/32 is reserved for documentation. See <https://tools.ietf.org/html/rfc3849>.
178 
179 static const uint8_t		kDNSServerBaseAddrV6[] =
180 {
181 	0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00	// 2001:db8:1::/96
182 };
183 check_compile_time( sizeof( kDNSServerBaseAddrV6 ) == 16 );
184 
185 #define kDNSServerReverseIPv6DomainStr		"0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa."
186 #define kDNSServerReverseIPv6DomainName												\
187 	( (const uint8_t *) "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0"	\
188 	"\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0"	\
189 	"\x1" "0" "\x1" "0" "\x1" "0" "\x1" "0" "\x1" "1" "\x1" "0" "\x1" "0" "\x1" "0"	\
190 	"\x1" "8" "\x1" "b" "\x1" "d" "\x1" "0" "\x1" "1" "\x1" "0" "\x1" "0" "\x1" "2"	\
191 	"\x3" "ip6" "\x4" "arpa" )
192 
193 static const uint8_t		kMDNSReplierBaseAddrV6[] =
194 {
195 	0x20, 0x01, 0x0D, 0xB8, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00	// 2001:db8:2::/96
196 };
197 check_compile_time( sizeof( kMDNSReplierBaseAddrV6 ) == 16 );
198 
199 static const uint8_t		kMDNSReplierLinkLocalBaseAddrV6[] =
200 {
201 	0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00	// fe80::/96
202 };
203 check_compile_time( sizeof( kMDNSReplierLinkLocalBaseAddrV6 ) == 16 );
204 
205 // Bad IPv4 and IPv6 Address Blocks
206 // Used by the DNS server when it needs to respond with intentionally "bad" A/AAAA record data, i.e., IP addresses neither
207 // in 203.0.113.0/24 nor 2001:db8:1::/120.
208 
209 #define kDNSServerBadBaseAddrV4		UINT32_C( 0x00000000 )	// 0.0.0.0/24
210 
211 static const uint8_t		kDNSServerBadBaseAddrV6[] =
212 {
213 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00	// ::ffff:0:0/120
214 };
215 check_compile_time( sizeof( kDNSServerBadBaseAddrV6 ) == 16 );
216 
217 #if( TARGET_OS_DARWIN )
218 // IPv6 Unique Local Address for assigning extra randomly-generated IPv6 addresses to the loopback interface.
219 // 40-bit Global ID: 0xEDF03555E4 (randomly-generated)
220 // 16-bit Subnet ID: 0
221 // See <https://tools.ietf.org/html/rfc4193#section-3.1>.
222 
223 static const uint8_t		kExtraLoopbackIPv6Prefix[] =
224 {
225 	0xFD, 0xED, 0xF0, 0x35, 0x55, 0xE4, 0x00, 0x00	// fded:f035:55e4::/64
226 };
227 
228 #define kExtraLoopbackIPv6PrefixBitLen		64
229 check_compile_time( ( sizeof( kExtraLoopbackIPv6Prefix ) * 8 ) == kExtraLoopbackIPv6PrefixBitLen );
230 #endif
231 
232 //===========================================================================================================================
233 //	DNS Server Domains
234 //===========================================================================================================================
235 
236 #define kDNSServerDomain_Default		( (const uint8_t *) "\x01" "d" "\x04" "test" )
237 #define kDNSServerDomain_DNSSEC			( (const uint8_t *) "\x06" "dnssec" "\x04" "test" )
238 
239 //===========================================================================================================================
240 //	Misc.
241 //===========================================================================================================================
242 
243 #define kLowerAlphaNumericCharSet			"abcdefghijklmnopqrstuvwxyz0123456789"
244 #define kLowerAlphaNumericCharSetSize		sizeof_string( kLowerAlphaNumericCharSet )
245 
246 #if( !defined( kWhiteSpaceCharSet ) )
247 	#define kWhiteSpaceCharSet		"\t\n\v\f\r "
248 #endif
249 
250 #define _RandomStringExact( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, OUT_STRING ) \
251 	RandomString( CHAR_SET, CHAR_SET_SIZE, CHAR_COUNT, CHAR_COUNT, OUT_STRING )
252 
253 #define kNoSuchRecordStr			"No Such Record"
254 #define kNoSuchRecordAStr			"No Such Record (A)"
255 #define kNoSuchRecordAAAAStr		"No Such Record (AAAA)"
256 #define kNoSuchNameStr				"No Such Name"
257 
258 #define kRootLabel		( (const uint8_t *) "" )
259 
260 #if !defined( nw_forget )
261 	#define nw_forget( X )		ForgetCustom( X, nw_release )
262 #endif
263 
264 //===========================================================================================================================
265 //	Gerneral Command Options
266 //===========================================================================================================================
267 
268 // Command option macros
269 
270 #define Command( NAME, CALLBACK, SUB_OPTIONS, SHORT_HELP, IS_NOTCOMMON )											\
271 	CLI_COMMAND_EX( NAME, CALLBACK, SUB_OPTIONS, (IS_NOTCOMMON) ? kCLIOptionFlags_NotCommon : kCLIOptionFlags_None,	\
272 		(SHORT_HELP), NULL )
273 
274 #define kRequiredOptionSuffix		" [REQUIRED]"
275 
276 #define MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP )	\
277 	CLI_OPTION_MULTI_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP,									\
278 		(IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP,														\
279 		(IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
280 
281 #define MultiStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
282 		MultiStringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, VAL_COUNT_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
283 
284 #define IntegerOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP )	\
285 	CLI_OPTION_INTEGER_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP,									\
286 		(IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP,									\
287 		(IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
288 
289 #define IntegerOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED )	\
290 	IntegerOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
291 
292 #define DoubleOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED )	\
293 	CLI_OPTION_DOUBLE_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP,							\
294 		(IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP,						\
295 		(IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
296 
297 #define BooleanOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP ) \
298 	CLI_OPTION_BOOLEAN( (SHORT_CHAR), (LONG_NAME), (VAL_PTR), (SHORT_HELP), NULL )
299 
300 #define StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, LONG_HELP )	\
301 	CLI_OPTION_STRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP,										\
302 		(IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP,									\
303 		(IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, LONG_HELP )
304 
305 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
306 	StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
307 
308 #define CFStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED )	\
309 	CLI_OPTION_CFSTRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP,						\
310 		(IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP,						\
311 		(IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
312 
313 // DNS-SD API flag options
314 
315 static int		gDNSSDFlags						= 0;
316 static int		gDNSSDFlag_AllowExpiredAnswers	= false;
317 static int		gDNSSDFlag_BrowseDomains		= false;
318 static int		gDNSSDFlag_DenyCellular			= false;
319 static int		gDNSSDFlag_DenyConstrained		= false;
320 static int		gDNSSDFlag_DenyExpensive		= false;
321 static int		gDNSSDFlag_ForceMulticast		= false;
322 static int		gDNSSDFlag_IncludeAWDL			= false;
323 static int		gDNSSDFlag_KnownUnique			= false;
324 static int		gDNSSDFlag_NoAutoRename			= false;
325 static int		gDNSSDFlag_PathEvaluationDone	= false;
326 static int		gDNSSDFlag_RegistrationDomains	= false;
327 static int		gDNSSDFlag_ReturnIntermediates	= false;
328 static int		gDNSSDFlag_Shared				= false;
329 static int		gDNSSDFlag_SuppressUnusable		= false;
330 static int		gDNSSDFlag_Timeout				= false;
331 static int		gDNSSDFlag_UnicastResponse		= false;
332 static int		gDNSSDFlag_Unique				= false;
333 static int		gDNSSDFlag_WakeOnResolve		= false;
334 static int		gDNSSDFlag_EnableDNSSEC			= false;
335 
336 #define DNSSDFlagsOption()								\
337 	IntegerOption( 'f', "flags", &gDNSSDFlags, "flags",	\
338 		"DNSServiceFlags as an integer. This value is bitwise ORed with other single flag options.", false )
339 
340 #define DNSSDFlagOption( SHORT_CHAR, FLAG_NAME ) \
341 	BooleanOption( SHORT_CHAR, # FLAG_NAME, &gDNSSDFlag_ ## FLAG_NAME, "Use kDNSServiceFlags" # FLAG_NAME "." )
342 
343 #define DNSSDFlagsOption_AllowExpiredAnswers()		DNSSDFlagOption( 'X', AllowExpiredAnswers )
344 #define DNSSDFlagsOption_DenyCellular()				DNSSDFlagOption( 'C', DenyCellular )
345 #define DNSSDFlagsOption_DenyConstrained()			DNSSDFlagOption( 'R', DenyConstrained)
346 #define DNSSDFlagsOption_DenyExpensive()			DNSSDFlagOption( 'E', DenyExpensive )
347 #define DNSSDFlagsOption_ForceMulticast()			DNSSDFlagOption( 'M', ForceMulticast )
348 #define DNSSDFlagsOption_IncludeAWDL()				DNSSDFlagOption( 'A', IncludeAWDL )
349 #define DNSSDFlagsOption_KnownUnique()				DNSSDFlagOption( 'K', KnownUnique )
350 #define DNSSDFlagsOption_NoAutoRename()				DNSSDFlagOption( 'N', NoAutoRename )
351 #define DNSSDFlagsOption_PathEvalDone()				DNSSDFlagOption( 'P', PathEvaluationDone )
352 #define DNSSDFlagsOption_ReturnIntermediates()		DNSSDFlagOption( 'I', ReturnIntermediates )
353 #define DNSSDFlagsOption_Shared()					DNSSDFlagOption( 'S', Shared )
354 #define DNSSDFlagsOption_SuppressUnusable()			DNSSDFlagOption( 'S', SuppressUnusable )
355 #define DNSSDFlagsOption_Timeout()					DNSSDFlagOption( 'T', Timeout )
356 #define DNSSDFlagsOption_UnicastResponse()			DNSSDFlagOption( 'U', UnicastResponse )
357 #define DNSSDFlagsOption_Unique()					DNSSDFlagOption( 'U', Unique )
358 #define DNSSDFlagsOption_WakeOnResolve()			DNSSDFlagOption( 'W', WakeOnResolve )
359 #define DNSSDFlagsOption_EnableDNSSEC()				DNSSDFlagOption( 'D', EnableDNSSEC )
360 
361 // Interface option
362 
363 static const char *		gInterface = NULL;
364 
365 #define InterfaceOption()										\
366 	StringOption( 'i', "interface", &gInterface, "interface",	\
367 		"Network interface by name or index. Use index -1 for local-only.", false )
368 
369 // Connection options
370 
371 #define kConnectionArg_Normal			""
372 #define kConnectionArgPrefix_PID		"pid:"
373 #define kConnectionArgPrefix_UUID		"uuid:"
374 
375 static const char *		gConnectionOpt = kConnectionArg_Normal;
376 
377 #define ConnectionOptions()																						\
378 	{ kCLIOptionType_String, 0, "connection", &gConnectionOpt, NULL, (intptr_t) kConnectionArg_Normal, "type",	\
379 		kCLIOptionFlags_OptionalArgument, NULL, NULL, NULL, NULL,												\
380 		"Specifies the type of main connection to use. See " kConnectionSection_Name " below.", NULL }
381 
382 #define kConnectionSection_Name		"Connection Option"
383 #define kConnectionSection_Text																							\
384 	"The default behavior is to create a main connection with DNSServiceCreateConnection() and perform operations on\n"	\
385 	"the main connection using the kDNSServiceFlagsShareConnection flag. This behavior can be explicitly invoked by\n"	\
386 	"specifying the connection option without an argument, i.e.,\n"														\
387 	"\n"																												\
388 	"    --connection\n"																								\
389 	"\n"																												\
390 	"To instead use a delegate connection created with DNSServiceCreateDelegateConnection(), use\n"						\
391 	"\n"																												\
392 	"    --connection=pid:<PID>\n"																						\
393 	"\n"																												\
394 	"to specify the delegator by PID, or use\n"																			\
395 	"\n"																												\
396 	"    --connection=uuid:<UUID>\n"																					\
397 	"\n"																												\
398 	"to specify the delegator by UUID.\n"																				\
399 	"\n"																												\
400 	"To not use a main connection at all, but instead perform operations on their own implicit connections, use\n"		\
401 	"\n"																												\
402 	"    --no-connection\n"
403 
404 #define ConnectionSection()		CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
405 
406 // Help text for record data options
407 
408 #define kRDataArgPrefix_Domain			"domain:"
409 #define kRDataArgPrefix_File			"file:"
410 #define kRDataArgPrefix_HexString		"hex:"
411 #define kRDataArgPrefix_IPv4			"ipv4:"
412 #define kRDataArgPrefix_IPv6			"ipv6:"
413 #define kRDataArgPrefix_SRV				"srv:"
414 #define kRDataArgPrefix_String			"string:"
415 #define kRDataArgPrefix_TXT				"txt:"
416 
417 #define kRecordDataSection_Name		"Record Data Arguments"
418 #define kRecordDataSection_Text																							\
419 	"A record data argument is specified in one of the following formats:\n"											\
420 	"\n"																												\
421 	"Format                       Syntax                                   Example\n"									\
422 	"Domain name                  domain:<domain name>                     domain:demo._test._tcp.local\n"				\
423 	"File containing record data  file:<file path>                         file:/path/to/binary-rdata-file\n"			\
424 	"Hexadecimal string           hex:<hex string>                         hex:c0000201 or hex:'C0 00 02 01'\n"		    \
425 	"IPv4 address                 ipv4:<IPv4 address>                      ipv4:192.0.2.1\n"							\
426 	"IPv6 address                 ipv6:<IPv6 address>                      ipv6:2001:db8::1\n"							\
427 	"SRV record                   srv:<priority>,<weight>,<port>,<target>  srv:0,0,64206,example.local\n"				\
428 	"String                       string:<string>                          string:'\\x09color=red'\n"					\
429 	"TXT record strings           txt:<comma-delimited strings>            txt:'vers=1.0,lang=en\\,es\\,fr,passreq'\n"	\
430 	"\n"																												\
431 	"Note: The string format converts each \\xHH escape sequence into the octet represented by the HH hex digit pair.\n"
432 
433 #define RecordDataSection()		CLI_SECTION( kRecordDataSection_Name, kRecordDataSection_Text )
434 
435 // Fallback DNS service option
436 
437 #if( MDNSRESPONDER_PROJECT )
438 static const char *		gFallbackDNSService = NULL;
439 
440 #define kFallbackDNSServiceArgPrefix_DoH		"doh:"
441 #define kFallbackDNSServiceArgPrefix_DoT		"dot:"
442 
443 #define FallbackDNSServiceGroup()		CLI_OPTION_GROUP( "Default Fallback DNS Service" )
444 #define FallbackDNSServiceOption()																						\
445 	StringOptionEx( 0, "fallback", &gFallbackDNSService, "DNS service", "Default fallback DNS service to set.", false,	\
446 		"\n"																											\
447 		"When this option is used, an nw_resolver_config is created for the specified DoH or DoT service.\n"			\
448 		"DNSServiceSetResolverDefaults() is then used to set the DNS service described by nw_resolver_config as the\n"	\
449 		"default fallback DNS service for the dnssdutil process.\n"														\
450 		"\n"																											\
451 		"To specify a DNS over HTTPS (DoH) service, use\n"																\
452 		"\n"																											\
453 		"    --fallback=doh:<URL>\n"																					\
454 		"\n"																											\
455 		"Example: --fallback=doh:https://dns.example.com/dns-query\n"													\
456 		"\n"																											\
457 		"To specify a DNS over TLS (DoT) service, use\n"																\
458 		"\n"																											\
459 		"    --fallback=dot:<hostname>\n"																				\
460 		"\n"																											\
461 		"Example: --fallback=dot:dns.example.com\n"																		\
462 	)
463 #endif
464 
465 //===========================================================================================================================
466 //	Output Formatting
467 //===========================================================================================================================
468 
469 #define kOutputFormatStr_JSON		"json"
470 #define kOutputFormatStr_XML		"xml"
471 #define kOutputFormatStr_Binary		"binary"
472 
473 typedef enum
474 {
475 	kOutputFormatType_Invalid	= 0,
476 	kOutputFormatType_JSON		= 1,
477 	kOutputFormatType_XML		= 2,
478 	kOutputFormatType_Binary	= 3
479 
480 }	OutputFormatType;
481 
482 #define FormatOption( SHORT_CHAR, LONG_NAME, VAL_PTR, SHORT_HELP, IS_REQUIRED )			\
483 	StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, "format", SHORT_HELP, IS_REQUIRED,	\
484 		"\n"																			\
485 		"Use '" kOutputFormatStr_JSON   "' for JavaScript Object Notation (JSON).\n"	\
486 		"Use '" kOutputFormatStr_XML    "' for property list XML version 1.0.\n"		\
487 		"Use '" kOutputFormatStr_Binary "' for property list binary version 1.0.\n"		\
488 		"\n"																			\
489 	)
490 
491 //===========================================================================================================================
492 //	Browse Command Options
493 //===========================================================================================================================
494 
495 static char **			gBrowse_ServiceTypes		= NULL;
496 static size_t			gBrowse_ServiceTypesCount	= 0;
497 static const char *		gBrowse_Domain				= NULL;
498 static int				gBrowse_DoResolve			= false;
499 static int				gBrowse_QueryTXT			= false;
500 static int				gBrowse_TimeLimitSecs		= 0;
501 
502 static CLIOption		kBrowseOpts[] =
503 {
504 	InterfaceOption(),
505 	MultiStringOption(	't', "type",	&gBrowse_ServiceTypes, &gBrowse_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\".", true ),
506 	StringOption(		'd', "domain",	&gBrowse_Domain, "domain", "Domain in which to browse for the service type(s).", false ),
507 
508 	CLI_OPTION_GROUP( "Flags" ),
509 	DNSSDFlagsOption(),
510 	DNSSDFlagsOption_IncludeAWDL(),
511 
512 	CLI_OPTION_GROUP( "Operation" ),
513 	ConnectionOptions(),
514 	BooleanOption(  0 , "resolve",		&gBrowse_DoResolve,		"Resolve service instances." ),
515 	BooleanOption(  0 , "queryTXT",		&gBrowse_QueryTXT,		"Query TXT records of service instances." ),
516 	IntegerOption( 'l', "timeLimit",	&gBrowse_TimeLimitSecs,	"seconds", "Specifies the max duration of the browse operation. Use '0' for no time limit.", false ),
517 
518 	ConnectionSection(),
519 	CLI_OPTION_END()
520 };
521 
522 //===========================================================================================================================
523 //	GetAddrInfo Command Options
524 //===========================================================================================================================
525 
526 static const char *		gGetAddrInfo_Name			= NULL;
527 static int				gGetAddrInfo_ProtocolIPv4	= false;
528 static int				gGetAddrInfo_ProtocolIPv6	= false;
529 static int				gGetAddrInfo_OneShot		= false;
530 static int				gGetAddrInfo_TimeLimitSecs	= 0;
531 
532 static CLIOption		kGetAddrInfoOpts[] =
533 {
534 	InterfaceOption(),
535 	StringOption(  'n', "name", &gGetAddrInfo_Name,			"domain name", "Domain name to resolve.", true ),
536 	BooleanOption(  0 , "ipv4", &gGetAddrInfo_ProtocolIPv4,	"Use kDNSServiceProtocol_IPv4." ),
537 	BooleanOption(  0 , "ipv6", &gGetAddrInfo_ProtocolIPv6,	"Use kDNSServiceProtocol_IPv6." ),
538 
539 	CLI_OPTION_GROUP( "Flags" ),
540 	DNSSDFlagsOption(),
541 	DNSSDFlagsOption_AllowExpiredAnswers(),
542 	DNSSDFlagsOption_DenyCellular(),
543 	DNSSDFlagsOption_DenyConstrained(),
544 	DNSSDFlagsOption_DenyExpensive(),
545 	DNSSDFlagsOption_IncludeAWDL(),
546 	DNSSDFlagsOption_PathEvalDone(),
547 	DNSSDFlagsOption_ReturnIntermediates(),
548 	DNSSDFlagsOption_SuppressUnusable(),
549 	DNSSDFlagsOption_Timeout(),
550 
551 	CLI_OPTION_GROUP( "Operation" ),
552 	ConnectionOptions(),
553 	BooleanOption( 'o', "oneshot",		&gGetAddrInfo_OneShot,			"Finish after first set of results." ),
554 	IntegerOption( 'l', "timeLimit",	&gGetAddrInfo_TimeLimitSecs,	"seconds", "Maximum duration of the GetAddrInfo operation. Use '0' for no time limit.", false ),
555 
556 #if( MDNSRESPONDER_PROJECT )
557 	FallbackDNSServiceGroup(),
558 	FallbackDNSServiceOption(),
559 #endif
560 	ConnectionSection(),
561 	CLI_OPTION_END()
562 };
563 
564 //===========================================================================================================================
565 //	QueryRecord Command Options
566 //===========================================================================================================================
567 
568 static const char *		gQueryRecord_Name			= NULL;
569 static const char *		gQueryRecord_Type			= NULL;
570 static int				gQueryRecord_OneShot		= false;
571 static int				gQueryRecord_TimeLimitSecs	= 0;
572 static int				gQueryRecord_RawRData		= false;
573 
574 static CLIOption		kQueryRecordOpts[] =
575 {
576 	InterfaceOption(),
577 	StringOption( 'n', "name", &gQueryRecord_Name, "domain name", "Full domain name of record to query.", true ),
578 	StringOption( 't', "type", &gQueryRecord_Type, "record type", "Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
579 
580 	CLI_OPTION_GROUP( "Flags" ),
581 	DNSSDFlagsOption(),
582 	DNSSDFlagsOption_AllowExpiredAnswers(),
583 	DNSSDFlagsOption_DenyCellular(),
584 	DNSSDFlagsOption_DenyConstrained(),
585 	DNSSDFlagsOption_DenyExpensive(),
586 	DNSSDFlagsOption_ForceMulticast(),
587 	DNSSDFlagsOption_IncludeAWDL(),
588 	DNSSDFlagsOption_PathEvalDone(),
589 	DNSSDFlagsOption_ReturnIntermediates(),
590 	DNSSDFlagsOption_SuppressUnusable(),
591 	DNSSDFlagsOption_Timeout(),
592 	DNSSDFlagsOption_UnicastResponse(),
593 	DNSSDFlagsOption_EnableDNSSEC(),
594 
595 	CLI_OPTION_GROUP( "Operation" ),
596 	ConnectionOptions(),
597 	BooleanOption( 'o', "oneshot",		&gQueryRecord_OneShot,			"Finish after first set of results." ),
598 	IntegerOption( 'l', "timeLimit",	&gQueryRecord_TimeLimitSecs,	"seconds", "Maximum duration of the query record operation. Use '0' for no time limit.", false ),
599 	BooleanOption(  0 , "raw",			&gQueryRecord_RawRData,			"Show record data as a hexdump." ),
600 
601 #if( MDNSRESPONDER_PROJECT )
602 	FallbackDNSServiceGroup(),
603 	FallbackDNSServiceOption(),
604 #endif
605 	ConnectionSection(),
606 	CLI_OPTION_END()
607 };
608 
609 //===========================================================================================================================
610 //	Register Command Options
611 //===========================================================================================================================
612 
613 static const char *			gRegister_Name			= NULL;
614 static const char *			gRegister_Type			= NULL;
615 static const char *			gRegister_Domain		= NULL;
616 static int					gRegister_Port			= 0;
617 static const char *			gRegister_TXT			= NULL;
618 static int					gRegister_LifetimeMs	= -1;
619 static const char **		gAddRecord_Types		= NULL;
620 static size_t				gAddRecord_TypesCount	= 0;
621 static const char **		gAddRecord_Data			= NULL;
622 static size_t				gAddRecord_DataCount	= 0;
623 static const char **		gAddRecord_TTLs			= NULL;
624 static size_t				gAddRecord_TTLsCount	= 0;
625 static const char *			gUpdateRecord_Data		= NULL;
626 static int					gUpdateRecord_DelayMs	= 0;
627 static int					gUpdateRecord_TTL		= 0;
628 
629 static CLIOption		kRegisterOpts[] =
630 {
631 	InterfaceOption(),
632 	StringOption(  'n', "name",		&gRegister_Name,	"service name",	"Name of service.", false ),
633 	StringOption(  't', "type",		&gRegister_Type,	"service type",	"Service type, e.g., \"_ssh._tcp\".", true ),
634 	StringOption(  'd', "domain",	&gRegister_Domain,	"domain",		"Domain in which to advertise the service.", false ),
635 	IntegerOption( 'p', "port",		&gRegister_Port,	"port number",	"Service's port number.", true ),
636 	StringOption(   0 , "txt",		&gRegister_TXT,		"record data",	"The TXT record data. See " kRecordDataSection_Name " below.", false ),
637 
638 	CLI_OPTION_GROUP( "Flags" ),
639 	DNSSDFlagsOption(),
640 	DNSSDFlagsOption_IncludeAWDL(),
641 	DNSSDFlagsOption_KnownUnique(),
642 	DNSSDFlagsOption_NoAutoRename(),
643 
644 	CLI_OPTION_GROUP( "Operation" ),
645 	IntegerOption( 'l', "lifetime", &gRegister_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
646 
647 	CLI_OPTION_GROUP( "Options for updating the registered service's primary TXT record with DNSServiceUpdateRecord()\n" ),
648 	StringOption(  0 , "updateData",	&gUpdateRecord_Data,	"record data",	"Record data for the record update. See " kRecordDataSection_Name " below.", false ),
649 	IntegerOption( 0 , "updateDelay",	&gUpdateRecord_DelayMs,	"ms",			"Number of milliseconds after registration to wait before record update.", false ),
650 	IntegerOption( 0 , "updateTTL",		&gUpdateRecord_TTL,		"seconds",		"Time-to-live of the updated record.", false ),
651 
652 	CLI_OPTION_GROUP( "Options for adding extra record(s) to the registered service with DNSServiceAddRecord()\n" ),
653 	MultiStringOption(   0 , "addType",	&gAddRecord_Types,	&gAddRecord_TypesCount,	"record type",	"Type of additional record by name (e.g., TXT, SRV, etc.) or number.", false ),
654 	MultiStringOptionEx( 0 , "addData",	&gAddRecord_Data,	&gAddRecord_DataCount,	"record data",	"Additional record's data. See " kRecordDataSection_Name " below.", false, NULL ),
655 	MultiStringOption(   0 , "addTTL",	&gAddRecord_TTLs,	&gAddRecord_TTLsCount,	"seconds",		"Time-to-live of additional record in seconds. Use '0' for default.", false ),
656 
657 	RecordDataSection(),
658 	CLI_OPTION_END()
659 };
660 
661 //===========================================================================================================================
662 //	RegisterRecord Command Options
663 //===========================================================================================================================
664 
665 static const char *		gRegisterRecord_Name			= NULL;
666 static const char *		gRegisterRecord_Type			= NULL;
667 static const char *		gRegisterRecord_Data			= NULL;
668 static int				gRegisterRecord_TTL				= 0;
669 static int				gRegisterRecord_LifetimeMs		= -1;
670 static const char *		gRegisterRecord_UpdateData		= NULL;
671 static int				gRegisterRecord_UpdateDelayMs	= 0;
672 static int				gRegisterRecord_UpdateTTL		= 0;
673 
674 static CLIOption		kRegisterRecordOpts[] =
675 {
676 	InterfaceOption(),
677 	StringOption( 'n', "name",	&gRegisterRecord_Name,	"record name",	"Fully qualified domain name of record.", true ),
678 	StringOption( 't', "type",	&gRegisterRecord_Type,	"record type",	"Record type by name (e.g., TXT, PTR, A) or number.", true ),
679 	StringOption( 'd', "data",	&gRegisterRecord_Data,	"record data",	"The record data. See " kRecordDataSection_Name " below.", false ),
680 	IntegerOption( 0 , "ttl",	&gRegisterRecord_TTL,	"seconds",		"Time-to-live in seconds. Use '0' for default.", false ),
681 
682 	CLI_OPTION_GROUP( "Flags" ),
683 	DNSSDFlagsOption(),
684 	DNSSDFlagsOption_IncludeAWDL(),
685 	DNSSDFlagsOption_KnownUnique(),
686 	DNSSDFlagsOption_Shared(),
687 	DNSSDFlagsOption_Unique(),
688 
689 	CLI_OPTION_GROUP( "Operation" ),
690 	IntegerOption( 'l', "lifetime", &gRegisterRecord_LifetimeMs, "ms", "Lifetime of the service registration in milliseconds.", false ),
691 
692 	CLI_OPTION_GROUP( "Options for updating the registered record with DNSServiceUpdateRecord()\n" ),
693 	StringOption(  0 , "updateData",	&gRegisterRecord_UpdateData,	"record data",	"Record data for the record update.", false ),
694 	IntegerOption( 0 , "updateDelay",	&gRegisterRecord_UpdateDelayMs,	"ms",			"Number of milliseconds after registration to wait before record update.", false ),
695 	IntegerOption( 0 , "updateTTL",		&gRegisterRecord_UpdateTTL,		"seconds",		"Time-to-live of the updated record.", false ),
696 
697 	RecordDataSection(),
698 	CLI_OPTION_END()
699 };
700 
701 //===========================================================================================================================
702 //	Resolve Command Options
703 //===========================================================================================================================
704 
705 static char *		gResolve_Name			= NULL;
706 static char *		gResolve_Type			= NULL;
707 static char *		gResolve_Domain			= NULL;
708 static int			gResolve_TimeLimitSecs	= 0;
709 
710 static CLIOption		kResolveOpts[] =
711 {
712 	InterfaceOption(),
713 	StringOption( 'n', "name",		&gResolve_Name,		"service name", "Name of the service instance to resolve.", true ),
714 	StringOption( 't', "type",		&gResolve_Type,		"service type", "Type of the service instance to resolve.", true ),
715 	StringOption( 'd', "domain",	&gResolve_Domain,	"domain", "Domain of the service instance to resolve.", true ),
716 
717 	CLI_OPTION_GROUP( "Flags" ),
718 	DNSSDFlagsOption(),
719 	DNSSDFlagsOption_ForceMulticast(),
720 	DNSSDFlagsOption_IncludeAWDL(),
721 	DNSSDFlagsOption_ReturnIntermediates(),
722 	DNSSDFlagsOption_WakeOnResolve(),
723 
724 	CLI_OPTION_GROUP( "Operation" ),
725 	ConnectionOptions(),
726 	IntegerOption( 'l', "timeLimit", &gResolve_TimeLimitSecs, "seconds", "Maximum duration of the resolve operation. Use '0' for no time limit.", false ),
727 
728 	ConnectionSection(),
729 	CLI_OPTION_END()
730 };
731 
732 //===========================================================================================================================
733 //	Reconfirm Command Options
734 //===========================================================================================================================
735 
736 static const char *		gReconfirmRecord_Name	= NULL;
737 static const char *		gReconfirmRecord_Type	= NULL;
738 static const char *		gReconfirmRecord_Class	= NULL;
739 static const char *		gReconfirmRecord_Data	= NULL;
740 
741 static CLIOption		kReconfirmOpts[] =
742 {
743 	InterfaceOption(),
744 	StringOption( 'n', "name",	&gReconfirmRecord_Name,		"record name",	"Full name of the record to reconfirm.", true ),
745 	StringOption( 't', "type",	&gReconfirmRecord_Type,		"record type",	"Type of the record to reconfirm.", true ),
746 	StringOption( 'c', "class",	&gReconfirmRecord_Class,	"record class",	"Class of the record to reconfirm. Default class is IN.", false ),
747 	StringOption( 'd', "data",	&gReconfirmRecord_Data,		"record data",	"The record data. See " kRecordDataSection_Name " below.", false ),
748 
749 	CLI_OPTION_GROUP( "Flags" ),
750 	DNSSDFlagsOption(),
751 
752 	RecordDataSection(),
753 	CLI_OPTION_END()
754 };
755 
756 //===========================================================================================================================
757 //	getaddrinfo-POSIX Command Options
758 //===========================================================================================================================
759 
760 static const char *		gGAIPOSIX_HostName			= NULL;
761 static const char *		gGAIPOSIX_ServName			= NULL;
762 static const char *		gGAIPOSIX_Family			= NULL;
763 static int				gGAIPOSIXFlag_AddrConfig	= false;
764 static int				gGAIPOSIXFlag_All			= false;
765 static int				gGAIPOSIXFlag_CanonName		= false;
766 static int				gGAIPOSIXFlag_NumericHost	= false;
767 static int				gGAIPOSIXFlag_NumericServ	= false;
768 static int				gGAIPOSIXFlag_Passive		= false;
769 static int				gGAIPOSIXFlag_V4Mapped		= false;
770 #if( defined( AI_V4MAPPED_CFG ) )
771 static int				gGAIPOSIXFlag_V4MappedCFG	= false;
772 #endif
773 #if( defined( AI_DEFAULT ) )
774 static int				gGAIPOSIXFlag_Default		= false;
775 #endif
776 #if( defined( AI_UNUSABLE ) )
777 static int				gGAIPOSIXFlag_Unusable		= false;
778 #endif
779 
780 static CLIOption		kGetAddrInfoPOSIXOpts[] =
781 {
782 	StringOption(	'n', "hostname",			&gGAIPOSIX_HostName,		"hostname", "Domain name to resolve or an IPv4 or IPv6 address.", true ),
783 	StringOption(	's', "servname",			&gGAIPOSIX_ServName,		"servname", "Port number in decimal or service name from services(5).", false ),
784 
785 	CLI_OPTION_GROUP( "Hints" ),
786 	StringOptionEx(	'f', "family",				&gGAIPOSIX_Family,			"address family", "Address family to use for hints ai_family field.", false,
787 		"\n"
788 		"Possible address family values are 'inet' for AF_INET, 'inet6' for AF_INET6, or 'unspec' for AF_UNSPEC. If no\n"
789 		"address family is specified, then AF_UNSPEC is used.\n"
790 		"\n" ),
791 	BooleanOption(   0 , "flag-addrconfig",		&gGAIPOSIXFlag_AddrConfig,	"In hints ai_flags field, set AI_ADDRCONFIG." ),
792 	BooleanOption(   0 , "flag-all",			&gGAIPOSIXFlag_All,			"In hints ai_flags field, set AI_ALL." ),
793 	BooleanOption(   0 , "flag-canonname",		&gGAIPOSIXFlag_CanonName,	"In hints ai_flags field, set AI_CANONNAME." ),
794 	BooleanOption(   0 , "flag-numerichost",	&gGAIPOSIXFlag_NumericHost,	"In hints ai_flags field, set AI_NUMERICHOST." ),
795 	BooleanOption(   0 , "flag-numericserv",	&gGAIPOSIXFlag_NumericServ,	"In hints ai_flags field, set AI_NUMERICSERV." ),
796 	BooleanOption(   0 , "flag-passive",		&gGAIPOSIXFlag_Passive,		"In hints ai_flags field, set AI_PASSIVE." ),
797 	BooleanOption(   0 , "flag-v4mapped",		&gGAIPOSIXFlag_V4Mapped,	"In hints ai_flags field, set AI_V4MAPPED." ),
798 #if( defined( AI_V4MAPPED_CFG ) )
799 	BooleanOption(   0 , "flag-v4mappedcfg",	&gGAIPOSIXFlag_V4MappedCFG,	"In hints ai_flags field, set AI_V4MAPPED_CFG." ),
800 #endif
801 #if( defined( AI_DEFAULT ) )
802 	BooleanOption(   0 , "flag-default",		&gGAIPOSIXFlag_Default,		"In hints ai_flags field, set AI_DEFAULT." ),
803 #endif
804 #if( defined( AI_UNUSABLE ) )
805 	BooleanOption(   0 , "flag-unusable",		&gGAIPOSIXFlag_Unusable,	"In hints ai_flags field, set AI_UNUSABLE." ),
806 #endif
807 
808 #if( MDNSRESPONDER_PROJECT )
809 	FallbackDNSServiceGroup(),
810 	FallbackDNSServiceOption(),
811 #endif
812 	CLI_SECTION( "Notes", "See getaddrinfo(3) man page for more details.\n" ),
813 	CLI_OPTION_END()
814 };
815 
816 //===========================================================================================================================
817 //	ReverseLookup Command Options
818 //===========================================================================================================================
819 
820 static const char *		gReverseLookup_IPAddr			= NULL;
821 static int				gReverseLookup_OneShot			= false;
822 static int				gReverseLookup_TimeLimitSecs	= 0;
823 
824 static CLIOption		kReverseLookupOpts[] =
825 {
826 	InterfaceOption(),
827 	StringOption( 'a', "address", &gReverseLookup_IPAddr, "IP address", "IPv4 or IPv6 address for which to perform a reverse IP lookup.", true ),
828 
829 	CLI_OPTION_GROUP( "Flags" ),
830 	DNSSDFlagsOption(),
831 	DNSSDFlagsOption_ForceMulticast(),
832 	DNSSDFlagsOption_ReturnIntermediates(),
833 	DNSSDFlagsOption_SuppressUnusable(),
834 
835 	CLI_OPTION_GROUP( "Operation" ),
836 	ConnectionOptions(),
837 	BooleanOption( 'o', "oneshot",		&gReverseLookup_OneShot,		"Finish after first set of results." ),
838 	IntegerOption( 'l', "timeLimit",	&gReverseLookup_TimeLimitSecs,	"seconds", "Specifies the max duration of the query record operation. Use '0' for no time limit.", false ),
839 
840 	ConnectionSection(),
841 	CLI_OPTION_END()
842 };
843 
844 //===========================================================================================================================
845 //	PortMapping Command Options
846 //===========================================================================================================================
847 
848 static int		gPortMapping_ProtocolTCP	= false;
849 static int		gPortMapping_ProtocolUDP	= false;
850 static int		gPortMapping_InternalPort	= 0;
851 static int		gPortMapping_ExternalPort	= 0;
852 static int		gPortMapping_TTL			= 0;
853 
854 static CLIOption		kPortMappingOpts[] =
855 {
856 	InterfaceOption(),
857 	BooleanOption( 0, "tcp",			&gPortMapping_ProtocolTCP,	"Use kDNSServiceProtocol_TCP." ),
858 	BooleanOption( 0, "udp",			&gPortMapping_ProtocolUDP,	"Use kDNSServiceProtocol_UDP." ),
859 	IntegerOption( 0, "internalPort",	&gPortMapping_InternalPort,	"port number", "Internal port.", false ),
860 	IntegerOption( 0, "externalPort",	&gPortMapping_ExternalPort,	"port number", "Requested external port. Use '0' for any external port.", false ),
861 	IntegerOption( 0, "ttl",			&gPortMapping_TTL,			"seconds", "Requested TTL (renewal period) in seconds. Use '0' for a default value.", false ),
862 
863 	CLI_OPTION_GROUP( "Flags" ),
864 	DNSSDFlagsOption(),
865 
866 	CLI_OPTION_GROUP( "Operation" ),
867 	ConnectionOptions(),
868 
869 	ConnectionSection(),
870 	CLI_OPTION_END()
871 };
872 
873 #if( TARGET_OS_DARWIN )
874 //===========================================================================================================================
875 //	RegisterKA Command Options
876 //===========================================================================================================================
877 
878 static const char *		gRegisterKA_LocalAddress	= NULL;
879 static const char *		gRegisterKA_RemoteAddress	= NULL;
880 static int				gRegisterKA_Timeout			= 0;
881 
882 static CLIOption		kRegisterKA_Opts[] =
883 {
884 	DNSSDFlagsOption(),
885 	StringOption(  'l', "local",   &gRegisterKA_LocalAddress,  "IP addr+port", "TCP connection's local IPv4 or IPv6 address and port pair.", true ),
886 	StringOption(  'r', "remote",  &gRegisterKA_RemoteAddress, "IP addr+port", "TCP connection's remote IPv4 or IPv6 address and port pair.", true ),
887 	IntegerOption( 't', "timeout", &gRegisterKA_Timeout,       "timeout", "Keepalive record's timeout value, i.e., its 't=' value.", false ),
888 	CLI_OPTION_END()
889 };
890 
891 static void	RegisterKACmd( void );
892 #endif
893 
894 //===========================================================================================================================
895 //	BrowseAll Command Options
896 //===========================================================================================================================
897 
898 static const char *		gBrowseAll_Domain				= NULL;
899 static const char **	gBrowseAll_ServiceTypes			= NULL;
900 static size_t			gBrowseAll_ServiceTypesCount	= 0;
901 static int				gBrowseAll_BrowseTimeSecs		= 5;
902 static int				gBrowseAll_ConnectTimeout		= 0;
903 
904 static CLIOption		kBrowseAllOpts[] =
905 {
906 	InterfaceOption(),
907 	StringOption(	   'd', "domain", &gBrowseAll_Domain, "domain", "Domain in which to browse for the service.", false ),
908 	MultiStringOption( 't', "type",   &gBrowseAll_ServiceTypes, &gBrowseAll_ServiceTypesCount, "service type", "Service type(s), e.g., \"_ssh._tcp\". All services are browsed for if none is specified.", false ),
909 
910 	CLI_OPTION_GROUP( "Flags" ),
911 	DNSSDFlagsOption_IncludeAWDL(),
912 
913 	CLI_OPTION_GROUP( "Operation" ),
914 	IntegerOption( 'b', "browseTime",     &gBrowseAll_BrowseTimeSecs, "seconds", "Amount of time to spend browsing in seconds. (default: 5)", false ),
915 	IntegerOption( 'c', "connectTimeout", &gBrowseAll_ConnectTimeout, "seconds", "Timeout for connection attempts. If <= 0, no connections are attempted. (default: 0)", false ),
916 	CLI_OPTION_END()
917 };
918 
919 //===========================================================================================================================
920 //	GetNameInfo Command Options
921 //===========================================================================================================================
922 
923 static void	GetNameInfoCmd( void );
924 
925 static char *		gGetNameInfo_IPAddress			= NULL;
926 static int			gGetNameInfoFlag_DGram			= false;
927 static int			gGetNameInfoFlag_NameReqd		= false;
928 static int			gGetNameInfoFlag_NoFQDN			= false;
929 static int			gGetNameInfoFlag_NumericHost	= false;
930 static int			gGetNameInfoFlag_NumericScope	= false;
931 static int			gGetNameInfoFlag_NumericServ	= false;
932 
933 static CLIOption		kGetNameInfoOpts[] =
934 {
935 	StringOption( 'a', "address",           &gGetNameInfo_IPAddress,        "IP address", "IPv4 or IPv6 address to use in sockaddr structure.", true ),
936 
937 	CLI_OPTION_GROUP( "Flags" ),
938 	BooleanOption( 0 , "flag-dgram",        &gGetNameInfoFlag_DGram,        "Use NI_DGRAM flag." ),
939 	BooleanOption( 0 , "flag-namereqd",     &gGetNameInfoFlag_NameReqd,     "Use NI_NAMEREQD flag." ),
940 	BooleanOption( 0 , "flag-nofqdn",       &gGetNameInfoFlag_NoFQDN,       "Use NI_NOFQDN flag." ),
941 	BooleanOption( 0 , "flag-numerichost",  &gGetNameInfoFlag_NumericHost,  "Use NI_NUMERICHOST flag." ),
942 	BooleanOption( 0 , "flag-numericscope", &gGetNameInfoFlag_NumericScope, "Use NI_NUMERICSCOPE flag." ),
943 	BooleanOption( 0 , "flag-numericserv",  &gGetNameInfoFlag_NumericServ,  "Use NI_NUMERICSERV flag." ),
944 
945 	CLI_SECTION( "Notes", "See getnameinfo(3) man page for more details.\n" ),
946 	CLI_OPTION_END()
947 };
948 
949 //===========================================================================================================================
950 //	GetAddrInfoStress Command Options
951 //===========================================================================================================================
952 
953 static int		gGAIStress_TestDurationSecs	= 0;
954 static int		gGAIStress_ConnectionCount	= 0;
955 static int		gGAIStress_DurationMinMs	= 0;
956 static int		gGAIStress_DurationMaxMs	= 0;
957 static int		gGAIStress_RequestCountMax	= 0;
958 
959 static CLIOption		kGetAddrInfoStressOpts[] =
960 {
961 	InterfaceOption(),
962 
963 	CLI_OPTION_GROUP( "Flags" ),
964 	DNSSDFlagsOption_ReturnIntermediates(),
965 	DNSSDFlagsOption_SuppressUnusable(),
966 
967 	CLI_OPTION_GROUP( "Operation" ),
968 	IntegerOption( 0, "testDuration",			&gGAIStress_TestDurationSecs,	"seconds",	"Stress test duration in seconds. Use '0' for forever.", false ),
969 	IntegerOption( 0, "connectionCount",		&gGAIStress_ConnectionCount,	"integer",	"Number of simultaneous DNS-SD connections.", true ),
970 	IntegerOption( 0, "requestDurationMin",		&gGAIStress_DurationMinMs,		"ms",		"Minimum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
971 	IntegerOption( 0, "requestDurationMax",		&gGAIStress_DurationMaxMs,		"ms",		"Maximum duration of DNSServiceGetAddrInfo() request in milliseconds.", true ),
972 	IntegerOption( 0, "consecutiveRequestMax",	&gGAIStress_RequestCountMax,	"integer",	"Maximum number of requests on a connection before restarting it.", true ),
973 	CLI_OPTION_END()
974 };
975 
976 //===========================================================================================================================
977 //	DNSQuery Command Options
978 //===========================================================================================================================
979 
980 static char *		gDNSQuery_Name				= NULL;
981 static char *		gDNSQuery_Type				= "A";
982 static char *		gDNSQuery_Server			= NULL;
983 static int			gDNSQuery_TimeLimitSecs		= 5;
984 static int			gDNSQuery_UseTCP			= false;
985 static int			gDNSQuery_Flags				= kDNSHeaderFlag_RecursionDesired;
986 static int			gDNSQuery_DNSSEC			= false;
987 static int			gDNSQuery_CheckingDisabled	= false;
988 static int			gDNSQuery_RawRData			= false;
989 static int			gDNSQuery_Verbose			= false;
990 
991 #if( TARGET_OS_DARWIN )
992 	#define kDNSQueryServerOptionIsRequired		false
993 #else
994 	#define kDNSQueryServerOptionIsRequired		true
995 #endif
996 
997 static CLIOption		kDNSQueryOpts[] =
998 {
999 	StringOption(  'n', "name",             &gDNSQuery_Name,             "name", "Question name (QNAME) to put in DNS query message.", true ),
1000 	StringOption(  't', "type",             &gDNSQuery_Type,             "type", "Question type (QTYPE) to put in DNS query message. (default: A)", false ),
1001 	StringOption(  's', "server",           &gDNSQuery_Server,           "IP address", "DNS server's IPv4 or IPv6 address.", kDNSQueryServerOptionIsRequired ),
1002 	IntegerOption( 'l', "timeLimit",        &gDNSQuery_TimeLimitSecs,    "seconds", "Specifies query time limit. Use '-1' for no limit and '0' to exit immediately after sending.", false ),
1003 	BooleanOption(  0 , "tcp",              &gDNSQuery_UseTCP,           "Send the DNS query via TCP instead of UDP." ),
1004 	IntegerOption( 'f', "flags",            &gDNSQuery_Flags,            "flags", "16-bit value for DNS header flags/codes field. (default: 0x0100 [Recursion Desired])", false ),
1005 	BooleanOption(  0 , "dnssec",           &gDNSQuery_DNSSEC,           "Set the AD bit and include OPT record with DO extended flag bit set." ),
1006 	BooleanOption(  0 , "checkingDisabled", &gDNSQuery_CheckingDisabled, "Set the Checking Disabled (CD) bit." ),
1007 	BooleanOption(  0 , "raw",              &gDNSQuery_RawRData,         "Present record data as a hexdump." ),
1008 	BooleanOption( 'v', "verbose",          &gDNSQuery_Verbose,          "Prints the DNS message to be sent to the server." ),
1009 	CLI_OPTION_END()
1010 };
1011 
1012 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
1013 //===========================================================================================================================
1014 //	DNSCrypt Command Options
1015 //===========================================================================================================================
1016 
1017 static char *		gDNSCrypt_ProviderName	= NULL;
1018 static char *		gDNSCrypt_ProviderKey	= NULL;
1019 static char *		gDNSCrypt_Name			= NULL;
1020 static char *		gDNSCrypt_Type			= NULL;
1021 static char *		gDNSCrypt_Server		= NULL;
1022 static int			gDNSCrypt_TimeLimitSecs	= 5;
1023 static int			gDNSCrypt_RawRData		= false;
1024 static int			gDNSCrypt_Verbose		= false;
1025 
1026 static CLIOption		kDNSCryptOpts[] =
1027 {
1028 	StringOption(  'p', "providerName",	&gDNSCrypt_ProviderName,	"name", "The DNSCrypt provider name.", true ),
1029 	StringOption(  'k', "providerKey",	&gDNSCrypt_ProviderKey,		"hex string", "The DNSCrypt provider's public signing key.", true ),
1030 	StringOption(  'n', "name",			&gDNSCrypt_Name,			"name",	"Question name (QNAME) to put in DNS query message.", true ),
1031 	StringOption(  't', "type",			&gDNSCrypt_Type,			"type",	"Question type (QTYPE) to put in DNS query message.", true ),
1032 	StringOption(  's', "server",		&gDNSCrypt_Server,			"IP address", "DNS server's IPv4 or IPv6 address.", true ),
1033 	IntegerOption( 'l', "timeLimit",	&gDNSCrypt_TimeLimitSecs,	"seconds", "Specifies query time limit. Use '-1' for no time limit and '0' to exit immediately after sending.", false ),
1034 	BooleanOption(  0 , "raw",			&gDNSCrypt_RawRData,		"Present record data as a hexdump." ),
1035 	BooleanOption( 'v', "verbose",		&gDNSCrypt_Verbose,			"Prints the DNS message to be sent to the server." ),
1036 	CLI_OPTION_END()
1037 };
1038 #endif
1039 
1040 //===========================================================================================================================
1041 //	MDNSQuery Command Options
1042 //===========================================================================================================================
1043 
1044 static char *		gMDNSQuery_Name			= NULL;
1045 static char *		gMDNSQuery_Type			= NULL;
1046 static int			gMDNSQuery_SourcePort	= 0;
1047 static int			gMDNSQuery_IsQU			= false;
1048 static int			gMDNSQuery_RawRData		= false;
1049 static int			gMDNSQuery_UseIPv4		= false;
1050 static int			gMDNSQuery_UseIPv6		= false;
1051 static int			gMDNSQuery_AllResponses	= false;
1052 static int			gMDNSQuery_ReceiveSecs	= 1;
1053 
1054 static CLIOption		kMDNSQueryOpts[] =
1055 {
1056 	StringOption(  'i', "interface",	&gInterface,				"name or index", "Network interface by name or index.", true ),
1057 	StringOption(  'n', "name",			&gMDNSQuery_Name,			"name", "Question name (QNAME) to put in mDNS message.", true ),
1058 	StringOption(  't', "type",			&gMDNSQuery_Type,			"type", "Question type (QTYPE) to put in mDNS message.", true ),
1059 	IntegerOption( 'p', "sourcePort",	&gMDNSQuery_SourcePort,		"port number", "UDP source port to use when sending mDNS messages. Default is 5353 for QM questions.", false ),
1060 	BooleanOption( 'u', "QU",			&gMDNSQuery_IsQU,			"Set the unicast-response bit, i.e., send a QU question." ),
1061 	BooleanOption(  0 , "raw",			&gMDNSQuery_RawRData,		"Present record data as a hexdump." ),
1062 	BooleanOption(  0 , "ipv4",			&gMDNSQuery_UseIPv4,		"Use IPv4." ),
1063 	BooleanOption(  0 , "ipv6",			&gMDNSQuery_UseIPv6,		"Use IPv6." ),
1064 	BooleanOption( 'a', "allResponses",	&gMDNSQuery_AllResponses,	"Print all received mDNS messages, not just those containing answers." ),
1065 	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 ),
1066 	CLI_OPTION_END()
1067 };
1068 
1069 //===========================================================================================================================
1070 //	MDNSCollider Command Options
1071 //===========================================================================================================================
1072 
1073 #define kMDNSColliderProgramSection_Intro																				\
1074 	"Programs dictate when the collider sends out unsolicited response messages for its record and how the collider\n"	\
1075 	"ought to react to probe queries that match its record's name, if at all.\n"										\
1076 	"\n"																												\
1077 	"For example, suppose that the goal is to cause a specific unique record in the verified state to be renamed.\n"	\
1078 	"The collider should be invoked such that its record's name is equal to that of the record being targeted. Also,\n"	\
1079 	"the record's type and data should be such that no record with that name, type, and data combination currently\n"	\
1080 	"exists. If the mDNS responder that owns the record follows sections 8.1 and 9 of RFC 6762, then the goal can be\n"	\
1081 	"accomplished with the following program:\n"																		\
1082 	"\n"																												\
1083 	"    probes 3r; send; wait 5000\n"																					\
1084 	"\n"																												\
1085 	"The first command, 'probes 3r', tells the collider to respond to the next three probe queries that match its\n"	\
1086 	"record's name. The second command, makes the collider send an unsolicited response message that contains its\n"	\
1087 	"record in the answer section. The third command makes the collider wait for five seconds before exiting, which\n"	\
1088 	"is more than enough time for the collider to respond to probe queries.\n"											\
1089 	"\n"																												\
1090 	"The send command will cause the targeted record to go into the probing state per section 9 since the collider's\n"	\
1091 	"record conflicts with target record. Per the probes command, the subsequent probe query sent during the probing\n"	\
1092 	"state will be answered by the collider, which will cause the record to be renamed per section 8.1.\n"
1093 
1094 #define kMDNSColliderProgramSection_Probes																				\
1095 	"The probes command defines how the collider ought to react to probe queries that match its record's name.\n"		\
1096 	"\n"																												\
1097 	"Usage: probes [<action-string>]\n"																					\
1098 	"\n"																												\
1099 	"The syntax for an action-string is\n"																				\
1100 	"\n"																												\
1101 	"    <action-string> ::= <action> | <action-string> \"-\" <action>\n"												\
1102 	"    <action>        ::= [<repeat-count>] <action-code>\n"															\
1103 	"    <repeat-count>  ::= \"1\" | \"2\" | ... | \"10\"\n"															\
1104 	"    <action-code>   ::= \"n\" | \"r\" | \"u\" | \"m\" | \"p\"\n"													\
1105 	"\n"																												\
1106 	"An expanded action-string is defined as\n"																			\
1107 	"\n"																												\
1108 	"    <expanded-action-string> ::= <action-code> | <expanded-action-string> \"-\" <action-code>\n"					\
1109 	"\n"																												\
1110 	"The action-string argument is converted into an expanded-action-string by expanding each action with a\n"			\
1111 	"repeat-count into an expanded-action-string consisting of exactly <repeat-count> <action-code>s. For example,\n"	\
1112 	"2n-r expands to n-n-r. Action-strings that expand to expanded-action-strings with more than 10 action-codes\n"		\
1113 	"are not allowed.\n"																								\
1114 	"\n"																												\
1115 	"When the probes command is executed, it does two things. Firstly, it resets to zero the collider's count of\n"		\
1116 	"probe queries that match its record's name. Secondly, it defines how the collider ought to react to such probe\n"	\
1117 	"queries based on the action-string argument. Specifically, the nth action-code in the expanded version of the\n"	\
1118 	"action-string argument defines how the collider ought to react to the nth received probe query:\n"					\
1119 	"\n"																												\
1120 	"    Code  Action\n"																								\
1121 	"    ----  ------\n"																								\
1122 	"    n     Do nothing.\n"																							\
1123 	"    r     Respond to the probe query.\n"																			\
1124 	"    u     Respond to the probe query via unicast.\n"																\
1125 	"    m     Respond to the probe query via multicast.\n"																\
1126 	"    p     Multicast own probe query. (Useful for causing simultaneous probe scenarios.)\n"							\
1127 	"\n"																												\
1128 	"Note: If no action is defined for a received probe query, then the collider does nothing, i.e., it doesn't send\n"	\
1129 	"a response nor does it multicast its own probe query.\n"
1130 
1131 #define kMDNSColliderProgramSection_Send																				\
1132 	"The send command multicasts an unsolicited mDNS response containing the collider's record in the answer\n"			\
1133 	"section, which can be used to force unique records with the same record name into the probing state.\n"			\
1134 	"\n"																												\
1135 	"Usage: send\n"
1136 
1137 #define kMDNSColliderProgramSection_Wait																				\
1138 	"The wait command pauses program execution for the interval of time specified by its argument.\n"					\
1139 	"\n"																												\
1140 	"Usage: wait <milliseconds>\n"
1141 
1142 #define kMDNSColliderProgramSection_Loop																				\
1143 	"The loop command starts a counting loop. The done statement marks the end of the loop body. The loop command's\n"	\
1144 	"argument specifies the number of loop iterations. Note: Loop nesting is supported up to a depth of 16.\n"			\
1145 	"\n"																												\
1146 	"Usage: loop <non-zero count>; ... ; done\n"																		\
1147 	"\n"																												\
1148 	"For example, the following program sends three unsolicited responses at an approximate rate of one per second:\n"	\
1149 	"\n"																												\
1150 	"    loop 3; wait 1000; send; done"
1151 
1152 #define ConnectionSection()		CLI_SECTION( kConnectionSection_Name, kConnectionSection_Text )
1153 
1154 static const char *		gMDNSCollider_Name			= NULL;
1155 static const char *		gMDNSCollider_Type			= NULL;
1156 static const char *		gMDNSCollider_RecordData	= NULL;
1157 static int				gMDNSCollider_UseIPv4		= false;
1158 static int				gMDNSCollider_UseIPv6		= false;
1159 static const char *		gMDNSCollider_Program		= NULL;
1160 
1161 static CLIOption		kMDNSColliderOpts[] =
1162 {
1163 	StringOption(  'i', "interface", &gInterface,               "name or index", "Network interface by name or index.", true ),
1164 	StringOption(  'n', "name",      &gMDNSCollider_Name,       "name", "Collider's record name.", true ),
1165 	StringOption(  't', "type",      &gMDNSCollider_Type,       "type", "Collider's record type.", true ),
1166 	StringOption(  'd', "data",      &gMDNSCollider_RecordData, "record data", "Collider's record data. See " kRecordDataSection_Name " below.", true ),
1167 	StringOption(  'p', "program",   &gMDNSCollider_Program,    "program", "Program to execute. See Program section below.", true ),
1168 	BooleanOption(  0 , "ipv4",      &gMDNSCollider_UseIPv4,    "Use IPv4." ),
1169 	BooleanOption(  0 , "ipv6",      &gMDNSCollider_UseIPv6,    "Use IPv6." ),
1170 
1171 	RecordDataSection(),
1172 	CLI_SECTION( "Program",					kMDNSColliderProgramSection_Intro ),
1173 	CLI_SECTION( "Program Command: probes",	kMDNSColliderProgramSection_Probes ),
1174 	CLI_SECTION( "Program Command: send",	kMDNSColliderProgramSection_Send ),
1175 	CLI_SECTION( "Program Command: wait",	kMDNSColliderProgramSection_Wait ),
1176 	CLI_SECTION( "Program Command: loop",	kMDNSColliderProgramSection_Loop ),
1177 	CLI_OPTION_END()
1178 };
1179 
1180 static void	MDNSColliderCmd( void );
1181 
1182 #if( TARGET_OS_DARWIN )
1183 //===========================================================================================================================
1184 //	PIDToUUID Command Options
1185 //===========================================================================================================================
1186 
1187 static int		gPIDToUUID_PID = 0;
1188 
1189 static CLIOption		kPIDToUUIDOpts[] =
1190 {
1191 	IntegerOption( 'p', "pid", &gPIDToUUID_PID, "PID", "Process ID.", true ),
1192 	CLI_OPTION_END()
1193 };
1194 #endif
1195 
1196 //===========================================================================================================================
1197 //	DNSServer Command Options
1198 //===========================================================================================================================
1199 
1200 static const char		kDNSServerInfoText_Intro[] =
1201 	"The DNS server answers certain queries in the d.test. domain. Responses are dynamically generated based on the\n"
1202 	"presence of special labels in the query's QNAME. There are currently nine types of special labels that can be\n"
1203 	"used to generate specific responses: Alias labels, Alias-TTL labels, Count labels, Tag labels, TTL labels, the\n"
1204 	"IPv4 label, the IPv6 label, Index labels, and SRV labels.\n"
1205 	"\n"
1206 	"Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n";
1207 
1208 static const char		kDNSServerInfoText_NameExistence[] =
1209 	"A name is considered to exist if it's an Address name or an SRV name.\n"
1210 	"\n"
1211 	"An Address name is defined as a name that ends with d.test., and the other labels, if any, and in no particular\n"
1212 	"order, unless otherwise noted, consist of\n"
1213 	"\n"
1214 	"    1. at most one Alias or Alias-TTL label as the first label;\n"
1215 	"    2. at most one Count label;\n"
1216 	"    3. zero or more Tag labels;\n"
1217 	"    4. at most one TTL label; and\n"
1218 	"    5. at most one IPv4 or IPv6 label.\n"
1219 	"    6. at most one Index label.\n"
1220 	"\n"
1221 	"An SRV name is defined as a name with the following form:\n"
1222 	"\n"
1223 	" _<service>._<proto>[.<parent domain>][.<SRV label 1>[.<target 1>][.<SRV label 2>[.<target 2>][...]]].d.test.\n"
1224 	"\n"
1225 	"See \"SRV Names\" for details.\n";
1226 
1227 static const char		kDNSServerInfoText_ResourceRecords[] =
1228 	"Currently, the server only supports CNAME, A, AAAA, and SRV records.\n"
1229 	"\n"
1230 	"Address names that begin with an Alias or Alias-TTL label are aliases of canonical names, i.e., they're the\n"
1231 	"names of CNAME records. See \"Alias Labels\" and \"Alias-TTL Labels\" for details.\n"
1232 	"\n"
1233 	"A canonical Address name can exclusively be the name of one or more A records, can exclusively be the name or\n"
1234 	"one or more AAAA records, or can be the name of both A and AAAA records. Address names that contain an IPv4\n"
1235 	"label have at least one A record, but no AAAA records. Address names that contain an IPv6 label, have at least\n"
1236 	"one AAAA record, but no A records. All other Address names have at least one A record and at least one AAAA\n"
1237 	"record. See \"Count Labels\" for how the number of address records for a given Address name is determined.\n"
1238 	"\n"
1239 	"A records contain IPv4 addresses in the 203.0.113.0/24 block, while AAAA records contain IPv6 addresses in the\n"
1240 	"2001:db8:1::/120 block. Both of these address blocks are reserved for documentation. See\n"
1241 	"<https://tools.ietf.org/html/rfc5737> and <https://tools.ietf.org/html/rfc3849>.\n"
1242 	"\n"
1243 	"SRV names are names of SRV records.\n"
1244 	"\n"
1245 	"Unless otherwise specified, all resource records will use a default TTL. The default TTL can be set with the\n"
1246 	"--defaultTTL option. See \"Alias-TTL Labels\" and \"TTL Labels\" for details on how to query for CNAME, A, and\n"
1247 	"AAAA records with specific TTL values.\n";
1248 
1249 static const char		kDNSServerInfoText_AliasLabel[] =
1250 	"Alias labels are of the form \"alias\" or \"alias-N\", where N is an integer in [2, 2^31 - 1].\n"
1251 	"\n"
1252 	"If QNAME is an Address name and its first label is Alias label \"alias-N\", then the response will contain\n"
1253 	"exactly N CNAME records:\n"
1254 	"\n"
1255 	"    1. For each i in [3, N], the response will contain a CNAME record whose name is identical to QNAME, except\n"
1256 	"       that the first label is \"alias-i\" instead, and whose RDATA is the name of the other CNAME record whose\n"
1257 	"       name has \"alias-(i - 1)\" as its first label.\n"
1258 	"\n"
1259 	"    2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n"
1260 	"       is \"alias-2\" instead, and whose RDATA is the name identical to QNAME, except that the first label is\n"
1261 	"       \"alias\" instead.\n"
1262 	"\n"
1263 	"    3. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n"
1264 	"       is \"alias\" instead, and whose RDATA is the name identical to QNAME minus its first label.\n"
1265 	"\n"
1266 	"If QNAME is an Address name and its first label is Alias label \"alias\", then the response will contain a\n"
1267 	"single CNAME record. The CNAME record's name will be equal to QNAME and its RDATA will be the name identical to\n"
1268 	"QNAME minus its first label.\n"
1269 	"\n"
1270 	"Example. A response to a query with a QNAME of alias-3.count-5.d.test will contain the following CNAME\n"
1271 	"records:\n"
1272 	"\n"
1273 	"    alias-4.count-5.d.test.                        60    IN CNAME alias-3.count-5.d.test.\n"
1274 	"    alias-3.count-5.d.test.                        60    IN CNAME alias-2.count-5.d.test.\n"
1275 	"    alias-2.count-5.d.test.                        60    IN CNAME alias.count-5.d.test.\n"
1276 	"    alias.count-5.d.test.                          60    IN CNAME count-5.d.test.\n";
1277 
1278 static const char		kDNSServerInfoText_AliasTTLLabel[] =
1279 	"Alias-TTL labels are of the form \"alias-ttl-T_1[-T_2[...-T_N]]\", where each T_i is an integer in\n"
1280 	"[0, 2^31 - 1] and N is a positive integer bounded by the size of the maximum legal label length (63 octets).\n"
1281 	"\n"
1282 	"If QNAME is an Address name and its first label is Alias-TTL label \"alias-ttl-T_1...-T_N\", then the response\n"
1283 	"will contain exactly N CNAME records:\n"
1284 	"\n"
1285 	"    1. For each i in [1, N - 1], the response will contain a CNAME record whose name is identical to QNAME,\n"
1286 	"       except that the first label is \"alias-ttl-T_i...-T_N\" instead, whose TTL value is T_i, and whose RDATA\n"
1287 	"       is the name of the other CNAME record whose name has \"alias-ttl-T_(i+1)...-T_N\" as its first label.\n"
1288 	"\n"
1289 	"    2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n"
1290 	"       is \"alias-ttl-T_N\", whose TTL is T_N, and whose RDATA is identical to QNAME stripped of its first\n"
1291 	"       label.\n"
1292 	"\n"
1293 	"Example. A response to a query with a QNAME of alias-ttl-20-40-80.count-5.d.test will contain the following\n"
1294 	"CNAME records:\n"
1295 	"\n"
1296 	"    alias-ttl-20-40-80.count-5.d.test.             20    IN CNAME alias-ttl-40-80.count-5.d.test.\n"
1297 	"    alias-ttl-40-80.count-5.d.test.                40    IN CNAME alias-ttl-80.count-5.d.test.\n"
1298 	"    alias-ttl-80.count-5.d.test.                   80    IN CNAME count-5.d.test.\n";
1299 
1300 static const char		kDNSServerInfoText_CountLabel[] =
1301 	"Count labels are of the form \"count-N_1\" or \"count-N_1-N_2\", where N_1 is an integer in [0, 255] and N_2 is\n"
1302 	"an integer in [N_1, 255].\n"
1303 	"\n"
1304 	"If QNAME is an Address name, contains Count label \"count-N\", and has the type of address records specified by\n"
1305 	"QTYPE, then the response will contain exactly N address records:\n"
1306 	"\n"
1307 	"    1. For i in [1, N], the response will contain an address record of type QTYPE whose name is equal to QNAME\n"
1308 	"       and whose RDATA is an address equal to a constant base address + i.\n"
1309 	"\n"
1310 	"    2. The address records will be ordered by the address contained in RDATA in ascending order.\n"
1311 	"\n"
1312 	"Example. A response to an A record query with a QNAME of alias.count-3.d.test will contain the following A\n"
1313 	"records:\n"
1314 	"\n"
1315 	"    count-3.d.test.                                60    IN A     203.0.113.1\n"
1316 	"    count-3.d.test.                                60    IN A     203.0.113.2\n"
1317 	"    count-3.d.test.                                60    IN A     203.0.113.3\n"
1318 	"\n"
1319 	"If QNAME is an Address name, contains Count label \"count-N_1-N_2\", and has the type of address records\n"
1320 	"specified by QTYPE, then the response will contain exactly N_1 address records:\n"
1321 	"\n"
1322 	"    1. Each of the address records will be of type QTYPE, have name equal to QNAME, and have as its RDATA a\n"
1323 	"       unique address equal to a constant base address + i, where i is a randomly chosen integer in [1, N_2].\n"
1324 	"\n"
1325 	"    2. The order of the address records will be random.\n"
1326 	"\n"
1327 	"Example. A response to a AAAA record query with a QNAME of count-3-100.ttl-20.d.test could contain the\n"
1328 	"following AAAA records:\n"
1329 	"\n"
1330 	"    count-3-100.ttl-20.d.test.                     20    IN AAAA  2001:db8:1::c\n"
1331 	"    count-3-100.ttl-20.d.test.                     20    IN AAAA  2001:db8:1::3a\n"
1332 	"    count-3-100.ttl-20.d.test.                     20    IN AAAA  2001:db8:1::4f\n"
1333 	"\n"
1334 	"If QNAME is an Address name, but doesn't have the type of address records specified by QTYPE, then the response\n"
1335 	"will contain no address records, regardless of whether it contains a Count label.\n"
1336 	"\n"
1337 	"Address names that don't have a Count label are treated as though they contain a count label equal to\n"
1338 	"count-1\".\n";
1339 
1340 static const char		kDNSServerInfoText_TagLabel[] =
1341 	"Tag labels are labels prefixed with \"tag-\" and contain zero or more arbitrary octets after the prefix.\n"
1342 	"\n"
1343 	"This type of label exists to allow testers to \"uniquify\" domain names. Tag labels can also serve as padding\n"
1344 	"to increase the sizes of domain names.\n";
1345 
1346 static const char		kDNSServerInfoText_TTLLabel[] =
1347 	"TTL labels are of the form \"ttl-T\", where T is an integer in [0, 2^31 - 1].\n"
1348 	"\n"
1349 	"If QNAME is an Address name and contains TTL label \"ttl-T\", then all non-CNAME records contained in the\n"
1350 	"response will have a TTL value equal to T.\n";
1351 
1352 static const char		kDNSServerInfoText_IPv4Label[] =
1353 	"The IPv4 label is \"ipv4\". See \"Resource Records\" for the affect of this label.\n";
1354 
1355 static const char		kDNSServerInfoText_IPv6Label[] =
1356 	"The IPv6 label is \"ipv6\". See \"Resource Records\" for the affect of this label.\n";
1357 
1358 static const char		kDNSServerInfoText_IndexLabel[] =
1359 	"Index labels are of the form \"index-N\", where N is an integer in [1, 2^31 - 1].\n"
1360 	"\n"
1361 	"When the server runs in loopback-only mode, each of the server's addresses is assigned a sequential index value\n"
1362 	"starting from 1. For example, if the server is running in loopback-only mode and listening exclusively on IPv6\n"
1363 	"with two extra IPv6 addresses, then address ::1 would be assigned index 1, the first extra IPv6 address would be\n"
1364 	"assigned index 2, and the second extra IPv6 address would be assigned index 3.\n"
1365 	"\n"
1366 	"If QNAME is an Address name and has an index label, then the query will be ignored unless the query was received\n"
1367 	"on an address whose index value equals that of the index label. This is useful for simulating unresponsive servers.\n";
1368 
1369 static const char		kDNSServerInfoText_SRVNames[] =
1370 	"SRV labels are of the form \"srv-R-W-P\", where R, W, and P are integers in [0, 2^16 - 1].\n"
1371 	"\n"
1372 	"After the first two labels, i.e., the service and protocol labels, the sequence of labels, which may be empty,\n"
1373 	"leading up to the the first SRV label, if one exists, or the d.test. labels will be used as a parent domain for\n"
1374 	"the target hostname of each of the SRV name's SRV records.\n"
1375 	"\n"
1376 	"If QNAME is an SRV name and QTYPE is SRV, then for each SRV label, the response will contain an SRV record with\n"
1377 	"priority R, weight W, port P, and target hostname <target>[.<parent domain>]., where <target> is the sequence\n"
1378 	"of labels, which may be empty, that follows the SRV label leading up to either the next SRV label or the\n"
1379 	"d.test. labels, whichever comes first.\n"
1380 	"\n"
1381 	"Example. A response to an SRV record query with a QNAME of\n"
1382 	"_http._tcp.example.com.srv-0-0-80.www.srv-1-0-8080.www.d.test. will contain the following SRV records:\n"
1383 	"\n"
1384 	"_http._tcp.example.com.srv-0-0-80.www.srv-1-0-8080.www.d.test.     60    IN SRV   0 0 80 www.example.com.\n"
1385 	"_http._tcp.example.com.srv-0-0-80.www.srv-1-0-8080.www.d.test.     60    IN SRV   1 0 8080 www.example.com.\n";
1386 
1387 static const char		kDNSServerInfoText_BadUDPMode[] =
1388 	"The purpose of Bad UDP mode is to test mDNSResponder's TCP fallback mechanism by which mDNSResponder reissues a\n"
1389 	"UDP query as a TCP query if the UDP response contains the expected QNAME, QTYPE, and QCLASS, but a message ID\n"
1390 	"that's not equal to the query's message ID.\n"
1391 	"\n"
1392 	"This mode is identical to the normal mode except that all responses sent via UDP have a message ID equal to the\n"
1393 	"query's message ID plus one. Also, in this mode, to aid in debugging, A records in responses sent via UDP have\n"
1394 	"IPv4 addresses in the 0.0.0.0/24 block instead of the 203.0.113.0/24 block, i.e., 0.0.0.0 is used as the IPv4\n"
1395 	"base address, and AAAA records in responses sent via UDP have IPv6 addresses in the ::ffff:0:0/120 block\n"
1396 	"instead of the 2001:db8:1::/120 block, i.e., ::ffff:0:0 is used as the IPv6 base address.\n";
1397 
1398 static int				gDNSServer_LoopbackOnly			= false;
1399 static int				gDNSServer_Foreground			= false;
1400 static int				gDNSServer_ResponseDelayMs		= 0;
1401 static int				gDNSServer_DefaultTTL			= 60;
1402 static int				gDNSServer_Port					= kDNSPort;
1403 static const char *		gDNSServer_DomainOverride		= NULL;
1404 static char **			gDNSServer_IgnoredQTypes		= NULL;
1405 static size_t			gDNSServer_IgnoredQTypesCount	= 0;
1406 static int				gDNSServer_ListenOnV4			= false;
1407 static int				gDNSServer_ListenOnV6			= false;
1408 static int				gDNSServer_BadUDPMode			= false;
1409 #if( TARGET_OS_DARWIN )
1410 static const char *		gDNSServer_FollowPID			= NULL;
1411 static int				gDNSServer_ExtraV6Count			= 0;
1412 #endif
1413 
1414 static CLIOption		kDNSServerOpts[] =
1415 {
1416 	BooleanOption(     'l', "loopback",      &gDNSServer_LoopbackOnly,    "Bind only to the loopback interface." ),
1417 	BooleanOption(     'f', "foreground",    &gDNSServer_Foreground,      "Direct log output to stdout instead of system logging." ),
1418 	IntegerOption(     'd', "responseDelay", &gDNSServer_ResponseDelayMs, "ms", "The amount of additional delay in milliseconds to apply to responses. (default: 0)", false ),
1419 	IntegerOption(      0 , "defaultTTL",    &gDNSServer_DefaultTTL,      "seconds", "Resource record TTL value to use when unspecified. (default: 60)", false ),
1420 	IntegerOption(     'p', "port",          &gDNSServer_Port,            "port number", "UDP/TCP port number to use. Use 0 for any port. (default: 53)", false ),
1421 	StringOption(       0 , "domain",        &gDNSServer_DomainOverride,  "domain", "Use to override 'd.test.' as the server's domain.", false ),
1422 	MultiStringOption( 'i', "ignoreQType",	 &gDNSServer_IgnoredQTypes, &gDNSServer_IgnoredQTypesCount, "qtype", "A QTYPE to ignore. This option can be specified more than once.", false ),
1423 	BooleanOption(      0 , "ipv4",          &gDNSServer_ListenOnV4,      "Listen on IPv4. Will listen on both IPv4 and IPv6 if neither --ipv4 nor --ipv6 is used." ),
1424 	BooleanOption(      0 , "ipv6",          &gDNSServer_ListenOnV6,      "Listen on IPv6. Will listen on both IPv4 and IPv6 if neither --ipv4 nor --ipv6 is used." ),
1425 #if( TARGET_OS_DARWIN )
1426 	StringOption(       0 , "follow",        &gDNSServer_FollowPID,       "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1427 #endif
1428 	BooleanOption(      0 , "badUDPMode",    &gDNSServer_BadUDPMode,      "Run in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1429 
1430 #if( TARGET_OS_DARWIN )
1431 	CLI_OPTION_GROUP( "Loopback-Only Mode Options" ),
1432 	IntegerOptionEx( 0 , "extraIPv6",     &gDNSServer_ExtraV6Count,    "count", "The number of extra IPv6 addresses to listen on. (default: 0)", false,
1433 		"\n"
1434 		"This option will add extra IPv6 addresses from the fded:f035:55e4::/64 address block to the loopback interface.\n"
1435 		"The server will then bind to those addresses in addition to the standard loopback IP addresses, i.e., 127.0.0.1.\n"
1436 		"and/or ::1, depending on the specified IP protocol options.\n"
1437 		"\n"
1438 		"This option is useful for setting up a DNS configuration with multiple server addresses, e.g., one for the\n"
1439 		"primary server, one for the secondary server, etc. The Index label can then be used to simulate unresponsive\n"
1440 		"servers.\n"
1441 		"\n"
1442 		"Note: This option is ignored unless the server is in loopback only mode and listening on IPv6.\n"
1443 		"Note: This option currently requires root privileges.\n"
1444 	),
1445 #endif
1446 	CLI_SECTION( "Intro",				kDNSServerInfoText_Intro ),
1447 	CLI_SECTION( "Name Existence",		kDNSServerInfoText_NameExistence ),
1448 	CLI_SECTION( "Resource Records",	kDNSServerInfoText_ResourceRecords ),
1449 	CLI_SECTION( "Alias Labels",		kDNSServerInfoText_AliasLabel ),
1450 	CLI_SECTION( "Alias-TTL Labels",	kDNSServerInfoText_AliasTTLLabel ),
1451 	CLI_SECTION( "Count Labels",		kDNSServerInfoText_CountLabel ),
1452 	CLI_SECTION( "Tag Labels",			kDNSServerInfoText_TagLabel ),
1453 	CLI_SECTION( "TTL Labels",			kDNSServerInfoText_TTLLabel ),
1454 	CLI_SECTION( "IPv4 Label",			kDNSServerInfoText_IPv4Label ),
1455 	CLI_SECTION( "IPv6 Label",			kDNSServerInfoText_IPv6Label ),
1456 	CLI_SECTION( "Index Labels",		kDNSServerInfoText_IndexLabel ),
1457 	CLI_SECTION( "SRV Names",			kDNSServerInfoText_SRVNames ),
1458 	CLI_SECTION( "Bad UDP Mode",		kDNSServerInfoText_BadUDPMode ),
1459 	CLI_OPTION_END()
1460 };
1461 
1462 static void	DNSServerCmd( void );
1463 
1464 //===========================================================================================================================
1465 //	MDNSReplier Command Options
1466 //===========================================================================================================================
1467 
1468 #define kMDNSReplierPortBase		50000
1469 
1470 static const char		kMDNSReplierInfoText_Intro[] =
1471 	"The mDNS replier answers mDNS queries for its authoritative records. These records are of class IN and of types\n"
1472 	"PTR, SRV, TXT, A, and AAAA as described below.\n"
1473 	"\n"
1474 	"Note: Sub-strings representing integers in domain name labels are in decimal notation and without leading zeros.\n";
1475 
1476 static const char		kMDNSReplierInfoText_Parameters[] =
1477 	"There are five parameters that control the replier's set of authoritative records.\n"
1478 	"\n"
1479 	"    1. <hostname> is the base name used for service instance names and the names of A and AAAA records. This\n"
1480 	"       parameter is specified with the --hostname option.\n"
1481 	"    2. <tag> is an arbitrary string used to uniquify service types. This parameter is specified with the --tag\n"
1482 	"       option.\n"
1483 	"    3. N_max in an integer in [1, 65535] and limits service types to those that have no more than N_max\n"
1484 	"       instances. It also limits the number of hostnames to N_max, i.e., <hostname>.local.,\n"
1485 	"       <hostname>-1.local., ..., <hostname>-N_max.local. This parameter is specified with the\n"
1486 	"       --maxInstanceCount option.\n"
1487 	"    4. N_a is an integer in [1, 255] and the number of A records per hostname. This parameter is specified\n"
1488 	"       with the --countA option.\n"
1489 	"    5. N_aaaa is an integer in [1, 255] and the number of AAAA records per hostname. This parameter is\n"
1490 	"       specified with the --countAAAA option.\n";
1491 
1492 static const char		kMDNSReplierInfoText_PTR[] =
1493 	"The replier's authoritative PTR records have names of the form _t-<tag>-<L>-<N>._tcp.local., where L is an\n"
1494 	"integer in [1, 65535], and N is an integer in [1, N_max].\n"
1495 	"\n"
1496 	"For a given L and N, the replier has exactly N authoritative PTR records:\n"
1497 	"\n"
1498 	"    1. The first PTR record is defined as\n"
1499 	"\n"
1500 	"        NAME:  _t-<tag>-<L>-<N>._tcp.local.\n"
1501 	"        TYPE:  PTR\n"
1502 	"        CLASS: IN\n"
1503 	"        TTL:   4500\n"
1504 	"        RDATA: <hostname>._t-<tag>-<L>-<N>._tcp.local.\n"
1505 	"\n"
1506 	"    2. For each i in [2, N], there is one PTR record defined as\n"
1507 	"\n"
1508 	"        NAME:  _t-<tag>-<L>-<N>._tcp.local.\n"
1509 	"        TYPE:  PTR\n"
1510 	"        CLASS: IN\n"
1511 	"        TTL:   4500\n"
1512 	"        RDATA: \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n";
1513 
1514 static const char		kMDNSReplierInfoText_SRV[] =
1515 	"The replier's authoritative SRV records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n"
1516 	"where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n"
1517 	"\"<hostname> (<i>)\", where i is in [2, N].\n"
1518 	"\n"
1519 	"For a given L and N, the replier has exactly N authoritative SRV records:\n"
1520 	"\n"
1521 	"    1. The first SRV record is defined as\n"
1522 	"\n"
1523 	"        NAME:  <hostname>._t-<tag>-<L>-<N>._tcp.local.\n"
1524 	"        TYPE:  SRV\n"
1525 	"        CLASS: IN\n"
1526 	"        TTL:   120\n"
1527 	"        RDATA:\n"
1528 	"            Priority: 0\n"
1529 	"            Weight:   0\n"
1530 	"            Port:     (50000 + L) mod 2^16\n"
1531 	"            Target:   <hostname>.local.\n"
1532 	"\n"
1533 	"    2. For each i in [2, N], there is one SRV record defined as:\n"
1534 	"\n"
1535 	"        NAME:  \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n"
1536 	"        TYPE:  SRV\n"
1537 	"        CLASS: IN\n"
1538 	"        TTL:   120\n"
1539 	"        RDATA:\n"
1540 	"            Priority: 0\n"
1541 	"            Weight:   0\n"
1542 	"            Port:     (50000 + L) mod 2^16\n"
1543 	"            Target:   <hostname>-<i>.local.\n";
1544 
1545 static const char		kMDNSReplierInfoText_TXT[] =
1546 	"The replier's authoritative TXT records have names of the form <instance name>._t-<tag>-<L>-<N>._tcp.local.,\n"
1547 	"where L is an integer in [1, 65535], N is an integer in [1, N_max], and <instance name> is <hostname> or\n"
1548 	"\"<hostname> (<i>)\", where i is in [2, N].\n"
1549 	"\n"
1550 	"For a given L and N, the replier has exactly N authoritative TXT records:\n"
1551 	"\n"
1552 	"    1. The first TXT record is defined as\n"
1553 	"\n"
1554 	"        NAME:     <hostname>._t-<tag>-<L>-<N>._tcp.local.\n"
1555 	"        TYPE:     TXT\n"
1556 	"        CLASS:    IN\n"
1557 	"        TTL:      4500\n"
1558 	"        RDLENGTH: L\n"
1559 	"        RDATA:    <one or more strings with an aggregate length of L octets>\n"
1560 	"\n"
1561 	"    2. For each i in [2, N], there is one TXT record:\n"
1562 	"\n"
1563 	"        NAME:     \"<hostname> (<i>)._t-<tag>-<L>-<N>._tcp.local.\"\n"
1564 	"        TYPE:     TXT\n"
1565 	"        CLASS:    IN\n"
1566 	"        TTL:      4500\n"
1567 	"        RDLENGTH: L\n"
1568 	"        RDATA:    <one or more strings with an aggregate length of L octets>\n"
1569 	"\n"
1570 	"The RDATA of each TXT record is exactly L octets and consists of a repeating series of the 15-byte string\n"
1571 	"\"hash=0x<32-bit FNV-1 hash of the record name as an 8-character hexadecimal string>\". The last instance of\n"
1572 	"the string may be truncated to satisfy the TXT record data's size requirement.\n";
1573 
1574 static const char		kMDNSReplierInfoText_A[] =
1575 	"The replier has exactly N_max ✕ N_a authoritative A records:\n"
1576 	"\n"
1577 	"    For each i in [1, N_max], for each j in [1, N_a], an A record is defined as\n"
1578 	"\n"
1579 	"        NAME:     \"<hostname>.local.\" if i = 1, otherwise \"<hostname>-<i>.local.\"\n"
1580 	"        TYPE:     A\n"
1581 	"        CLASS:    IN\n"
1582 	"        TTL:      120\n"
1583 	"        RDLENGTH: 4\n"
1584 	"        RDATA:    0.<⌊i / 256⌋>.<i mod 256>.<j>\n";
1585 
1586 static const char		kMDNSReplierInfoText_AAAA[] =
1587 	"The replier has exactly N_max ✕ N_aaaa authoritative AAAA records:\n"
1588 	"\n"
1589 	"    1. For each j in [1, N_aaaa], a AAAA record is defined as\n"
1590 	"\n"
1591 	"        NAME:     <hostname>.local.\n"
1592 	"        TYPE:     AAAA\n"
1593 	"        CLASS:    IN\n"
1594 	"        TTL:      120\n"
1595 	"        RDLENGTH: 16\n"
1596 	"        RDATA:    fe80::1:<j>\n"
1597 	"\n"
1598 	"    2. For each i in [2, N_max], for each j in [1, N_aaaa], a AAAA record is defined as\n"
1599 	"\n"
1600 	"        NAME:     <hostname>-<i>.local.\n"
1601 	"        TYPE:     AAAA\n"
1602 	"        CLASS:    IN\n"
1603 	"        TTL:      120\n"
1604 	"        RDLENGTH: 16\n"
1605 	"        RDATA:    2001:db8:2::<i>:<j>\n";
1606 
1607 static const char		kMDNSReplierInfoText_Responses[] =
1608 	"When generating answers for a query message, any two records pertaining to the same hostname will be grouped\n"
1609 	"together in the same response message, and any two records pertaining to different hostnames will be in\n"
1610 	"separate response messages.\n";
1611 
1612 static const char *		gMDNSReplier_Hostname				= NULL;
1613 static const char *		gMDNSReplier_ServiceTypeTag			= NULL;
1614 static int				gMDNSReplier_MaxInstanceCount		= 1000;
1615 static int				gMDNSReplier_NoAdditionals			= false;
1616 static int				gMDNSReplier_RecordCountA			= 1;
1617 static int				gMDNSReplier_RecordCountAAAA		= 1;
1618 static double			gMDNSReplier_UnicastDropRate		= 0.0;
1619 static double			gMDNSReplier_MulticastDropRate		= 0.0;
1620 static int				gMDNSReplier_MaxDropCount			= 0;
1621 static int				gMDNSReplier_UseIPv4				= false;
1622 static int				gMDNSReplier_UseIPv6				= false;
1623 static int				gMDNSReplier_Foreground				= false;
1624 #if( TARGET_OS_POSIX )
1625 static const char *		gMDNSReplier_FollowPID				= NULL;
1626 #endif
1627 
1628 static CLIOption		kMDNSReplierOpts[] =
1629 {
1630 	StringOption(  'i', "interface",        &gInterface,                     "name or index", "Network interface by name or index.", true ),
1631 	StringOption(  'n', "hostname",         &gMDNSReplier_Hostname,          "string", "Base name to use for hostnames and service instance names.", true ),
1632 	StringOption(  't', "tag",              &gMDNSReplier_ServiceTypeTag,    "string", "Tag to use for service types, e.g., _t-<tag>-<TXT size>-<count>._tcp.", true ),
1633 	IntegerOption( 'c', "maxInstanceCount", &gMDNSReplier_MaxInstanceCount,  "count", "Maximum number of service instances. (default: 1000)", false ),
1634 	BooleanOption(  0 , "noAdditionals",    &gMDNSReplier_NoAdditionals,     "When answering queries, don't include any additional records." ),
1635 	IntegerOption(  0 , "countA",           &gMDNSReplier_RecordCountA,      "count", "Number of A records per hostname. (default: 1)", false ),
1636 	IntegerOption(  0 , "countAAAA",        &gMDNSReplier_RecordCountAAAA,   "count", "Number of AAAA records per hostname. (default: 1)", false ),
1637 	DoubleOption(   0 , "udrop",            &gMDNSReplier_UnicastDropRate,   "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1638 	DoubleOption(   0 , "mdrop",            &gMDNSReplier_MulticastDropRate, "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1639 	IntegerOption(  0 , "maxDropCount",     &gMDNSReplier_MaxDropCount,      "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1640 	BooleanOption(  0 , "ipv4",             &gMDNSReplier_UseIPv4,           "Use IPv4." ),
1641 	BooleanOption(  0 , "ipv6",             &gMDNSReplier_UseIPv6,           "Use IPv6." ),
1642 	BooleanOption( 'f', "foreground",       &gMDNSReplier_Foreground,        "Direct log output to stdout instead of system logging." ),
1643 #if( TARGET_OS_POSIX )
1644 	StringOption(   0 , "follow",           &gMDNSReplier_FollowPID,         "pid", "Exit when the process, usually the parent process, specified by PID exits.", false ),
1645 #endif
1646 
1647 	CLI_SECTION( "Intro",							kMDNSReplierInfoText_Intro ),
1648 	CLI_SECTION( "Authoritative Record Parameters",	kMDNSReplierInfoText_Parameters ),
1649 	CLI_SECTION( "Authoritative PTR Records",		kMDNSReplierInfoText_PTR ),
1650 	CLI_SECTION( "Authoritative SRV Records",		kMDNSReplierInfoText_SRV ),
1651 	CLI_SECTION( "Authoritative TXT Records",		kMDNSReplierInfoText_TXT ),
1652 	CLI_SECTION( "Authoritative A Records",			kMDNSReplierInfoText_A ),
1653 	CLI_SECTION( "Authoritative AAAA Records",		kMDNSReplierInfoText_AAAA ),
1654 	CLI_SECTION( "Responses",						kMDNSReplierInfoText_Responses ),
1655 	CLI_OPTION_END()
1656 };
1657 
1658 static void	MDNSReplierCmd( void );
1659 
1660 //===========================================================================================================================
1661 //	Test Command Options
1662 //===========================================================================================================================
1663 
1664 #define kTestExitStatusSection_Name		"Exit Status"
1665 #define kTestExitStatusSection_Text																						\
1666 	"This test command can exit with one of three status codes:\n"														\
1667 	"\n"																												\
1668 	"0 - The test ran to completion and passed.\n"																		\
1669 	"1 - A fatal error prevented the test from completing.\n"															\
1670 	"2 - The test ran to completion, but it or a subtest failed. See test output for details.\n"						\
1671 	"\n"																												\
1672 	"Note: The pass/fail status applies to the correctness or results. It does not necessarily imply anything about\n"	\
1673 	"performance.\n"
1674 
1675 #define TestExitStatusSection()		CLI_SECTION( kTestExitStatusSection_Name, kTestExitStatusSection_Text )
1676 
1677 #define kGAIPerfTestSuiteName_Basic			"basic"
1678 #define kGAIPerfTestSuiteName_Advanced		"advanced"
1679 
1680 static const char *		gGAIPerf_TestSuite				= NULL;
1681 static int				gGAIPerf_CallDelayMs			= 10;
1682 static int				gGAIPerf_ServerDelayMs			= 10;
1683 static int				gGAIPerf_SkipPathEvalulation	= false;
1684 static int				gGAIPerf_BadUDPMode				= false;
1685 static int				gGAIPerf_IterationCount			= 100;
1686 static int				gGAIPerf_IterationTimeLimitMs	= 100;
1687 static const char *		gGAIPerf_OutputFilePath			= NULL;
1688 static const char *		gGAIPerf_OutputFormat			= kOutputFormatStr_JSON;
1689 static int				gGAIPerf_OutputAppendNewline	= false;
1690 
1691 static void	GAIPerfCmd( void );
1692 
1693 #define kGAIPerfSectionText_TestSuiteBasic																					\
1694 	"This test suite consists of the following three test cases:\n"															\
1695 	"\n"																													\
1696 	"Test Case #1: Resolve a domain name with\n"																			\
1697 	"\n"																													\
1698 	"    2 CNAME records, 4 A records, and 4 AAAA records\n"																\
1699 	"\n"																													\
1700 	"to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n"		\
1701 	"server queries.\n"																										\
1702 	"\n"																													\
1703 	"Test Case #2: Resolve a domain name with\n"																			\
1704 	"\n"																													\
1705 	"    2 CNAME records, 4 A records, and 4 AAAA records\n"																\
1706 	"\n"																													\
1707 	"to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n"		\
1708 	"requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n"		\
1709 	"which should ideally require no additional server queries, i.e., the results should come from the cache.\n"			\
1710 	"\n"																													\
1711 	"Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n"		\
1712 	"records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n"		\
1713 	"domain name in the preliminary iteration isn't counted in the performance stats.\n"									\
1714 	"\n"																													\
1715 	"Test Case #3: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1716 
1717 #define kGAIPerfSectionText_TestSuiteAdvanced																				\
1718 	"This test suite consists of 33 test cases. Test cases 1 through 32 can be described in the following way\n"			\
1719 	"\n"																													\
1720 	"Test Case #N (where N is in [1, 32] and odd): Resolve a domain name with\n"											\
1721 	"\n"																													\
1722 	"    N_c CNAME records, N_a A records, and N_a AAAA records\n"															\
1723 	"\n"																													\
1724 	"to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n"		\
1725 	"server queries.\n"																										\
1726 	"\n"																													\
1727 	"Test Case #N (where N is in [1, 32] and even): Resolve a domain name with\n"											\
1728 	"\n"																													\
1729 	"    N_c CNAME records, N_a A records, and N_a AAAA records\n"															\
1730 	"\n"																													\
1731 	"to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n"		\
1732 	"requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n"		\
1733 	"which should ideally require no additional server queries, i.e., the results should come from the cache.\n"			\
1734 	"\n"																													\
1735 	"Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n"		\
1736 	"records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n"		\
1737 	"domain name in the preliminary iteration isn't counted in the performance stats.\n"									\
1738 	"\n"																													\
1739 	"N_c and N_a take on the following values, depending on the value of N:\n"												\
1740 	"\n"																													\
1741 	"    N_c is 0 if N is in [1, 8].\n"																						\
1742 	"    N_c is 1 if N is in [9, 16].\n"																					\
1743 	"    N_c is 2 if N is in [17, 24].\n"																					\
1744 	"    N_c is 4 if N is in [25, 32].\n"																					\
1745 	"\n"																													\
1746 	"    N_a is 1 if N mod 8 is 1 or 2.\n"																					\
1747 	"    N_a is 2 if N mod 8 is 3 or 4.\n"																					\
1748 	"    N_a is 4 if N mod 8 is 5 or 6.\n"																					\
1749 	"    N_a is 8 if N mod 8 is 7 or 0.\n"																					\
1750 	"\n"																													\
1751 	"Finally,\n"																											\
1752 	"\n"																													\
1753 	"Test Case #33: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n"
1754 
1755 static CLIOption		kGAIPerfOpts[] =
1756 {
1757 	StringOptionEx( 's', "suite",         &gGAIPerf_TestSuite,            "name", "Name of the predefined test suite to run.", true,
1758 		"\n"
1759 		"There are currently two predefined test suites, '" kGAIPerfTestSuiteName_Basic "' and '" kGAIPerfTestSuiteName_Advanced "', which are described below.\n"
1760 		"\n"
1761 	),
1762 	StringOption(   'o', "output",        &gGAIPerf_OutputFilePath,       "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1763 	FormatOption(   'f', "format",        &gGAIPerf_OutputFormat,         "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1764 	BooleanOption(  'n', "appendNewline", &gGAIPerf_OutputAppendNewline,  "If the output format is JSON, output a trailing newline character." ),
1765 	IntegerOption(  'i', "iterations",    &gGAIPerf_IterationCount,       "count", "The number of iterations per test case. (default: 100)", false ),
1766 	IntegerOption(  'l', "timeLimit",     &gGAIPerf_IterationTimeLimitMs, "ms", "Time limit for each DNSServiceGetAddrInfo() operation in milliseconds. (default: 100)", false ),
1767 	IntegerOption(   0 , "callDelay",     &gGAIPerf_CallDelayMs,          "ms", "Time to wait before calling DNSServiceGetAddrInfo() in milliseconds. (default: 10)", false ),
1768 	BooleanOption(   0 , "skipPathEval",  &gGAIPerf_SkipPathEvalulation,  "Use kDNSServiceFlagsPathEvaluationDone when calling DNSServiceGetAddrInfo()." ),
1769 
1770 	CLI_OPTION_GROUP( "DNS Server Options" ),
1771 	IntegerOption(   0 , "responseDelay", &gGAIPerf_ServerDelayMs,        "ms", "Additional delay in milliseconds to have the server apply to responses. (default: 10)", false ),
1772 	BooleanOption(   0 , "badUDPMode",    &gGAIPerf_BadUDPMode,           "Run server in Bad UDP mode to trigger mDNSResponder's TCP fallback mechanism." ),
1773 
1774 	CLI_SECTION( "Test Suite \"Basic\"",	kGAIPerfSectionText_TestSuiteBasic ),
1775 	CLI_SECTION( "Test Suite \"Advanced\"",	kGAIPerfSectionText_TestSuiteAdvanced ),
1776 	TestExitStatusSection(),
1777 	CLI_OPTION_END()
1778 };
1779 
1780 static void	MDNSDiscoveryTestCmd( void );
1781 
1782 static int				gMDNSDiscoveryTest_InstanceCount		= 100;
1783 static int				gMDNSDiscoveryTest_TXTSize				= 100;
1784 static int				gMDNSDiscoveryTest_BrowseTimeSecs		= 2;
1785 static int				gMDNSDiscoveryTest_FlushCache			= false;
1786 static char *			gMDNSDiscoveryTest_Interface			= NULL;
1787 static int				gMDNSDiscoveryTest_NoAdditionals		= false;
1788 static int				gMDNSDiscoveryTest_RecordCountA			= 1;
1789 static int				gMDNSDiscoveryTest_RecordCountAAAA		= 1;
1790 static double			gMDNSDiscoveryTest_UnicastDropRate		= 0.0;
1791 static double			gMDNSDiscoveryTest_MulticastDropRate	= 0.0;
1792 static int				gMDNSDiscoveryTest_MaxDropCount			= 0;
1793 static int				gMDNSDiscoveryTest_UseIPv4				= false;
1794 static int				gMDNSDiscoveryTest_UseIPv6				= false;
1795 #if( MDNSRESPONDER_PROJECT )
1796 static int				gMDNSDiscoveryTest_UseNewGAI			= false;
1797 #endif
1798 static const char *		gMDNSDiscoveryTest_OutputFormat			= kOutputFormatStr_JSON;
1799 static int				gMDNSDiscoveryTest_OutputAppendNewline	= false;
1800 static const char *		gMDNSDiscoveryTest_OutputFilePath		= NULL;
1801 
1802 static CLIOption		kMDNSDiscoveryTestOpts[] =
1803 {
1804 	IntegerOption( 'c', "instanceCount",  &gMDNSDiscoveryTest_InstanceCount,       "count", "Number of service instances to discover. (default: 100)", false ),
1805 	IntegerOption( 's', "txtSize",        &gMDNSDiscoveryTest_TXTSize,             "bytes", "Desired size of each service instance's TXT record data. (default: 100)", false ),
1806 	IntegerOption( 'b', "browseTime",     &gMDNSDiscoveryTest_BrowseTimeSecs,      "seconds", "Amount of time to spend browsing in seconds. (default: 2)", false ),
1807 	BooleanOption(  0 , "flushCache",     &gMDNSDiscoveryTest_FlushCache,          "Flush mDNSResponder's record cache before browsing. Requires root privileges." ),
1808 
1809 	CLI_OPTION_GROUP( "mDNS Replier Parameters" ),
1810 	StringOption(  'i', "interface",      &gMDNSDiscoveryTest_Interface,           "name or index", "Network interface. If unspecified, any available mDNS-capable interface will be used.", false ),
1811 	BooleanOption(  0 , "noAdditionals",  &gMDNSDiscoveryTest_NoAdditionals,       "When answering queries, don't include any additional records." ),
1812 	IntegerOption(  0 , "countA",         &gMDNSDiscoveryTest_RecordCountA,        "count", "Number of A records per hostname. (default: 1)", false ),
1813 	IntegerOption(  0 , "countAAAA",      &gMDNSDiscoveryTest_RecordCountAAAA,     "count", "Number of AAAA records per hostname. (default: 1)", false ),
1814 	DoubleOption(   0 , "udrop",          &gMDNSDiscoveryTest_UnicastDropRate,     "probability", "Probability of dropping a unicast response. (default: 0.0)", false ),
1815 	DoubleOption(   0 , "mdrop",          &gMDNSDiscoveryTest_MulticastDropRate,   "probability", "Probability of dropping a multicast query or response. (default: 0.0)", false ),
1816 	IntegerOption(  0 , "maxDropCount",   &gMDNSDiscoveryTest_MaxDropCount,        "count", "If > 0, drop probabilities are limted to first <count> responses from each instance. (default: 0)", false ),
1817 	BooleanOption(  0 , "ipv4",           &gMDNSDiscoveryTest_UseIPv4,             "Use IPv4." ),
1818 	BooleanOption(  0 , "ipv6",           &gMDNSDiscoveryTest_UseIPv6,             "Use IPv6." ),
1819 #if( MDNSRESPONDER_PROJECT )
1820 	BooleanOption(  0 , "useNewGAI",      &gMDNSDiscoveryTest_UseNewGAI,           "Use dnssd_getaddrinfo_* instead of DNSServiceGetAddrInfo()." ),
1821 #endif
1822 
1823 	CLI_OPTION_GROUP( "Results" ),
1824 	FormatOption(   'f', "format",        &gMDNSDiscoveryTest_OutputFormat,        "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1825 	StringOption(   'o', "output",        &gMDNSDiscoveryTest_OutputFilePath,      "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1826 
1827 	TestExitStatusSection(),
1828 	CLI_OPTION_END()
1829 };
1830 
1831 static void	DotLocalTestCmd( void );
1832 
1833 static const char *		gDotLocalTest_Interface			= NULL;
1834 static const char *		gDotLocalTest_OutputFormat		= kOutputFormatStr_JSON;
1835 static const char *		gDotLocalTest_OutputFilePath	= NULL;
1836 
1837 #define kDotLocalTestSubtestDesc_GAIMDNSOnly	"GAI for a dotlocal name that has only MDNS A and AAAA records."
1838 #define kDotLocalTestSubtestDesc_GAIDNSOnly		"GAI for a dotlocal name that has only DNS A and AAAA records."
1839 #define kDotLocalTestSubtestDesc_GAIBoth		"GAI for a dotlocal name that has both mDNS and DNS A and AAAA records."
1840 #define kDotLocalTestSubtestDesc_GAINeither		"GAI for a dotlocal name that has no A or AAAA records."
1841 #define kDotLocalTestSubtestDesc_GAINoSuchRecord \
1842 	"GAI for a dotlocal name that has no A or AAAA records, but is a subdomain name of a search domain."
1843 #define kDotLocalTestSubtestDesc_QuerySRV		"SRV query for a dotlocal name that has only a DNS SRV record."
1844 
1845 #define kDotLocalTestSectionText_Description																				\
1846 	"The goal of the dotlocal test is to verify that mDNSResponder properly handles queries for domain names in the\n"		\
1847 	"local domain when a local SOA record exists. As part of the test setup, a test DNS server and an mdnsreplier are\n"	\
1848 	"spawned, and a dummy local SOA record is registered with DNSServiceRegisterRecord(). The server is invoked such\n"		\
1849 	"that its domain is a second-level subdomain of the local domain, i.e., <some label>.local, while the mdnsreplier is\n"	\
1850 	"invoked such that its base hostname is equal to the server's domain, e.g., if the server's domain is test.local.,\n"	\
1851 	"then the mdnsreplier's base hostname is test.local.\n"																	\
1852 	"\n"																													\
1853 	"The dotlocal test consists of six subtests that perform either a DNSServiceGetAddrInfo (GAI) operation for a\n"		\
1854 	"hostname in the local domain or a DNSServiceQueryRecord operation to query for an SRV record in the local domain:\n"	\
1855 	"\n"																													\
1856 	"1. " kDotLocalTestSubtestDesc_GAIMDNSOnly		"\n"																	\
1857 	"2. " kDotLocalTestSubtestDesc_GAIDNSOnly		"\n"																	\
1858 	"3. " kDotLocalTestSubtestDesc_GAIBoth			"\n"																	\
1859 	"4. " kDotLocalTestSubtestDesc_GAINeither		"\n"																	\
1860 	"5. " kDotLocalTestSubtestDesc_GAINoSuchRecord	"\n"																	\
1861 	"6. " kDotLocalTestSubtestDesc_QuerySRV			"\n"																	\
1862 	"\n"																													\
1863 	"Each subtest runs for five seconds.\n"
1864 
1865 static CLIOption		kDotLocalTestOpts[] =
1866 {
1867 	StringOption(  'i', "interface",     &gDotLocalTest_Interface,           "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1868 
1869 	CLI_OPTION_GROUP( "Results" ),
1870 	FormatOption(  'f', "format",        &gDotLocalTest_OutputFormat,        "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1871 	StringOption(  'o', "output",        &gDotLocalTest_OutputFilePath,      "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1872 
1873 	CLI_SECTION( "Description", kDotLocalTestSectionText_Description ),
1874 	TestExitStatusSection(),
1875 	CLI_OPTION_END()
1876 };
1877 
1878 static void	ProbeConflictTestCmd( void );
1879 
1880 static const char *		gProbeConflictTest_Interface		= NULL;
1881 static int				gProbeConflictTest_UseComputerName	= false;
1882 static int				gProbeConflictTest_UseIPv4			= false;
1883 static int				gProbeConflictTest_UseIPv6			= false;
1884 static const char *		gProbeConflictTest_OutputFormat		= kOutputFormatStr_JSON;
1885 static const char *		gProbeConflictTest_OutputFilePath	= NULL;
1886 
1887 static CLIOption		kProbeConflictTestOpts[] =
1888 {
1889 	StringOption(  'i', "interface",       &gProbeConflictTest_Interface,       "name or index", "mdnsreplier's network interface. If not set, any mDNS-capable interface will be used.", false ),
1890 	BooleanOption( 'c', "useComputerName", &gProbeConflictTest_UseComputerName, "Use the device's \"computer name\" for the test service's name." ),
1891 	BooleanOption(  0 , "ipv4",            &gProbeConflictTest_UseIPv4,         "Use IPv4 instead of IPv6. (Default behavior.)" ),
1892 	BooleanOption(  0 , "ipv6",            &gProbeConflictTest_UseIPv6,         "Use IPv6 instead of IPv4." ),
1893 
1894 	CLI_OPTION_GROUP( "Results" ),
1895 	FormatOption(  'f', "format",          &gProbeConflictTest_OutputFormat,    "Specifies the test report output format. (default: " kOutputFormatStr_JSON ")", false ),
1896 	StringOption(  'o', "output",          &gProbeConflictTest_OutputFilePath,  "path", "Path of the file to write test report to instead of standard output (stdout).", false ),
1897 
1898 	TestExitStatusSection(),
1899 	CLI_OPTION_END()
1900 };
1901 
1902 static void	RegistrationTestCmd( void );
1903 
1904 static int				gRegistrationTest_BATSEnvironment	= false;
1905 static const char *		gRegistrationTest_OutputFormat		= kOutputFormatStr_JSON;
1906 static const char *		gRegistrationTest_OutputFilePath	= NULL;
1907 
1908 static CLIOption		kRegistrationTestOpts[] =
1909 {
1910 	CLI_OPTION_BOOLEAN( 0, "bats", &gRegistrationTest_BATSEnvironment, "Informs the test that it's running in a BATS environment.",
1911 		"\n"
1912 		"This option allows the test to take special measures while running in a BATS environment. Currently, this option\n"
1913 		"only has an effect on watchOS. Because it has been observed that the Wi-Fi interface sometimes goes down during\n"
1914 		"watchOS BATS testing, for watchOS, when a service is registered using kDNSServiceInterfaceIndexAny,\n"
1915 		"\n"
1916 		"    1. missing browse and query \"add\" results for Wi-Fi interfaces aren't enough for a subtest to fail; and\n"
1917 		"    2. unexpected browse and query results for Wi-Fi interfaces are ignored.\n"
1918 	),
1919 	CLI_OPTION_GROUP( "Results" ),
1920 	FormatOption( 'f', "format", &gRegistrationTest_OutputFormat,   "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1921 	StringOption( 'o', "output", &gRegistrationTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1922 
1923 	TestExitStatusSection(),
1924 	CLI_OPTION_END()
1925 };
1926 
1927 #if( MDNSRESPONDER_PROJECT )
1928 static void	FallbackTestCmd( void );
1929 
1930 static int				gFallbackTest_UseRefused		= false;
1931 static const char *		gFallbackTest_OutputFormat		= kOutputFormatStr_JSON;
1932 static const char *		gFallbackTest_OutputFilePath	= NULL;
1933 
1934 static CLIOption		kFallbackTestOpts[] =
1935 {
1936 	BooleanOption( 0 , "useRefused", &gFallbackTest_UseRefused,     "Have the server use the Refused RCODE in responses when a query is not allowed to be answered." ),
1937 	CLI_OPTION_GROUP( "Results" ),
1938 	FormatOption( 'f', "format",     &gFallbackTest_OutputFormat,   "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1939 	StringOption( 'o', "output",     &gFallbackTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1940 
1941 	TestExitStatusSection(),
1942 	CLI_OPTION_END()
1943 };
1944 
1945 static void ExpensiveConstrainedTestCmd( void );
1946 
1947 static const char *     gExpensiveConstrainedTest_Interface                 = NULL;
1948 static const char *     gExpensiveConstrainedTest_Name                      = NULL;
1949 static Boolean          gExpensiveConstrainedTest_DenyExpensive             = false;
1950 static Boolean          gExpensiveConstrainedTest_DenyConstrained           = false;
1951 static Boolean          gExpensiveConstrainedTest_StartFromExpensive        = false;
1952 static int              gExpensiveConstrainedTest_ProtocolIPv4              = false;
1953 static int              gExpensiveConstrainedTest_ProtocolIPv6              = false;
1954 static const char *     gExpensiveConstrainedTest_OutputFormat              = kOutputFormatStr_JSON;
1955 static const char *     gExpensiveConstrainedTest_OutputFilePath            = NULL;
1956 
1957 static CLIOption        kExpensiveConstrainedTestOpts[] =
1958 {
1959     CLI_OPTION_GROUP( "Results" ),
1960     FormatOption( 'f', "format", &gExpensiveConstrainedTest_OutputFormat,              "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1961     StringOption( 'o', "output", &gExpensiveConstrainedTest_OutputFilePath, "path",    "Path of the file to write test results to instead of standard output (stdout).", false ),
1962 
1963     TestExitStatusSection(),
1964     CLI_OPTION_END()
1965 };
1966 
1967 static void	DNSProxyTestCmd( void );
1968 
1969 static const char *		gDNSProxyTest_OutputFormat		= kOutputFormatStr_JSON;
1970 static const char *		gDNSProxyTest_OutputFilePath	= NULL;
1971 
1972 static CLIOption		kDNSProxyTestOpts[] =
1973 {
1974 	CLI_OPTION_GROUP( "Results" ),
1975 	FormatOption( 'f', "format", &gDNSProxyTest_OutputFormat,   "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1976 	StringOption( 'o', "output", &gDNSProxyTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1977 
1978 	TestExitStatusSection(),
1979 	CLI_OPTION_END()
1980 };
1981 
1982 static void	RCodeTestCmd( void );
1983 
1984 static const char *		gRCodeTest_OutputFormat		= kOutputFormatStr_JSON;
1985 static const char *		gRCodeTest_OutputFilePath	= NULL;
1986 
1987 static CLIOption		kRCodeTestOpts[] =
1988 {
1989 	CLI_OPTION_GROUP( "Results" ),
1990 	FormatOption( 'f', "format", &gRCodeTest_OutputFormat,   "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
1991 	StringOption( 'o', "output", &gRCodeTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
1992 
1993 	TestExitStatusSection(),
1994 	CLI_OPTION_END()
1995 };
1996 
1997 static void XCTestCmd( void );
1998 
1999 static const char *     gXCTest_Classname        = NULL;
2000 
2001 static CLIOption        kXCTestOpts[] =
2002 {
2003     StringOption(      'c', "class", &gXCTest_Classname, "classname", "The classname of the XCTest to run (from /AppleInternal/XCTests/com.apple.mDNSResponder/Tests.xctest)", true ),
2004     CLI_OPTION_END()
2005 };
2006 
2007 static void MultiConnectTestCmd( void );
2008 
2009 static int    			gMultiConnectTest_ConnectionCount = 4; // default to 4
2010 
2011 static CLIOption        kMultiConnectTestOpts[] =
2012 {
2013 	IntegerOption( 0, "connections", &gMultiConnectTest_ConnectionCount,	"count", "Number of simultanious connections. (default: 4)", false ),
2014     CLI_OPTION_END()
2015 };
2016 #endif	// MDNSRESPONDER_PROJECT
2017 
2018 #if( TARGET_OS_DARWIN )
2019 static void	KeepAliveTestCmd( void );
2020 
2021 static const char *		gKeepAliveTest_OutputFormat		= kOutputFormatStr_JSON;
2022 static const char *		gKeepAliveTest_OutputFilePath	= NULL;
2023 
2024 static CLIOption		kKeepAliveTestOpts[] =
2025 {
2026 	CLI_OPTION_GROUP( "Results" ),
2027 	FormatOption( 'f', "format", &gKeepAliveTest_OutputFormat,   "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
2028 	StringOption( 'o', "output", &gKeepAliveTest_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ),
2029 
2030 	TestExitStatusSection(),
2031 	CLI_OPTION_END()
2032 };
2033 #endif	// TARGET_OS_DARWIN
2034 
2035 static void DNSSECTestCmd( void );
2036 
2037 static const char * gDNSSECTest_TestCaseName	= NULL;
2038 #if ( ENABLE_DNSSDUTIL_DNSSEC_TEST == 1 )
2039 static const char * gDNSSECTest_OutputFormat	= kOutputFormatStr_JSON;
2040 static const char * gDNSSECTest_OutputFilePath	= NULL;
2041 #endif
2042 
2043 static CLIOption	kDNSSECTestOpts[] =
2044 {
2045 	StringOption( 'n', "testCaseName", &gDNSSECTest_TestCaseName,                  "Specifies the DNSSEC test that the user intends to run", "test name", true ),
2046 
2047 	CLI_OPTION_GROUP( "Results" ),
2048 	FormatOption( 'f', "format",       &gExpensiveConstrainedTest_OutputFormat,    "Specifies the test results output format. (default: " kOutputFormatStr_JSON ")", false ),
2049 	StringOption( 'o', "output",       &gExpensiveConstrainedTest_OutputFilePath,  "path",    "Path of the file to write test results to instead of standard output (stdout).", false ),
2050 
2051 	TestExitStatusSection(),
2052 	CLI_OPTION_END()
2053 };
2054 
2055 static CLIOption		kTestOpts[] =
2056 {
2057 	Command( "gaiperf",        GAIPerfCmd,           kGAIPerfOpts,            "Runs DNSServiceGetAddrInfo() performance tests.", false ),
2058 	Command( "mdnsdiscovery",  MDNSDiscoveryTestCmd, kMDNSDiscoveryTestOpts,  "Tests mDNS service discovery for correctness.", false ),
2059 	Command( "dotlocal",       DotLocalTestCmd,      kDotLocalTestOpts,       "Tests DNS and mDNS queries for domain names in the local domain.", false ),
2060 	Command( "probeconflicts", ProbeConflictTestCmd, kProbeConflictTestOpts,  "Tests various probing conflict scenarios.", false ),
2061 	Command( "registration",   RegistrationTestCmd,  kRegistrationTestOpts,   "Tests service registrations.", false ),
2062 #if( MDNSRESPONDER_PROJECT )
2063 	Command( "fallback",       FallbackTestCmd,      kFallbackTestOpts,       "Tests DNS server fallback.", false ),
2064     Command( "expensive_constrained_updates", ExpensiveConstrainedTestCmd, kExpensiveConstrainedTestOpts, "Tests if the mDNSResponder can handle expensive and constrained property change correctly", false),
2065 	Command( "dnsproxy",       DNSProxyTestCmd,      kDNSProxyTestOpts,       "Tests mDNSResponder's DNS proxy.", false ),
2066 	Command( "rcodes",         RCodeTestCmd,         kRCodeTestOpts,         "Tests handling of all DNS RCODEs.", false ),
2067     Command( "xctest",         XCTestCmd,            kXCTestOpts,			  "Run a XCTest from /AppleInternal/XCTests/com.apple.mDNSResponder/Tests.xctest.", true ),
2068 	Command( "multiconnect",   MultiConnectTestCmd,	 kMultiConnectTestOpts,	  "Tests multiple simultanious connections.", false ),
2069 #endif
2070 #if( TARGET_OS_DARWIN )
2071 	Command( "keepalive",      KeepAliveTestCmd,     kKeepAliveTestOpts,      "Tests keepalive record registrations.", false ),
2072 #endif
2073 	Command( "dnssec",         DNSSECTestCmd,        kDNSSECTestOpts,         "Tests mDNSResponder's DNSSEC validation", false),
2074 	CLI_OPTION_END()
2075 };
2076 
2077 //===========================================================================================================================
2078 //	SSDP Command Options
2079 //===========================================================================================================================
2080 
2081 static int				gSSDPDiscover_MX			= 1;
2082 static const char *		gSSDPDiscover_ST			= "ssdp:all";
2083 static int				gSSDPDiscover_ReceiveSecs	= 1;
2084 static int				gSSDPDiscover_UseIPv4		= false;
2085 static int				gSSDPDiscover_UseIPv6		= false;
2086 static int				gSSDPDiscover_Verbose		= false;
2087 
2088 static CLIOption		kSSDPDiscoverOpts[] =
2089 {
2090 	StringOption(  'i', "interface",	&gInterface,				"name or index", "Network interface by name or index.", true ),
2091 	IntegerOption( 'm', "mx",			&gSSDPDiscover_MX,			"seconds", "MX value in search request, i.e., max response delay in seconds. (Default: 1 second)", false ),
2092 	StringOption(  's', "st",			&gSSDPDiscover_ST,			"string", "ST value in search request, i.e., the search target. (Default: \"ssdp:all\")", false ),
2093 	IntegerOption( 'r', "receiveTime",	&gSSDPDiscover_ReceiveSecs,	"seconds", "Amount of time to spend receiving responses. -1 means unlimited. (Default: 1 second)", false ),
2094 	BooleanOption(  0 , "ipv4",			&gSSDPDiscover_UseIPv4,		"Use IPv4, i.e., multicast to 239.255.255.250:1900." ),
2095 	BooleanOption(  0 , "ipv6",			&gSSDPDiscover_UseIPv6,		"Use IPv6, i.e., multicast to [ff02::c]:1900" ),
2096 	BooleanOption( 'v', "verbose",		&gSSDPDiscover_Verbose,		"Prints the search request(s) that were sent." ),
2097 	CLI_OPTION_END()
2098 };
2099 
2100 static void	SSDPDiscoverCmd( void );
2101 
2102 static CLIOption		kSSDPOpts[] =
2103 {
2104 	Command( "discover", SSDPDiscoverCmd, kSSDPDiscoverOpts, "Crafts and multicasts an SSDP search message.", false ),
2105 	CLI_OPTION_END()
2106 };
2107 
2108 #if( TARGET_OS_DARWIN )
2109 //===========================================================================================================================
2110 //	res_query Command Options
2111 //===========================================================================================================================
2112 
2113 static void	ResQueryCmd( void );
2114 
2115 static const char *		gResQuery_Name			= NULL;
2116 static const char *		gResQuery_Type			= NULL;
2117 static const char *		gResQuery_Class			= NULL;
2118 static int				gResQuery_UseLibInfo	= false;
2119 
2120 static CLIOption		kResQueryOpts[] =
2121 {
2122 	StringOption( 'n', "name",		&gResQuery_Name,		"domain name",	"Full domain name of record to query.", true ),
2123 	StringOption( 't', "type",		&gResQuery_Type,		"record type",	"Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
2124 	StringOption( 'c', "class",		&gResQuery_Class,		"record class",	"Record class by name or number. Default class is IN.", false ),
2125 	BooleanOption( 0 , "libinfo",	&gResQuery_UseLibInfo,	"Use res_query from libinfo instead of libresolv." ),
2126 	CLI_OPTION_END()
2127 };
2128 
2129 //===========================================================================================================================
2130 //	dns_query Command Options
2131 //===========================================================================================================================
2132 
2133 static void ResolvDNSQueryCmd( void );
2134 
2135 static const char *		gResolvDNSQuery_Name	= NULL;
2136 static const char *		gResolvDNSQuery_Type	= NULL;
2137 static const char *		gResolvDNSQuery_Class	= NULL;
2138 static const char *		gResolvDNSQuery_Path	= NULL;
2139 
2140 static CLIOption		kResolvDNSQueryOpts[] =
2141 {
2142 	StringOption( 'n', "name",	&gResolvDNSQuery_Name,	"domain name",	"Full domain name of record to query.", true ),
2143 	StringOption( 't', "type",	&gResolvDNSQuery_Type,	"record type",	"Record type by name (e.g., TXT, SRV, etc.) or number.", true ),
2144 	StringOption( 'c', "class",	&gResolvDNSQuery_Class,	"record class",	"Record class by name or number. Default class is IN.", false ),
2145 	StringOption( 'p', "path",	&gResolvDNSQuery_Path,	"file path",	"The path argument to pass to dns_open() before calling dns_query(). Default value is NULL.", false ),
2146 	CLI_OPTION_END()
2147 };
2148 
2149 //===========================================================================================================================
2150 //	CFHost Command Options
2151 //===========================================================================================================================
2152 
2153 static void	CFHostCmd( void );
2154 
2155 static const char *		gCFHost_Name		= NULL;
2156 static int				gCFHost_WaitSecs	= 0;
2157 
2158 static CLIOption		kCFHostOpts[] =
2159 {
2160 	StringOption(  'n', "name", &gCFHost_Name,     "hostname", "Hostname to resolve.", true ),
2161 	IntegerOption( 'w', "wait", &gCFHost_WaitSecs, "seconds",  "Time in seconds to wait before a normal exit. (default: 0)", false ),
2162 	CLI_OPTION_END()
2163 };
2164 
2165 static CLIOption		kLegacyOpts[] =
2166 {
2167 	Command( "res_query", ResQueryCmd,       kResQueryOpts,       "Uses res_query() from either libresolv or libinfo to query for a record.", true ),
2168 	Command( "dns_query", ResolvDNSQueryCmd, kResolvDNSQueryOpts, "Uses dns_query() from libresolv to query for a record.", true ),
2169 	Command( "cfhost",    CFHostCmd,         kCFHostOpts,         "Uses CFHost to resolve a hostname.", true ),
2170 	CLI_OPTION_END()
2171 };
2172 
2173 //===========================================================================================================================
2174 //	DNSConfigAdd Command Options
2175 //===========================================================================================================================
2176 
2177 static void	DNSConfigAddCmd( void );
2178 
2179 static CFStringRef		gDNSConfigAdd_ID			= NULL;
2180 static char **			gDNSConfigAdd_IPAddrArray	= NULL;
2181 static size_t			gDNSConfigAdd_IPAddrCount	= 0;
2182 static char **			gDNSConfigAdd_DomainArray	= NULL;
2183 static size_t			gDNSConfigAdd_DomainCount	= 0;
2184 static const char *		gDNSConfigAdd_Interface		= NULL;
2185 static int				gDNSConfigAdd_SearchOrder	= -1;
2186 
2187 static CLIOption		kDNSConfigAddOpts[] =
2188 {
2189 	CFStringOption(     0 , "id",          &gDNSConfigAdd_ID,                                      "ID", "Arbitrary ID to use for resolver entry.", true ),
2190 	MultiStringOption( 'a', "address",     &gDNSConfigAdd_IPAddrArray, &gDNSConfigAdd_IPAddrCount, "IP address", "DNS server IP address(es). Can be specified more than once.", true ),
2191 	MultiStringOption( 'd', "domain",      &gDNSConfigAdd_DomainArray, &gDNSConfigAdd_DomainCount, "domain", "Specific domain(s) for the resolver entry. Can be specified more than once.", false ),
2192 	StringOption(      'i', "interface",   &gDNSConfigAdd_Interface,                               "interface name", "Specific interface for the resolver entry.", false ),
2193 	IntegerOption(     'o', "searchOrder", &gDNSConfigAdd_SearchOrder,                             "integer", "Resolver entry's search order. Will only be set for values >= 0. (default: -1)", false ),
2194 
2195 	CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
2196 	CLI_OPTION_END()
2197 };
2198 
2199 //===========================================================================================================================
2200 //	DNSConfigRemove Command Options
2201 //===========================================================================================================================
2202 
2203 static void	DNSConfigRemoveCmd( void );
2204 
2205 static CFStringRef		gDNSConfigRemove_ID = NULL;
2206 
2207 static CLIOption		kDNSConfigRemoveOpts[] =
2208 {
2209 	CFStringOption( 0, "id", &gDNSConfigRemove_ID, "ID", "ID of resolver entry to remove.", true ),
2210 
2211 	CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
2212 	CLI_OPTION_END()
2213 };
2214 
2215 static CLIOption		kDNSConfigOpts[] =
2216 {
2217 	Command( "add",    DNSConfigAddCmd,    kDNSConfigAddOpts,    "Add a supplemental resolver entry to the system's DNS configuration.", true ),
2218 	Command( "remove", DNSConfigRemoveCmd, kDNSConfigRemoveOpts, "Remove a supplemental resolver entry from the system's DNS configuration.", true ),
2219 	CLI_OPTION_END()
2220 };
2221 
2222 //===========================================================================================================================
2223 //	XPCSend
2224 //===========================================================================================================================
2225 
2226 static void	XPCSendCmd( void );
2227 
2228 static const char *		gXPCSend_ServiceName	= NULL;
2229 static const char *		gXPCSend_MessageStr		= NULL;
2230 
2231 static const char		kXPCSendMessageSection_Name[] = "Message Argument";
2232 static const char		kXPCSendMessageSection_Text[] =
2233 	"XPC messages are described as a string using the following syntax.\n"
2234 	"\n"
2235 	"With the exception of the top-most XPC message dictionary, dictionaries begin with a '{' and end with a '}'.\n"
2236 	"Key-value pairs are of the form <key>=<value>, where <key> is a string and <value> is a value of any of the\n"
2237 	"currently supported XPC types.\n"
2238 	"\n"
2239 	"Arrays begin with a '[' and end with a ']'.\n"
2240 	"\n"
2241 	"The following non-container XPC types are supported:\n"
2242 	"\n"
2243 	"Type                              Syntax                      Example\n"
2244 	"bool                              bool:<string>               bool:true (or yes/y/on/1), bool:false (or no/n/off/0)\n"
2245 	"data                              data:<hex string>           data:C0000201\n"
2246 	"int64  (signed 64-bit integer)    int:<pos. or neg. integer>  int:-1\n"
2247 	"string                            string:<string>             string:hello\\ world\n"
2248 	"uint64 (unsigned 64-bit integer)  uint:<pos. integer>         uint:1024 or uint:0x400\n"
2249 	"UUID                              uuid:<UUID>                 uuid:dab10183-84b5-4859-9de6-4bee287cfea3\n"
2250 	"\n"
2251 	"Example 1: 'cmd=string:add make=string:Apple model=string:Macintosh aliases=[string:Mac string:Macintosh\\ 128K]'\n"
2252 	"Example 2: 'cmd=string:search features={portable=bool:yes solar=bool:no} priceMin=uint:100 priceMax=uint:200'\n";
2253 
2254 static CLIOption		kXPCSendOpts[] =
2255 {
2256 	StringOption( 's', "service", &gXPCSend_ServiceName, "service name", "XPC service name.", true ),
2257 	StringOption( 'm', "message", &gXPCSend_MessageStr,  "message",      "XPC message as a string.", true ),
2258 
2259 	CLI_SECTION( kXPCSendMessageSection_Name, kXPCSendMessageSection_Text ),
2260 	CLI_OPTION_END()
2261 };
2262 #endif	// TARGET_OS_DARWIN
2263 
2264 #if( MDNSRESPONDER_PROJECT )
2265 //===========================================================================================================================
2266 //	InterfaceMonitor Command Options
2267 //===========================================================================================================================
2268 
2269 static void InterfaceMonitorCmd( void );
2270 
2271 static CLIOption		kInterfaceMonitorOpts[] =
2272 {
2273 	StringOption( 'i', "interface", &gInterface, "name or index", "Network interface by name or index.", true ),
2274 	CLI_OPTION_END()
2275 };
2276 
2277 //===========================================================================================================================
2278 //	Querier Command Options
2279 //===========================================================================================================================
2280 
2281 #define kMDNSResolverTypeStr_Normal		"normal"
2282 #define kMDNSResolverTypeStr_TCPOnly	"tcp"
2283 #define kMDNSResolverTypeStr_TLS		"tls"
2284 #define kMDNSResolverTypeStr_HTTPS		"https"
2285 
2286 static const char *		gQuerier_Name				= NULL;
2287 static const char *		gQuerier_Type				= "A";
2288 static const char *		gQuerier_Class				= "IN";
2289 static const char *		gQuerier_Delegator			= NULL;
2290 static int				gQuerier_DNSSECOK			= false;
2291 static int				gQuerier_CheckingDisabled	= false;
2292 static const char *		gQuerier_ResolverType		= NULL;
2293 static char **			gQuerier_ServerAddrs		= NULL;
2294 static size_t			gQuerier_ServerAddrCount	= 0;
2295 static const char *		gQuerier_ProviderName		= NULL;
2296 static const char *		gQuerier_URLPath			= NULL;
2297 static int				gQuerier_NoConnectionReuse	= false;
2298 static int				gQuerier_SquashCNAMEs		= false;
2299 
2300 static CLIOption		kQuerierOpts[] =
2301 {
2302 	StringOption( 'i', "interface",        &gInterface,                "name or index", "If specified, network traffic is scoped to this interface.", false ),
2303 	StringOption( 'n', "name",             &gQuerier_Name,             "name", "Question name (QNAME).", true ),
2304 	StringOption( 't', "type",             &gQuerier_Type,             "type", "Question type (QTYPE). (default: A)", false ),
2305 	StringOption( 'c', "class",            &gQuerier_Class,            "class", "Question class (QCLASS). (default: IN)", false ),
2306 	StringOption(  0 , "delegator",        &gQuerier_Delegator,        "PID|UUID", "Delegator's PID or UUID.", false ),
2307 	BooleanOption( 0 , "dnssec",           &gQuerier_DNSSECOK,         "Have queries include an OPT record with the DNSSEC OK (DO) bit set." ),
2308 	BooleanOption( 0 , "checkingDisabled", &gQuerier_CheckingDisabled, "Set the Checking Disabled (CD) bit in queries." ),
2309 
2310 	CLI_OPTION_GROUP( "Resolver-Specific Options" ),
2311 	StringOptionEx( 'r', "resolverType", &gQuerier_ResolverType, "resolver type", "Specifies the type of resolver to use.", false,
2312 		"\n"
2313 		"Use '" kMDNSResolverTypeStr_Normal  "' for DNS over UDP and TCP.\n"
2314 		"Use '" kMDNSResolverTypeStr_TCPOnly "' for DNS over TCP.\n"
2315 		"Use '" kMDNSResolverTypeStr_TLS     "' for DNS over TLS.\n"
2316 		"Use '" kMDNSResolverTypeStr_HTTPS   "' for DNS over HTTPS.\n"
2317 		"\n"
2318 		"If no resolver type is specified, an mdns_dns_service_manager will be used to determine which DNS service to use\n"
2319 		"based on the system's DNS settings. In this case, the other resolver-specific options will be ignored.\n"
2320 		"\n"
2321 	),
2322 	MultiStringOptionEx( 's', "server", &gQuerier_ServerAddrs, &gQuerier_ServerAddrCount, "IP address", "Server's IPv4 or IPv6 address with optionally-specified port.", false,
2323 		"\n"
2324 		"Use this option one or more times to specify a DNS service's server(s) by IP address.\n"
2325 		"\n"
2326 		"If no server IP addresses are specified for DNS over TLS/HTTPS resolvers, then connections to the DNS service\n"
2327 		"will use the specified provider name as the DNS service's hostname.\n"
2328 		"\n"
2329 	),
2330 	StringOption( 'p', "providerName",      &gQuerier_ProviderName,      "domain name", "Provider's domain name for DNS over TLS/HTTPS.", false ),
2331 	StringOption( 'q', "urlPath",           &gQuerier_URLPath,           "path", "URL path for DNS over HTTPS.", false ),
2332 	BooleanOption( 0 , "noConnectionReuse", &gQuerier_NoConnectionReuse, "Disable connection reuse." ),
2333 	BooleanOption( 0 , "squashCNAMEs",      &gQuerier_SquashCNAMEs,      "Squash CNAME chains in responses." ),
2334 	CLI_OPTION_END()
2335 };
2336 
2337 static void QuerierCommand( void );
2338 
2339 //===========================================================================================================================
2340 //	DNSProxy Command Options
2341 //===========================================================================================================================
2342 
2343 static void DNSProxyCmd( void );
2344 
2345 static char **			gDNSProxy_InputInterfaces		= NULL;
2346 static size_t			gDNSProxy_InputInterfaceCount	= 0;
2347 static const char *		gDNSProxy_OutputInterface		= NULL;
2348 static const char *		gDNSProxy_DNS64IPv6Prefix		= NULL;
2349 
2350 static CLIOption		kDNSProxyOpts[] =
2351 {
2352 	MultiStringOption( 'i', "inputInterface",  &gDNSProxy_InputInterfaces, &gDNSProxy_InputInterfaceCount, "name or index", "Interface to accept queries on. Can be specified more than once.", true ),
2353 	StringOption(      'o', "outputInterface", &gDNSProxy_OutputInterface, "name or index", "Interface to forward queries over. Use '0' for primary interface. (default: 0)", false ),
2354 	StringOption(      'p', "dns64Prefix",     &gDNSProxy_DNS64IPv6Prefix, "IPv6 prefix", "IPv6 prefix to use for DNS64 AAAA record synthesis.", false ),
2355 	CLI_OPTION_END()
2356 };
2357 
2358 //===========================================================================================================================
2359 //	GetAddrInfoNew Command Options
2360 //===========================================================================================================================
2361 
2362 static const char *		gGAINew_Hostname		= NULL;
2363 static const char *		gGAINew_DelegatorID		= NULL;
2364 static const char *		gGAINew_ServiceScheme	= NULL;
2365 static const char *		gGAINew_AccountID		= NULL;
2366 static int				gGAINew_ProtocolIPv4	= false;
2367 static int				gGAINew_ProtocolIPv6	= false;
2368 static int				gGAINew_WantAuthTags	= false;
2369 static int				gGAINew_OneShot			= false;
2370 static int				gGAINew_TimeLimitSecs	= 0;
2371 static const char *		gGAINew_QoS				= NULL;
2372 
2373 #define kQoSTypeStr_Unspecified			"unspecified"
2374 #define kQoSTypeStr_Background			"background"
2375 #define kQoSTypeStr_Utility				"utility"
2376 #define kQoSTypeStr_Default				"default"
2377 #define kQoSTypeStr_UserInitiated		"userInitiated"
2378 #define kQoSTypeStr_UserInteractive		"userInteractive"
2379 
2380 #define kQoSArgShortName		"QoS class"
2381 
2382 static CLIOption		kGetAddrInfoNewOpts[] =
2383 {
2384 	InterfaceOption(),
2385 	StringOption(  'n', "name",          &gGAINew_Hostname,      "domain name", "Hostname to resolve.", true ),
2386 	StringOption(  'd', "delegate",      &gGAINew_DelegatorID,   "PID|UUID", "Delegator's PID or UUID. If PID p < 0, the audit token of PID |p| will be used.", false ),
2387 	StringOption(   0,  "accountID",     &gGAINew_AccountID,     "account ID", "Account ID string.", false ),
2388 	StringOption(   0,  "serviceScheme", &gGAINew_ServiceScheme, "scheme", "Service scheme such as '_443._https'.", false ),
2389 	BooleanOption(  0 , "ipv4",          &gGAINew_ProtocolIPv4,  "Use kDNSServiceProtocol_IPv4." ),
2390 	BooleanOption(  0 , "ipv6",          &gGAINew_ProtocolIPv6,  "Use kDNSServiceProtocol_IPv6." ),
2391 	BooleanOption( 'a', "wantAuthTags",  &gGAINew_WantAuthTags,  "Want authentication tags." ),
2392 
2393 	CLI_OPTION_GROUP( "Flags" ),
2394 	DNSSDFlagsOption(),
2395 	DNSSDFlagsOption_AllowExpiredAnswers(),
2396 	DNSSDFlagsOption_DenyCellular(),
2397 	DNSSDFlagsOption_DenyConstrained(),
2398 	DNSSDFlagsOption_DenyExpensive(),
2399 	DNSSDFlagsOption_IncludeAWDL(),
2400 	DNSSDFlagsOption_PathEvalDone(),
2401 	DNSSDFlagsOption_ReturnIntermediates(),
2402 	DNSSDFlagsOption_SuppressUnusable(),
2403 	DNSSDFlagsOption_Timeout(),
2404 
2405 	CLI_OPTION_GROUP( "Operation" ),
2406 	ConnectionOptions(),
2407 	BooleanOption(  'o', "oneshot",   &gGAINew_OneShot,       "Finish after first set of results." ),
2408 	IntegerOption(  'l', "timeLimit", &gGAINew_TimeLimitSecs, "seconds", "Time limit for dnssd_getaddrinfo operation. Use '0' for no limit. (default: 0)", false ),
2409 	StringOptionEx( 'q', "qos",       &gGAINew_QoS,           kQoSArgShortName, "Specifies the QoS of the queue used for the dnssd_getaddrinfo object.", false,
2410 		"\n"
2411 		"Use '" kQoSTypeStr_Unspecified     "' for QOS_CLASS_UNSPECIFIED.\n"
2412 		"Use '" kQoSTypeStr_Background      "' for QOS_CLASS_BACKGROUND.\n"
2413 		"Use '" kQoSTypeStr_Utility         "' for QOS_CLASS_UTILITY.\n"
2414 		"Use '" kQoSTypeStr_Default         "' for QOS_CLASS_DEFAULT.\n"
2415 		"Use '" kQoSTypeStr_UserInitiated   "' for QOS_CLASS_USER_INITIATED.\n"
2416 		"Use '" kQoSTypeStr_UserInteractive "' for QOS_CLASS_USER_INTERACTIVE.\n"
2417 		"\n"
2418 	),
2419 	CLI_OPTION_END()
2420 };
2421 
2422 static void	GetAddrInfoNewCommand( void );
2423 
2424 #endif	// MDNSRESPONDER_PROJECT
2425 
2426 //===========================================================================================================================
2427 //	Command Table
2428 //===========================================================================================================================
2429 
2430 static OSStatus	VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset );
2431 
2432 static void	BrowseCmd( void );
2433 static void	GetAddrInfoCmd( void );
2434 static void	QueryRecordCmd( void );
2435 static void	RegisterCmd( void );
2436 static void	RegisterRecordCmd( void );
2437 static void	ResolveCmd( void );
2438 static void	ReconfirmCmd( void );
2439 static void	GetAddrInfoPOSIXCmd( void );
2440 static void	ReverseLookupCmd( void );
2441 static void	PortMappingCmd( void );
2442 static void	BrowseAllCmd( void );
2443 static void	GetAddrInfoStressCmd( void );
2444 static void	DNSQueryCmd( void );
2445 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2446 static void	DNSCryptCmd( void );
2447 #endif
2448 static void	MDNSQueryCmd( void );
2449 #if( TARGET_OS_DARWIN )
2450 static void	PIDToUUIDCmd( void );
2451 #endif
2452 static void	DaemonVersionCmd( void );
2453 
2454 static CLIOption		kGlobalOpts[] =
2455 {
2456 	CLI_OPTION_CALLBACK_EX( 'V', "version", VersionOptionCallback, NULL, NULL,
2457 		kCLIOptionFlags_NoArgument | kCLIOptionFlags_GlobalOnly, "Displays the version of this tool.", NULL ),
2458 	CLI_OPTION_HELP(),
2459 
2460 	// Common commands.
2461 
2462 	Command( "browse",				BrowseCmd,				kBrowseOpts,			"Uses DNSServiceBrowse() to browse for one or more service types.", false ),
2463 	Command( "getAddrInfo",			GetAddrInfoCmd,			kGetAddrInfoOpts,		"Uses DNSServiceGetAddrInfo() to resolve a hostname to IP addresses.", false ),
2464 	Command( "queryRecord",			QueryRecordCmd,			kQueryRecordOpts,		"Uses DNSServiceQueryRecord() to query for an arbitrary DNS record.", false ),
2465 	Command( "register",			RegisterCmd,			kRegisterOpts,			"Uses DNSServiceRegister() to register a service.", false ),
2466 	Command( "registerRecord",		RegisterRecordCmd,		kRegisterRecordOpts,	"Uses DNSServiceRegisterRecord() to register a record.", false ),
2467 	Command( "resolve",				ResolveCmd,				kResolveOpts,			"Uses DNSServiceResolve() to resolve a service.", false ),
2468 	Command( "reconfirm",			ReconfirmCmd,			kReconfirmOpts,			"Uses DNSServiceReconfirmRecord() to reconfirm a record.", false ),
2469 	Command( "getaddrinfo-posix",	GetAddrInfoPOSIXCmd,	kGetAddrInfoPOSIXOpts,	"Uses getaddrinfo() to resolve a hostname to IP addresses.", false ),
2470 	Command( "reverseLookup",		ReverseLookupCmd,		kReverseLookupOpts,		"Uses DNSServiceQueryRecord() to perform a reverse IP address lookup.", false ),
2471 	Command( "portMapping",			PortMappingCmd,			kPortMappingOpts,		"Uses DNSServiceNATPortMappingCreate() to create a port mapping.", false ),
2472 #if( TARGET_OS_DARWIN )
2473 	Command( "registerKA",			RegisterKACmd,			kRegisterKA_Opts,		"Uses DNSServiceSleepKeepalive_sockaddr() to register a keep alive record.", false ),
2474 #endif
2475 	Command( "browseAll",			BrowseAllCmd,			kBrowseAllOpts,			"Browse and resolve all (or specific) services and, optionally, attempt connections.", false ),
2476 
2477 	// Uncommon commands.
2478 
2479 	Command( "getnameinfo",			GetNameInfoCmd,			kGetNameInfoOpts,		"Calls getnameinfo() and prints results.", true ),
2480 	Command( "getAddrInfoStress",	GetAddrInfoStressCmd,	kGetAddrInfoStressOpts,	"Runs DNSServiceGetAddrInfo() stress testing.", true ),
2481 	Command( "DNSQuery",			DNSQueryCmd,			kDNSQueryOpts,			"Crafts and sends a DNS query.", true ),
2482 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
2483 	Command( "DNSCrypt",			DNSCryptCmd,			kDNSCryptOpts,			"Crafts and sends a DNSCrypt query.", true ),
2484 #endif
2485 	Command( "mdnsquery",			MDNSQueryCmd,			kMDNSQueryOpts,			"Crafts and sends an mDNS query over the specified interface.", true ),
2486 	Command( "mdnscollider",		MDNSColliderCmd,		kMDNSColliderOpts,		"Creates record name collision scenarios.", true ),
2487 #if( TARGET_OS_DARWIN )
2488 	Command( "pid2uuid",			PIDToUUIDCmd,			kPIDToUUIDOpts,			"Prints the UUID of a process.", true ),
2489 #endif
2490 	Command( "server",				DNSServerCmd,			kDNSServerOpts,			"DNS server for testing.", true ),
2491 	Command( "mdnsreplier",			MDNSReplierCmd,			kMDNSReplierOpts,		"Responds to mDNS queries for a set of authoritative resource records.", true ),
2492 	Command( "test",				NULL,					kTestOpts,				"Commands for testing DNS-SD.", true ),
2493 	Command( "ssdp",				NULL,					kSSDPOpts,				"Simple Service Discovery Protocol (SSDP).", true ),
2494 #if( TARGET_OS_DARWIN )
2495 	Command( "legacy",				NULL,					kLegacyOpts,			"Legacy DNS API.", true ),
2496 	Command( "dnsconfig",			NULL,					kDNSConfigOpts,			"Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ),
2497 	Command( "xpcsend",				XPCSendCmd,				kXPCSendOpts,			"Sends a message to an XPC service.", true ),
2498 #endif
2499 #if( MDNSRESPONDER_PROJECT )
2500 	Command( "interfaceMonitor",	InterfaceMonitorCmd,	kInterfaceMonitorOpts,	"Instantiates an mdns_interface_monitor.", true ),
2501 	Command( "querier",				QuerierCommand,			kQuerierOpts,			"Sends a DNS query using mdns_querier.", true ),
2502 	Command( "dnsproxy",			DNSProxyCmd,			kDNSProxyOpts,			"Enables mDNSResponder's DNS proxy.", true ),
2503 	Command( "getaddrinfo-new",		GetAddrInfoNewCommand,	kGetAddrInfoNewOpts,	"Uses dnssd_getaddrinfo to resolve a hostname to IP addresses.", false ),
2504 #endif
2505 	Command( "daemonVersion",		DaemonVersionCmd,		NULL,					"Prints the version of the DNS-SD daemon.", true ),
2506 
2507 	CLI_COMMAND_HELP(),
2508 	CLI_OPTION_END()
2509 };
2510 
2511 //===========================================================================================================================
2512 //	Helper Prototypes
2513 //===========================================================================================================================
2514 
2515 #define kExitReason_OneShotDone				"one-shot done"
2516 #define kExitReason_ReceivedResponse		"received response"
2517 #define kExitReason_SIGINT					"interrupt signal"
2518 #define kExitReason_Timeout					"timeout"
2519 #define kExitReason_TimeLimit				"time limit"
2520 
2521 static void	Exit( void *inContext ) ATTRIBUTE_NORETURN;
2522 
2523 static DNSServiceFlags	GetDNSSDFlagsFromOpts( void );
2524 
2525 typedef enum
2526 {
2527 	kConnectionType_None			= 0,
2528 	kConnectionType_Normal			= 1,
2529 	kConnectionType_DelegatePID		= 2,
2530 	kConnectionType_DelegateUUID	= 3
2531 
2532 }	ConnectionType;
2533 
2534 typedef struct
2535 {
2536 	ConnectionType		type;
2537 	union
2538 	{
2539 		int32_t			pid;
2540 		uint8_t			uuid[ 16 ];
2541 
2542 	}	delegate;
2543 
2544 }	ConnectionDesc;
2545 
2546 static OSStatus
2547 	CreateConnectionFromArgString(
2548 		const char *			inString,
2549 		dispatch_queue_t		inQueue,
2550 		DNSServiceRef *			outSDRef,
2551 		ConnectionDesc *		outDesc );
2552 static OSStatus			InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex );
2553 static OSStatus			RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen );
2554 static OSStatus			RecordTypeFromArgString( const char *inString, uint16_t *outValue );
2555 static OSStatus			RecordClassFromArgString( const char *inString, uint16_t *outValue );
2556 
2557 #define kInterfaceNameBufLen		( Max( IF_NAMESIZE, 16 ) + 1 )
2558 
2559 static char *			InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] );
2560 static const char *		RecordTypeToString( int inValue );
2561 #if( MDNSRESPONDER_PROJECT )
2562 static const char *		RecordClassToString( int inValue );
2563 #endif
2564 
2565 static OSStatus
2566 	WriteDNSQueryMessage(
2567 		uint8_t			inMsg[ kDNSQueryMessageMaxLen ],
2568 		uint16_t		inMsgID,
2569 		uint16_t		inFlags,
2570 		const char *	inQName,
2571 		uint16_t		inQType,
2572 		uint16_t		inQClass,
2573 		size_t *		outMsgLen );
2574 
2575 // Dispatch helpers
2576 
2577 typedef void ( *DispatchHandler )( void *inContext );
2578 
2579 static OSStatus
2580 	DispatchSignalSourceCreate(
2581 		int					inSignal,
2582 		dispatch_queue_t	inQueue,
2583 		DispatchHandler		inEventHandler,
2584 		void *				inContext,
2585 		dispatch_source_t *	outSource );
2586 static OSStatus
2587 	DispatchSocketSourceCreate(
2588 		SocketRef				inSock,
2589 		dispatch_source_type_t	inType,
2590 		dispatch_queue_t		inQueue,
2591 		DispatchHandler			inEventHandler,
2592 		DispatchHandler			inCancelHandler,
2593 		void *					inContext,
2594 		dispatch_source_t *		outSource );
2595 
2596 #define DispatchReadSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2597 	DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_READ, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2598 
2599 #define DispatchWriteSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
2600 	DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_WRITE, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
2601 
2602 static OSStatus
2603 	DispatchTimerCreate(
2604 		dispatch_time_t		inStart,
2605 		uint64_t			inIntervalNs,
2606 		uint64_t			inLeewayNs,
2607 		dispatch_queue_t	inQueue,
2608 		DispatchHandler		inEventHandler,
2609 		DispatchHandler		inCancelHandler,
2610 		void *				inContext,
2611 		dispatch_source_t *	outTimer );
2612 
2613 #define DispatchTimerOneShotCreate( IN_START, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, IN_CONTEXT, OUT_TIMER )	\
2614 	DispatchTimerCreate( IN_START, DISPATCH_TIME_FOREVER, IN_LEEWAY, IN_QUEUE, IN_EVENT_HANDLER, NULL, IN_CONTEXT, OUT_TIMER )
2615 
2616 #if( TARGET_OS_DARWIN )
2617 static OSStatus
2618 	DispatchProcessMonitorCreate(
2619 		pid_t				inPID,
2620 		unsigned long		inFlags,
2621 		dispatch_queue_t	inQueue,
2622 		DispatchHandler		inEventHandler,
2623 		DispatchHandler		inCancelHandler,
2624 		void *				inContext,
2625 		dispatch_source_t *	outMonitor );
2626 #endif
2627 
2628 static const char *	ServiceTypeDescription( const char *inName );
2629 
2630 typedef void ( *SocketContextFinalizer_f )( void *inUserCtx );
2631 
2632 typedef struct
2633 {
2634 	SocketRef						sock;			// Socket.
2635 	int32_t							refCount;		// Reference count.
2636 	void *							userContext;	// User's context.
2637 	SocketContextFinalizer_f		userFinalizer;	// User's finalizer.
2638 
2639 }	SocketContext;
2640 
2641 static SocketContext *	SocketContextCreate( SocketRef inSock, void *inUserContext, OSStatus *outError );
2642 static SocketContext *
2643 	SocketContextCreateEx(
2644 		SocketRef					inSock,
2645 		void *						inUserContext,
2646 		SocketContextFinalizer_f	inUserFinalizer,
2647 		OSStatus *					outError );
2648 static SocketContext *	SocketContextRetain( SocketContext *inContext );
2649 static void				SocketContextRelease( SocketContext *inContext );
2650 static void				SocketContextCancelHandler( void *inContext );
2651 static void				SocketContextFinalizerCF( void *inUserCtx );
2652 
2653 #define ForgetSocketContext( X )	ForgetCustom( X, SocketContextRelease )
2654 
2655 static OSStatus		StringToInt32( const char *inString, int32_t *outValue );
2656 static OSStatus		StringToUInt32( const char *inString, uint32_t *outValue );
2657 #if( TARGET_OS_DARWIN )
2658 static int64_t		_StringToInt64( const char *inString, OSStatus *outError );
2659 static uint64_t		_StringToUInt64( const char *inString, OSStatus *outError );
2660 static pid_t		_StringToPID( const char *inString, OSStatus *outError );
2661 static OSStatus
2662 	_ParseEscapedString(
2663 		const char *	inSrc,
2664 		const char *	inEnd,
2665 		const char *	inDelimiters,
2666 		char *			inBufPtr,
2667 		size_t			inBufLen,
2668 		size_t *		outCopiedLen,
2669 		size_t *		outActualLen,
2670 		const char **	outPtr );
2671 #endif
2672 static OSStatus		StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
2673 static OSStatus		StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
2674 static OSStatus		StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen );
2675 #if( TARGET_OS_DARWIN )
2676 static OSStatus		GetDefaultDNSServer( sockaddr_ip *outAddr );
2677 #endif
2678 static OSStatus
2679 	_ServerSocketOpenEx2(
2680 		int				inFamily,
2681 		int				inType,
2682 		int				inProtocol,
2683 		const void *	inAddr,
2684 		int				inPort,
2685 		int *			outPort,
2686 		int				inRcvBufSize,
2687 		Boolean			inNoPortReuse,
2688 		SocketRef *		outSock );
2689 
2690 static const struct sockaddr *	GetMDNSMulticastAddrV4( void );
2691 static const struct sockaddr *	GetMDNSMulticastAddrV6( void );
2692 
2693 static OSStatus
2694 	CreateMulticastSocket(
2695 		const struct sockaddr *	inAddr,
2696 		int						inPort,
2697 		const char *			inIfName,
2698 		uint32_t				inIfIndex,
2699 		Boolean					inJoin,
2700 		int *					outPort,
2701 		SocketRef *				outSock );
2702 
2703 static OSStatus	DecimalTextToUInt32( const char *inSrc, const char *inEnd, uint32_t *outValue, const char **outPtr );
2704 static OSStatus	CheckIntegerArgument( int inArgValue, const char *inArgName, int inMin, int inMax );
2705 static OSStatus	CheckDoubleArgument( double inArgValue, const char *inArgName, double inMin, double inMax );
2706 static OSStatus	CheckRootUser( void );
2707 #if( TARGET_OS_POSIX )
2708 static OSStatus
2709 	_SpawnCommand(
2710 		pid_t *			outPID,
2711 		const char *	inStdOutRedirect,
2712 		const char *	inStdErrRedirect,
2713 		const char *	inFormat,
2714 		... );
2715 #endif
2716 static OSStatus	OutputFormatFromArgString( const char *inArgString, OutputFormatType *outFormat );
2717 static OSStatus	OutputPropertyList( CFPropertyListRef inPList, OutputFormatType inType, const char *inOutputFilePath );
2718 static OSStatus	CreateSRVRecordDataFromString( const char *inString, uint8_t **outPtr, size_t *outLen );
2719 static OSStatus	CreateTXTRecordDataFromString( const char *inString, int inDelimiter, uint8_t **outPtr, size_t *outLen );
2720 static OSStatus
2721 	CreateNSECRecordData(
2722 		const uint8_t *	inNextDomainName,
2723 		uint8_t **		outPtr,
2724 		size_t *		outLen,
2725 		unsigned int	inTypeCount,
2726 		... );
2727 static OSStatus
2728 	AppendSOARecord(
2729 		DataBuffer *	inDB,
2730 		const uint8_t *	inNamePtr,
2731 		size_t			inNameLen,
2732 		uint16_t		inType,
2733 		uint16_t		inClass,
2734 		uint32_t		inTTL,
2735 		const uint8_t *	inMName,
2736 		const uint8_t *	inRName,
2737 		uint32_t		inSerial,
2738 		uint32_t		inRefresh,
2739 		uint32_t		inRetry,
2740 		uint32_t		inExpire,
2741 		uint32_t		inMinimumTTL,
2742 		size_t *		outLen );
2743 static OSStatus
2744 	CreateSOARecordData(
2745 		const uint8_t *	inMName,
2746 		const uint8_t *	inRName,
2747 		uint32_t		inSerial,
2748 		uint32_t		inRefresh,
2749 		uint32_t		inRetry,
2750 		uint32_t		inExpire,
2751 		uint32_t		inMinimumTTL,
2752 		uint8_t **		outPtr,
2753 		size_t *		outLen );
2754 static OSStatus
2755 	_DataBuffer_AppendDNSQuestion(
2756 		DataBuffer *	inDB,
2757 		const uint8_t *	inNamePtr,
2758 		size_t			inNameLen,
2759 		uint16_t		inType,
2760 		uint16_t		inClass );
2761 static OSStatus
2762 	_DataBuffer_AppendDNSRecord(
2763 		DataBuffer *	inDB,
2764 		const uint8_t *	inNamePtr,
2765 		size_t			inNameLen,
2766 		uint16_t		inType,
2767 		uint16_t		inClass,
2768 		uint32_t		inTTL,
2769 		const uint8_t *	inRDataPtr,
2770 		size_t			inRDataLen );
2771 static char *	_NanoTime64ToTimestamp( NanoTime64 inTime, char *inBuf, size_t inMaxLen );
2772 
2773 typedef struct MDNSInterfaceItem		MDNSInterfaceItem;
2774 struct MDNSInterfaceItem
2775 {
2776 	MDNSInterfaceItem *		next;
2777 	char *					ifName;
2778 	uint32_t				ifIndex;
2779 	Boolean					hasIPv4;
2780 	Boolean					hasIPv6;
2781 	Boolean					isAWDL;
2782 	Boolean					isWiFi;
2783 };
2784 
2785 typedef enum
2786 {
2787 	kMDNSInterfaceSubset_All		= 0,	// All mDNS-capable interfaces.
2788 	kMDNSInterfaceSubset_AWDL		= 1,	// All mDNS-capable AWDL interfaces.
2789 	kMDNSInterfaceSubset_NonAWDL	= 2		// All mDNS-capable non-AWDL iterfaces.
2790 
2791 }	MDNSInterfaceSubset;
2792 
2793 static OSStatus	_MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset, size_t inItemSize, MDNSInterfaceItem **outList );
2794 static void		_MDNSInterfaceListFree( MDNSInterfaceItem *inList );
2795 #define _MDNSInterfaceListForget( X )		ForgetCustom( X, _MDNSInterfaceListFree )
2796 static OSStatus _MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset, char inNameBuf[ IF_NAMESIZE + 1 ], uint32_t *outIndex );
2797 
2798 static OSStatus	_SetComputerName( CFStringRef inComputerName, CFStringEncoding inEncoding );
2799 static OSStatus	_SetComputerNameWithUTF8CString( const char *inComputerName );
2800 static OSStatus	_SetLocalHostName( CFStringRef inLocalHostName );
2801 static OSStatus	_SetLocalHostNameWithUTF8CString( const char *inLocalHostName );
2802 #if( TARGET_OS_DARWIN )
2803 static OSStatus	_InterfaceIPv6AddressAdd( const char *inIfName, uint8_t inAddr[ STATIC_PARAM 16 ], int inMaskBitLen );
2804 static OSStatus	_InterfaceIPv6AddressRemove( const char *inIfName, const uint8_t inAddr[ STATIC_PARAM 16 ] );
2805 #endif
2806 static int64_t	_TicksDiff( uint64_t inT1, uint64_t inT2 );
2807 static void		_SockAddrInitIPv4( struct sockaddr_in *inSA, uint32_t inIPv4, uint16_t inPort );
2808 static void
2809 	_SockAddrInitIPv6(
2810 		struct sockaddr_in6 *	inSA,
2811 		const uint8_t			inIPv6[ STATIC_PARAM 16 ],
2812 		uint32_t				inScope,
2813 		uint16_t				inPort );
2814 
2815 #define kIP6ArpaDomainStr					"ip6.arpa."
2816 #define kReverseIPv6DomainNameBufLen		( ( 4 * 16 ) + sizeof_string( kIP6ArpaDomainStr ) + 1 )
2817 
2818 static void
2819 	_WriteReverseIPv6DomainNameString(
2820 		const uint8_t	inIPv6Addr[ STATIC_PARAM 16 ],
2821 		char			outBuffer[ STATIC_PARAM kReverseIPv6DomainNameBufLen ] );
2822 
2823 #define kInAddrArpaDomainStr				"in-addr.arpa."
2824 #define kReverseIPv4DomainNameBufLen		( ( 4 * 4 ) + sizeof_string( kInAddrArpaDomainStr ) + 1 )
2825 
2826 static void
2827 	_WriteReverseIPv4DomainNameString(
2828 		uint32_t	inIPv4Addr,
2829 		char		outBuffer[ STATIC_PARAM kReverseIPv4DomainNameBufLen ] );
2830 
2831 #if( MDNSRESPONDER_PROJECT )
2832 static OSStatus	_SetDefaultFallbackDNSService( const char *inFallbackDNSServiceStr );
2833 #endif
2834 
2835 static OSStatus	_SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs );
2836 static OSStatus
2837 	_StringToIPv4Address(
2838 		const char *			inStr,
2839 		StringToIPAddressFlags	inFlags,
2840 		uint32_t *				outIP,
2841 		int *					outPort,
2842 		uint32_t *				outSubnet,
2843 		uint32_t *				outRouter,
2844 		const char **			outStr );
2845 static void	_StringArray_Free( char **inArray, size_t inCount );
2846 static OSStatus
2847 	_StringToIPv6Address(
2848 		const char *			inStr,
2849 		StringToIPAddressFlags	inFlags,
2850 		uint8_t					outIPv6[ 16 ],
2851 		uint32_t *				outScope,
2852 		int *					outPort,
2853 		int *					outPrefix,
2854 		const char **			outStr );
2855 static Boolean
2856 	_ParseQuotedEscapedString(
2857 		const char *	inSrc,
2858 		const char *	inEnd,
2859 		const char *	inDelimiters,
2860 		char *			inBuf,
2861 		size_t			inMaxLen,
2862 		size_t *		outCopiedLen,
2863 		size_t *		outTotalLen,
2864 		const char **	outSrc );
2865 static void *	_memdup( const void *inPtr, size_t inLen );
2866 static int		_memicmp( const void *inP1, const void *inP2, size_t inLen );
2867 static uint32_t	_FNV1( const void *inData, size_t inSize );
2868 
2869 #define Unused( X )		(void)(X)
2870 
2871 //===========================================================================================================================
2872 //	MDNSCollider
2873 //===========================================================================================================================
2874 
2875 typedef struct MDNSColliderPrivate *		MDNSColliderRef;
2876 
2877 typedef uint32_t		MDNSColliderProtocols;
2878 #define kMDNSColliderProtocol_None		0
2879 #define kMDNSColliderProtocol_IPv4		( 1 << 0 )
2880 #define kMDNSColliderProtocol_IPv6		( 1 << 1 )
2881 
2882 typedef void ( *MDNSColliderStopHandler_f )( void *inContext, OSStatus inError );
2883 
2884 static OSStatus	MDNSColliderCreate( dispatch_queue_t inQueue, MDNSColliderRef *outCollider );
2885 static OSStatus	MDNSColliderStart( MDNSColliderRef inCollider );
2886 static void		MDNSColliderStop( MDNSColliderRef inCollider );
2887 static void		MDNSColliderSetProtocols( MDNSColliderRef inCollider, MDNSColliderProtocols inProtocols );
2888 static void		MDNSColliderSetInterfaceIndex( MDNSColliderRef inCollider, uint32_t inInterfaceIndex );
2889 static OSStatus	MDNSColliderSetProgram( MDNSColliderRef inCollider, const char *inProgramStr );
2890 static void
2891 	MDNSColliderSetStopHandler(
2892 		MDNSColliderRef				inCollider,
2893 		MDNSColliderStopHandler_f	inStopHandler,
2894 		void *						inStopContext );
2895 static OSStatus
2896 	MDNSColliderSetRecord(
2897 		MDNSColliderRef	inCollider,
2898 		const uint8_t *	inName,
2899 		uint16_t		inType,
2900 		const void *	inRDataPtr,
2901 		size_t			inRDataLen );
2902 static CFTypeID	MDNSColliderGetTypeID( void );
2903 
2904 //===========================================================================================================================
2905 //	ServiceBrowser
2906 //===========================================================================================================================
2907 
2908 typedef struct ServiceBrowserPrivate *		ServiceBrowserRef;
2909 typedef struct ServiceBrowserResults		ServiceBrowserResults;
2910 typedef struct SBRDomain					SBRDomain;
2911 typedef struct SBRServiceType				SBRServiceType;
2912 typedef struct SBRServiceInstance			SBRServiceInstance;
2913 typedef struct SBRIPAddress					SBRIPAddress;
2914 
2915 typedef void ( *ServiceBrowserCallback_f )( ServiceBrowserResults *inResults, OSStatus inError, void *inContext );
2916 
2917 struct ServiceBrowserResults
2918 {
2919 	SBRDomain *		domainList;	// List of domains in which services were found.
2920 };
2921 
2922 struct SBRDomain
2923 {
2924 	SBRDomain *				next;		// Next domain in list.
2925 	char *					name;		// Name of domain represented by this object.
2926 	SBRServiceType *		typeList;	// List of service types in this domain.
2927 };
2928 
2929 struct SBRServiceType
2930 {
2931 	SBRServiceType *			next;			// Next service type in list.
2932 	char *						name;			// Name of service type represented by this object.
2933 	SBRServiceInstance *		instanceList;	// List of service instances of this service type.
2934 };
2935 
2936 struct SBRServiceInstance
2937 {
2938 	SBRServiceInstance *		next;			// Next service instance in list.
2939 	char *						name;			// Name of service instance represented by this object.
2940 	char *						hostname;		// Target from service instance's SRV record.
2941 	uint32_t					ifIndex;		// Index of interface over which this service instance was discovered.
2942 	uint16_t					port;			// Port from service instance's SRV record.
2943 	uint8_t *					txtPtr;			// Service instance's TXT record data.
2944 	size_t						txtLen;			// Service instance's TXT record data length.
2945 	SBRIPAddress *				ipaddrList;		// List of IP addresses that the hostname resolved to.
2946 	uint64_t					discoverTimeUs;	// Time it took to discover this service instance in microseconds.
2947 	uint64_t					resolveTimeUs;	// Time it took to resolve this service instance in microseconds.
2948 };
2949 
2950 struct SBRIPAddress
2951 {
2952 	SBRIPAddress *		next;			// Next IP address in list.
2953 	sockaddr_ip			sip;			// IPv4 or IPv6 address.
2954 	uint64_t			resolveTimeUs;	// Time it took to resolve this IP address in microseconds.
2955 };
2956 
2957 static CFTypeID	ServiceBrowserGetTypeID( void );
2958 static OSStatus
2959 	ServiceBrowserCreate(
2960 		dispatch_queue_t	inQueue,
2961 		uint32_t			inInterfaceIndex,
2962 		const char *		inDomain,
2963 		unsigned int		inBrowseTimeSecs,
2964 		Boolean				inIncludeAWDL,
2965 		ServiceBrowserRef *	outBrowser );
2966 #if( MDNSRESPONDER_PROJECT )
2967 static void		ServiceBrowserSetUseNewGAI( ServiceBrowserRef inBrowser, Boolean inUseNewGAI );
2968 #endif
2969 static void		ServiceBrowserStart( ServiceBrowserRef inBrowser );
2970 static OSStatus	ServiceBrowserAddServiceType( ServiceBrowserRef inBrowser, const char *inServiceType );
2971 static void
2972 	ServiceBrowserSetCallback(
2973 		ServiceBrowserRef			inBrowser,
2974 		ServiceBrowserCallback_f	inCallback,
2975 		void *						inContext );
2976 static void		ServiceBrowserResultsRetain( ServiceBrowserResults *inResults );
2977 static void		ServiceBrowserResultsRelease( ServiceBrowserResults *inResults );
2978 
2979 #define ForgetServiceBrowserResults( X )		ForgetCustom( X, ServiceBrowserResultsRelease )
2980 
2981 //===========================================================================================================================
2982 //	DNSServer
2983 //===========================================================================================================================
2984 
2985 typedef struct DNSServerPrivate *		DNSServerRef;
2986 
2987 typedef void ( *DNSServerStartHandler_f )( const sockaddr_ip *inServerArray, size_t inServerCount, void *inCtx );
2988 typedef void ( *DNSServerStopHandler_f )( OSStatus inError, void *inCtx );
2989 
2990 static CFTypeID	DNSServerGetTypeID( void );
2991 static OSStatus
2992 	_DNSServerCreate(
2993 		dispatch_queue_t		inQueue,
2994 		DNSServerStartHandler_f	inStartHandler,
2995 		DNSServerStopHandler_f	inStopHandler,
2996 		void *					inUserContext,
2997 		unsigned int			inResponseDelayMs,
2998 		uint32_t				inDefaultTTL,
2999 		const sockaddr_ip *		inServerArray,
3000 		size_t					inServerCount,
3001 		const char *			inDomain,
3002 		Boolean					inBadUDPMode,
3003 		DNSServerRef *			outServer );
3004 static OSStatus	_DNSServerSetIgnoredQType( DNSServerRef inServer, int inQType );
3005 static void		_DNSServerStart( DNSServerRef inServer );
3006 static void		_DNSServerStop( DNSServerRef inServer );
3007 
3008 #define DNSServerForget( X )		ForgetCustomEx( X, _DNSServerStop, CFRelease )
3009 
3010 //===========================================================================================================================
3011 //	main
3012 //===========================================================================================================================
3013 
3014 #define _PRINTF_EXTENSION_HANDLER_DECLARE( NAME )	\
3015 	static int										\
3016 		_PrintFExtensionHandler_ ## NAME (			\
3017 			PrintFContext *	inContext,				\
3018 			PrintFFormat *	inFormat,				\
3019 			PrintFVAList *	inArgs,					\
3020 			void *			inUserContext )
3021 
3022 _PRINTF_EXTENSION_HANDLER_DECLARE( Timestamp );
3023 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSMessage );
3024 _PRINTF_EXTENSION_HANDLER_DECLARE( RawDNSMessage );
3025 _PRINTF_EXTENSION_HANDLER_DECLARE( CallbackFlags );
3026 _PRINTF_EXTENSION_HANDLER_DECLARE( DNSRecordData );
3027 _PRINTF_EXTENSION_HANDLER_DECLARE( DomainName );
3028 
main(int argc,const char ** argv)3029 int	main( int argc, const char **argv )
3030 {
3031 	OSStatus		err;
3032 
3033 	// Route DebugServices logging output to stderr.
3034 
3035 	dlog_control( "DebugServices:output=file;stderr" );
3036 
3037 	PrintFRegisterExtension( "du:time",		_PrintFExtensionHandler_Timestamp,		NULL );
3038 	PrintFRegisterExtension( "du:dnsmsg",	_PrintFExtensionHandler_DNSMessage,		NULL );
3039 	PrintFRegisterExtension( "du:rdnsmsg",	_PrintFExtensionHandler_RawDNSMessage,	NULL );
3040 	PrintFRegisterExtension( "du:cbflags",	_PrintFExtensionHandler_CallbackFlags,	NULL );
3041 	PrintFRegisterExtension( "du:rdata",	_PrintFExtensionHandler_DNSRecordData,	NULL );
3042 	PrintFRegisterExtension( "du:dname",	_PrintFExtensionHandler_DomainName,		NULL );
3043 	CLIInit( argc, argv );
3044 	err = CLIParse( kGlobalOpts, kCLIFlags_None );
3045 	if( err ) gExitCode = 1;
3046 
3047 	return( gExitCode );
3048 }
3049 
3050 //===========================================================================================================================
3051 //	VersionOptionCallback
3052 //===========================================================================================================================
3053 
VersionOptionCallback(CLIOption * inOption,const char * inArg,int inUnset)3054 static OSStatus	VersionOptionCallback( CLIOption *inOption, const char *inArg, int inUnset )
3055 {
3056 	const char *		srcVers;
3057 #if( MDNSRESPONDER_PROJECT )
3058 	char				srcStr[ 16 ];
3059 #endif
3060 
3061 	Unused( inOption );
3062 	Unused( inArg );
3063 	Unused( inUnset );
3064 
3065 #if( MDNSRESPONDER_PROJECT )
3066 	srcVers = SourceVersionToCString( _DNS_SD_H, srcStr );
3067 #else
3068 	srcVers = DNSSDUTIL_SOURCE_VERSION;
3069 #endif
3070 	FPrintF( stdout, "%s version %v (%s)\n", gProgramName, kDNSSDUtilNumVersion, srcVers );
3071 
3072 	return( kEndingErr );
3073 }
3074 
3075 //===========================================================================================================================
3076 //	BrowseCmd
3077 //===========================================================================================================================
3078 
3079 typedef struct BrowseResolveOp		BrowseResolveOp;
3080 
3081 struct BrowseResolveOp
3082 {
3083 	BrowseResolveOp *		next;			// Next resolve operation in list.
3084 	DNSServiceRef			sdRef;			// sdRef of the DNSServiceResolve or DNSServiceQueryRecord operation.
3085 	char *					fullName;		// Full name of the service to resolve.
3086 	uint32_t				interfaceIndex;	// Interface index of the DNSServiceResolve or DNSServiceQueryRecord operation.
3087 };
3088 
3089 typedef struct
3090 {
3091 	DNSServiceRef			mainRef;			// Main sdRef for shared connection.
3092 	DNSServiceRef *			opRefs;				// Array of sdRefs for individual Browse operarions.
3093 	size_t					opRefsCount;		// Count of array of sdRefs for non-shared connections.
3094 	const char *			domain;				// Domain for DNSServiceBrowse operation(s).
3095 	DNSServiceFlags			flags;				// Flags for DNSServiceBrowse operation(s).
3096 	char **					serviceTypes;		// Array of service types to browse for.
3097 	size_t					serviceTypesCount;	// Count of array of service types to browse for.
3098 	int						timeLimitSecs;		// Time limit of DNSServiceBrowse operation in seconds.
3099 	BrowseResolveOp *		resolveList;		// List of resolve and/or TXT record query operations.
3100 	uint32_t				ifIndex;			// Interface index of DNSServiceBrowse operation(s).
3101 	Boolean					printedHeader;		// True if results header has been printed.
3102 	Boolean					doResolve;			// True if service instances are to be resolved.
3103 	Boolean					doResolveTXTOnly;	// True if TXT records of service instances are to be queried.
3104 
3105 }	BrowseContext;
3106 
3107 static void		BrowsePrintPrologue( const BrowseContext *inContext );
3108 static void		BrowseContextFree( BrowseContext *inContext );
3109 static OSStatus	BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp );
3110 static void		BrowseResolveOpFree( BrowseResolveOp *inOp );
3111 static void DNSSD_API
3112 	BrowseCallback(
3113 		DNSServiceRef		inSDRef,
3114 		DNSServiceFlags		inFlags,
3115 		uint32_t			inInterfaceIndex,
3116 		DNSServiceErrorType	inError,
3117 		const char *		inName,
3118 		const char *		inRegType,
3119 		const char *		inDomain,
3120 		void *				inContext );
3121 static void DNSSD_API
3122 	BrowseResolveCallback(
3123 		DNSServiceRef			inSDRef,
3124 		DNSServiceFlags			inFlags,
3125 		uint32_t				inInterfaceIndex,
3126 		DNSServiceErrorType		inError,
3127 		const char *			inFullName,
3128 		const char *			inHostname,
3129 		uint16_t				inPort,
3130 		uint16_t				inTXTLen,
3131 		const unsigned char *	inTXTPtr,
3132 		void *					inContext );
3133 static void DNSSD_API
3134 	BrowseQueryRecordCallback(
3135 		DNSServiceRef			inSDRef,
3136 		DNSServiceFlags			inFlags,
3137 		uint32_t				inInterfaceIndex,
3138 		DNSServiceErrorType		inError,
3139 		const char *			inFullName,
3140 		uint16_t				inType,
3141 		uint16_t				inClass,
3142 		uint16_t				inRDataLen,
3143 		const void *			inRDataPtr,
3144 		uint32_t				inTTL,
3145 		void *					inContext );
3146 
BrowseCmd(void)3147 static void	BrowseCmd( void )
3148 {
3149 	OSStatus				err;
3150 	size_t					i;
3151 	BrowseContext *			context			= NULL;
3152 	dispatch_source_t		signalSource	= NULL;
3153 	int						useMainConnection;
3154 
3155 	// Set up SIGINT handler.
3156 
3157 	signal( SIGINT, SIG_IGN );
3158 	err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
3159 	require_noerr( err, exit );
3160 	dispatch_resume( signalSource );
3161 
3162 	// Create context.
3163 
3164 	context = (BrowseContext *) calloc( 1, sizeof( *context ) );
3165 	require_action( context, exit, err = kNoMemoryErr );
3166 
3167 	context->opRefs = (DNSServiceRef *) calloc( gBrowse_ServiceTypesCount, sizeof( DNSServiceRef ) );
3168 	require_action( context->opRefs, exit, err = kNoMemoryErr );
3169 	context->opRefsCount = gBrowse_ServiceTypesCount;
3170 
3171 	// Check command parameters.
3172 
3173 	if( gBrowse_TimeLimitSecs < 0 )
3174 	{
3175 		FPrintF( stderr, "Invalid time limit: %d seconds.\n", gBrowse_TimeLimitSecs );
3176 		err = kParamErr;
3177 		goto exit;
3178 	}
3179 
3180 	// Create main connection.
3181 
3182 	if( gConnectionOpt )
3183 	{
3184 		err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3185 		require_noerr_quiet( err, exit );
3186 		useMainConnection = true;
3187 	}
3188 	else
3189 	{
3190 		useMainConnection = false;
3191 	}
3192 
3193 	// Get flags.
3194 
3195 	context->flags = GetDNSSDFlagsFromOpts();
3196 	if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3197 
3198 	// Get interface.
3199 
3200 	err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3201 	require_noerr_quiet( err, exit );
3202 
3203 	// Set remaining parameters.
3204 
3205 	context->serviceTypes		= gBrowse_ServiceTypes;
3206 	context->serviceTypesCount	= gBrowse_ServiceTypesCount;
3207 	context->domain				= gBrowse_Domain;
3208 	context->doResolve			= gBrowse_DoResolve	? true : false;
3209 	context->timeLimitSecs		= gBrowse_TimeLimitSecs;
3210 	context->doResolveTXTOnly	= gBrowse_QueryTXT	? true : false;
3211 
3212 	// Print prologue.
3213 
3214 	BrowsePrintPrologue( context );
3215 
3216 	// Start operation(s).
3217 
3218 	for( i = 0; i < context->serviceTypesCount; ++i )
3219 	{
3220 		DNSServiceRef		sdRef;
3221 
3222 		sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3223 		err = DNSServiceBrowse( &sdRef, context->flags, context->ifIndex, context->serviceTypes[ i ], context->domain,
3224 			BrowseCallback, context );
3225 		require_noerr( err, exit );
3226 
3227 		context->opRefs[ i ] = sdRef;
3228 		if( !useMainConnection )
3229 		{
3230 			err = DNSServiceSetDispatchQueue( context->opRefs[ i ], dispatch_get_main_queue() );
3231 			require_noerr( err, exit );
3232 		}
3233 	}
3234 
3235 	// Set time limit.
3236 
3237 	if( context->timeLimitSecs > 0 )
3238 	{
3239 		dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
3240 			kExitReason_TimeLimit, Exit );
3241 	}
3242 	dispatch_main();
3243 
3244 exit:
3245 	dispatch_source_forget( &signalSource );
3246 	if( context ) BrowseContextFree( context );
3247 	if( err ) exit( 1 );
3248 }
3249 
3250 //===========================================================================================================================
3251 //	BrowsePrintPrologue
3252 //===========================================================================================================================
3253 
BrowsePrintPrologue(const BrowseContext * inContext)3254 static void	BrowsePrintPrologue( const BrowseContext *inContext )
3255 {
3256 	const int						timeLimitSecs	= inContext->timeLimitSecs;
3257 	const char * const *			ptr				= (const char **) inContext->serviceTypes;
3258 	const char * const * const		end				= (const char **) inContext->serviceTypes + inContext->serviceTypesCount;
3259 	char							ifName[ kInterfaceNameBufLen ];
3260 
3261 	InterfaceIndexToName( inContext->ifIndex, ifName );
3262 
3263 	FPrintF( stdout, "Flags:         %#{flags}\n",	inContext->flags, kDNSServiceFlagsDescriptors );
3264 	FPrintF( stdout, "Interface:     %d (%s)\n",	(int32_t) inContext->ifIndex, ifName );
3265 	FPrintF( stdout, "Service types: %s",			*ptr++ );
3266 	while( ptr < end ) FPrintF( stdout, ", %s",		*ptr++ );
3267 	FPrintF( stdout, "\n" );
3268 	FPrintF( stdout, "Domain:        %s\n",	inContext->domain ? inContext->domain : "<NULL> (default domains)" );
3269 	FPrintF( stdout, "Time limit:    " );
3270 	if( timeLimitSecs > 0 )	FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3271 	else					FPrintF( stdout, "∞\n" );
3272 	FPrintF( stdout, "Start time:    %{du:time}\n", NULL );
3273 	FPrintF( stdout, "---\n" );
3274 }
3275 
3276 //===========================================================================================================================
3277 //	BrowseContextFree
3278 //===========================================================================================================================
3279 
BrowseContextFree(BrowseContext * inContext)3280 static void	BrowseContextFree( BrowseContext *inContext )
3281 {
3282 	size_t		i;
3283 
3284 	for( i = 0; i < inContext->opRefsCount; ++i )
3285 	{
3286 		DNSServiceForget( &inContext->opRefs[ i ] );
3287 	}
3288 	if( inContext->serviceTypes )
3289 	{
3290 		_StringArray_Free( inContext->serviceTypes, inContext->serviceTypesCount );
3291 		inContext->serviceTypes			= NULL;
3292 		inContext->serviceTypesCount	= 0;
3293 	}
3294 	DNSServiceForget( &inContext->mainRef );
3295 	free( inContext );
3296 }
3297 
3298 //===========================================================================================================================
3299 //	BrowseResolveOpCreate
3300 //===========================================================================================================================
3301 
BrowseResolveOpCreate(const char * inFullName,uint32_t inInterfaceIndex,BrowseResolveOp ** outOp)3302 static OSStatus	BrowseResolveOpCreate( const char *inFullName, uint32_t inInterfaceIndex, BrowseResolveOp **outOp )
3303 {
3304 	OSStatus				err;
3305 	BrowseResolveOp *		resolveOp;
3306 
3307 	resolveOp = (BrowseResolveOp *) calloc( 1, sizeof( *resolveOp ) );
3308 	require_action( resolveOp, exit, err = kNoMemoryErr );
3309 
3310 	resolveOp->fullName = strdup( inFullName );
3311 	require_action( resolveOp->fullName, exit, err = kNoMemoryErr );
3312 
3313 	resolveOp->interfaceIndex = inInterfaceIndex;
3314 
3315 	*outOp = resolveOp;
3316 	resolveOp = NULL;
3317 	err = kNoErr;
3318 
3319 exit:
3320 	if( resolveOp ) BrowseResolveOpFree( resolveOp );
3321 	return( err );
3322 }
3323 
3324 //===========================================================================================================================
3325 //	BrowseResolveOpFree
3326 //===========================================================================================================================
3327 
BrowseResolveOpFree(BrowseResolveOp * inOp)3328 static void	BrowseResolveOpFree( BrowseResolveOp *inOp )
3329 {
3330 	DNSServiceForget( &inOp->sdRef );
3331 	ForgetMem( &inOp->fullName );
3332 	free( inOp );
3333 }
3334 
3335 //===========================================================================================================================
3336 //	BrowseCallback
3337 //===========================================================================================================================
3338 
3339 static void DNSSD_API
BrowseCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inName,const char * inRegType,const char * inDomain,void * inContext)3340 	BrowseCallback(
3341 		DNSServiceRef		inSDRef,
3342 		DNSServiceFlags		inFlags,
3343 		uint32_t			inInterfaceIndex,
3344 		DNSServiceErrorType	inError,
3345 		const char *		inName,
3346 		const char *		inRegType,
3347 		const char *		inDomain,
3348 		void *				inContext )
3349 {
3350 	BrowseContext * const		context = (BrowseContext *) inContext;
3351 	OSStatus					err;
3352 	BrowseResolveOp *			newOp = NULL;
3353 	BrowseResolveOp **			p;
3354 	char						fullName[ kDNSServiceMaxDomainName ];
3355 	struct timeval				now;
3356 
3357 	Unused( inSDRef );
3358 
3359 	gettimeofday( &now, NULL );
3360 
3361 	err = inError;
3362 	require_noerr( err, exit );
3363 
3364 	if( !context->printedHeader )
3365 	{
3366 		FPrintF( stdout, "%-26s  %-16s IF %-20s %-20s Instance Name\n", "Timestamp", "Flags", "Domain", "Service Type" );
3367 		context->printedHeader = true;
3368 	}
3369 	FPrintF( stdout, "%{du:time}  %{du:cbflags} %2d %-20s %-20s %s\n",
3370 		&now, inFlags, (int32_t) inInterfaceIndex, inDomain, inRegType, inName );
3371 
3372 	if( !context->doResolve && !context->doResolveTXTOnly ) goto exit;
3373 
3374 	err = DNSServiceConstructFullName( fullName, inName, inRegType, inDomain );
3375 	require_noerr( err, exit );
3376 
3377 	if( inFlags & kDNSServiceFlagsAdd )
3378 	{
3379 		DNSServiceRef		sdRef;
3380 		DNSServiceFlags		flags;
3381 
3382 		err = BrowseResolveOpCreate( fullName, inInterfaceIndex, &newOp );
3383 		require_noerr( err, exit );
3384 
3385 		if( context->mainRef )
3386 		{
3387 			sdRef = context->mainRef;
3388 			flags = kDNSServiceFlagsShareConnection;
3389 		}
3390 		else
3391 		{
3392 			flags = 0;
3393 		}
3394 		if( context->doResolve )
3395 		{
3396 			err = DNSServiceResolve( &sdRef, flags, inInterfaceIndex, inName, inRegType, inDomain, BrowseResolveCallback,
3397 				NULL );
3398 			require_noerr( err, exit );
3399 		}
3400 		else
3401 		{
3402 			err = DNSServiceQueryRecord( &sdRef, flags, inInterfaceIndex, fullName, kDNSServiceType_TXT, kDNSServiceClass_IN,
3403 				BrowseQueryRecordCallback, NULL );
3404 			require_noerr( err, exit );
3405 		}
3406 
3407 		newOp->sdRef = sdRef;
3408 		if( !context->mainRef )
3409 		{
3410 			err = DNSServiceSetDispatchQueue( newOp->sdRef, dispatch_get_main_queue() );
3411 			require_noerr( err, exit );
3412 		}
3413 		for( p = &context->resolveList; *p; p = &( *p )->next ) {}
3414 		*p = newOp;
3415 		newOp = NULL;
3416 	}
3417 	else
3418 	{
3419 		BrowseResolveOp *		resolveOp;
3420 
3421 		for( p = &context->resolveList; ( resolveOp = *p ) != NULL; p = &resolveOp->next )
3422 		{
3423 			if( ( resolveOp->interfaceIndex == inInterfaceIndex ) && ( strcasecmp( resolveOp->fullName, fullName ) == 0 ) )
3424 			{
3425 				break;
3426 			}
3427 		}
3428 		if( resolveOp )
3429 		{
3430 			*p = resolveOp->next;
3431 			BrowseResolveOpFree( resolveOp );
3432 		}
3433 	}
3434 
3435 exit:
3436 	if( newOp ) BrowseResolveOpFree( newOp );
3437 	if( err ) exit( 1 );
3438 }
3439 
3440 //===========================================================================================================================
3441 //	BrowseQueryRecordCallback
3442 //===========================================================================================================================
3443 
3444 static void DNSSD_API
BrowseQueryRecordCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inFullName,uint16_t inType,uint16_t inClass,uint16_t inRDataLen,const void * inRDataPtr,uint32_t inTTL,void * inContext)3445 	BrowseQueryRecordCallback(
3446 		DNSServiceRef			inSDRef,
3447 		DNSServiceFlags			inFlags,
3448 		uint32_t				inInterfaceIndex,
3449 		DNSServiceErrorType		inError,
3450 		const char *			inFullName,
3451 		uint16_t				inType,
3452 		uint16_t				inClass,
3453 		uint16_t				inRDataLen,
3454 		const void *			inRDataPtr,
3455 		uint32_t				inTTL,
3456 		void *					inContext )
3457 {
3458 	OSStatus			err;
3459 	struct timeval		now;
3460 
3461 	Unused( inSDRef );
3462 	Unused( inClass );
3463 	Unused( inTTL );
3464 	Unused( inContext );
3465 
3466 	gettimeofday( &now, NULL );
3467 
3468 	err = inError;
3469 	require_noerr( err, exit );
3470 	require_action( inType == kDNSServiceType_TXT, exit, err = kTypeErr );
3471 
3472 	FPrintF( stdout, "%{du:time}  %s %s TXT on interface %d\n    TXT: %#{txt}\n",
3473 		&now, DNSServiceFlagsToAddRmvStr( inFlags ), inFullName, (int32_t) inInterfaceIndex, inRDataPtr,
3474 		(size_t) inRDataLen );
3475 
3476 exit:
3477 	if( err ) exit( 1 );
3478 }
3479 
3480 //===========================================================================================================================
3481 //	BrowseResolveCallback
3482 //===========================================================================================================================
3483 
3484 static void DNSSD_API
BrowseResolveCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inFullName,const char * inHostname,uint16_t inPort,uint16_t inTXTLen,const unsigned char * inTXTPtr,void * inContext)3485 	BrowseResolveCallback(
3486 		DNSServiceRef			inSDRef,
3487 		DNSServiceFlags			inFlags,
3488 		uint32_t				inInterfaceIndex,
3489 		DNSServiceErrorType		inError,
3490 		const char *			inFullName,
3491 		const char *			inHostname,
3492 		uint16_t				inPort,
3493 		uint16_t				inTXTLen,
3494 		const unsigned char *	inTXTPtr,
3495 		void *					inContext )
3496 {
3497 	struct timeval		now;
3498 	char				errorStr[ 64 ];
3499 
3500 	Unused( inSDRef );
3501 	Unused( inFlags );
3502 	Unused( inContext );
3503 
3504 	gettimeofday( &now, NULL );
3505 
3506 	if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
3507 
3508 	FPrintF( stdout, "%{du:time}  %s can be reached at %s:%u (interface %d)%?s\n",
3509 		&now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
3510 	if( inTXTLen == 1 )
3511 	{
3512 		FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
3513 	}
3514 	else
3515 	{
3516 		FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
3517 	}
3518 }
3519 
3520 //===========================================================================================================================
3521 //	GetAddrInfoCmd
3522 //===========================================================================================================================
3523 
3524 typedef struct
3525 {
3526 	DNSServiceRef			mainRef;		// Main sdRef for shared connection.
3527 	DNSServiceRef			opRef;			// sdRef for the DNSServiceGetAddrInfo operation.
3528 	const char *			name;			// Hostname to resolve.
3529 	DNSServiceFlags			flags;			// Flags argument for DNSServiceGetAddrInfo().
3530 	DNSServiceProtocol		protocols;		// Protocols argument for DNSServiceGetAddrInfo().
3531 	uint32_t				ifIndex;		// Interface index argument for DNSServiceGetAddrInfo().
3532 	int						timeLimitSecs;	// Time limit for the DNSServiceGetAddrInfo() operation in seconds.
3533 	Boolean					printedHeader;	// True if the results header has been printed.
3534 	Boolean					oneShotMode;	// True if command is done after the first set of results (one-shot mode).
3535 	Boolean					needIPv4;		// True if in one-shot mode and an IPv4 result is needed.
3536 	Boolean					needIPv6;		// True if in one-shot mode and an IPv6 result is needed.
3537 
3538 }	GetAddrInfoContext;
3539 
3540 static void	GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext );
3541 static void	GetAddrInfoContextFree( GetAddrInfoContext *inContext );
3542 static void DNSSD_API
3543 	GetAddrInfoCallback(
3544 		DNSServiceRef			inSDRef,
3545 		DNSServiceFlags			inFlags,
3546 		uint32_t				inInterfaceIndex,
3547 		DNSServiceErrorType		inError,
3548 		const char *			inHostname,
3549 		const struct sockaddr *	inSockAddr,
3550 		uint32_t				inTTL,
3551 		void *					inContext );
3552 
GetAddrInfoCmd(void)3553 static void	GetAddrInfoCmd( void )
3554 {
3555 	OSStatus					err;
3556 	DNSServiceRef				sdRef;
3557 	GetAddrInfoContext *		context			= NULL;
3558 	dispatch_source_t			signalSource	= NULL;
3559 	int							useMainConnection;
3560 
3561 	// Set up SIGINT handler.
3562 
3563 	signal( SIGINT, SIG_IGN );
3564 	err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
3565 	require_noerr( err, exit );
3566 	dispatch_resume( signalSource );
3567 
3568 	// Check command parameters.
3569 
3570 	if( gGetAddrInfo_TimeLimitSecs < 0 )
3571 	{
3572 		FPrintF( stderr, "Invalid time limit: %d s.\n", gGetAddrInfo_TimeLimitSecs );
3573 		err = kParamErr;
3574 		goto exit;
3575 	}
3576 
3577 #if( MDNSRESPONDER_PROJECT )
3578 	if( gFallbackDNSService )
3579 	{
3580 		err = _SetDefaultFallbackDNSService( gFallbackDNSService );
3581 		require_noerr_quiet( err, exit );
3582 	}
3583 #endif
3584 	// Create context.
3585 
3586 	context = (GetAddrInfoContext *) calloc( 1, sizeof( *context ) );
3587 	require_action( context, exit, err = kNoMemoryErr );
3588 
3589 	// Create main connection.
3590 
3591 	if( gConnectionOpt )
3592 	{
3593 		err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3594 		require_noerr_quiet( err, exit );
3595 		useMainConnection = true;
3596 	}
3597 	else
3598 	{
3599 		useMainConnection = false;
3600 	}
3601 
3602 	// Get flags.
3603 
3604 	context->flags = GetDNSSDFlagsFromOpts();
3605 	if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3606 
3607 	// Get interface.
3608 
3609 	err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3610 	require_noerr_quiet( err, exit );
3611 
3612 	// Set remaining parameters.
3613 
3614 	context->name			= gGetAddrInfo_Name;
3615 	context->timeLimitSecs	= gGetAddrInfo_TimeLimitSecs;
3616 	if( gGetAddrInfo_ProtocolIPv4 ) context->protocols |= kDNSServiceProtocol_IPv4;
3617 	if( gGetAddrInfo_ProtocolIPv6 ) context->protocols |= kDNSServiceProtocol_IPv6;
3618 	if( gGetAddrInfo_OneShot )
3619 	{
3620 		context->oneShotMode	= true;
3621 		context->needIPv4		= ( gGetAddrInfo_ProtocolIPv4 || !gGetAddrInfo_ProtocolIPv6 ) ? true : false;
3622 		context->needIPv6		= ( gGetAddrInfo_ProtocolIPv6 || !gGetAddrInfo_ProtocolIPv4 ) ? true : false;
3623 	}
3624 
3625 	// Print prologue.
3626 
3627 	GetAddrInfoPrintPrologue( context );
3628 
3629 	// Start operation.
3630 
3631 	sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3632 	err = DNSServiceGetAddrInfo( &sdRef, context->flags, context->ifIndex, context->protocols, context->name,
3633 		GetAddrInfoCallback, context );
3634 	require_noerr( err, exit );
3635 
3636 	context->opRef = sdRef;
3637 	if( !useMainConnection )
3638 	{
3639 		err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3640 		require_noerr( err, exit );
3641 	}
3642 
3643 	// Set time limit.
3644 
3645 	if( context->timeLimitSecs > 0 )
3646 	{
3647 		dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
3648 			kExitReason_TimeLimit, Exit );
3649 	}
3650 	dispatch_main();
3651 
3652 exit:
3653 	dispatch_source_forget( &signalSource );
3654 	if( context ) GetAddrInfoContextFree( context );
3655 	if( err ) exit( 1 );
3656 }
3657 
3658 //===========================================================================================================================
3659 //	GetAddrInfoPrintPrologue
3660 //===========================================================================================================================
3661 
GetAddrInfoPrintPrologue(const GetAddrInfoContext * inContext)3662 static void	GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext )
3663 {
3664 	const int		timeLimitSecs = inContext->timeLimitSecs;
3665 	char			ifName[ kInterfaceNameBufLen ];
3666 
3667 	InterfaceIndexToName( inContext->ifIndex, ifName );
3668 
3669 	FPrintF( stdout, "Flags:      %#{flags}\n",		inContext->flags, kDNSServiceFlagsDescriptors );
3670 	FPrintF( stdout, "Interface:  %d (%s)\n",		(int32_t) inContext->ifIndex, ifName );
3671 	FPrintF( stdout, "Protocols:  %#{flags}\n",		inContext->protocols, kDNSServiceProtocolDescriptors );
3672 	FPrintF( stdout, "Name:       %s\n",			inContext->name );
3673 	FPrintF( stdout, "Mode:       %s\n",			inContext->oneShotMode ? "one-shot" : "continuous" );
3674 	FPrintF( stdout, "Time limit: " );
3675 	if( timeLimitSecs > 0 )	FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3676 	else					FPrintF( stdout, "∞\n" );
3677 	FPrintF( stdout, "Start time: %{du:time}\n",	NULL );
3678 	FPrintF( stdout, "---\n" );
3679 }
3680 
3681 //===========================================================================================================================
3682 //	GetAddrInfoContextFree
3683 //===========================================================================================================================
3684 
GetAddrInfoContextFree(GetAddrInfoContext * inContext)3685 static void	GetAddrInfoContextFree( GetAddrInfoContext *inContext )
3686 {
3687 	DNSServiceForget( &inContext->opRef );
3688 	DNSServiceForget( &inContext->mainRef );
3689 	free( inContext );
3690 }
3691 
3692 //===========================================================================================================================
3693 //	GetAddrInfoCallback
3694 //===========================================================================================================================
3695 
3696 static void DNSSD_API
GetAddrInfoCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inHostname,const struct sockaddr * inSockAddr,uint32_t inTTL,void * inContext)3697 	GetAddrInfoCallback(
3698 		DNSServiceRef			inSDRef,
3699 		DNSServiceFlags			inFlags,
3700 		uint32_t				inInterfaceIndex,
3701 		DNSServiceErrorType		inError,
3702 		const char *			inHostname,
3703 		const struct sockaddr *	inSockAddr,
3704 		uint32_t				inTTL,
3705 		void *					inContext )
3706 {
3707 	GetAddrInfoContext * const		context = (GetAddrInfoContext *) inContext;
3708 	struct timeval					now;
3709 	OSStatus						err;
3710 	const char *					addrStr;
3711 	char							addrStrBuf[ kSockAddrStringMaxSize ];
3712 
3713 	Unused( inSDRef );
3714 
3715 	gettimeofday( &now, NULL );
3716 
3717 	switch( inError )
3718 	{
3719 		case kDNSServiceErr_NoError:
3720 		case kDNSServiceErr_NoSuchRecord:
3721 			err = kNoErr;
3722 			break;
3723 
3724 		case kDNSServiceErr_Timeout:
3725 			Exit( kExitReason_Timeout );
3726 
3727 		default:
3728 			err = inError;
3729 			goto exit;
3730 	}
3731 
3732 	if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
3733 	{
3734 		dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
3735 		err = kTypeErr;
3736 		goto exit;
3737 	}
3738 
3739 	if( !inError )
3740 	{
3741 		err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf );
3742 		require_noerr( err, exit );
3743 		addrStr = addrStrBuf;
3744 	}
3745 	else
3746 	{
3747 		addrStr = ( inSockAddr->sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr;
3748 	}
3749 
3750 	if( !context->printedHeader )
3751 	{
3752 		FPrintF( stdout, "%-26s  %-16s IF %-30s %-34s %6s\n", "Timestamp", "Flags", "Hostname", "Address", "TTL" );
3753 		context->printedHeader = true;
3754 	}
3755 	FPrintF( stdout, "%{du:time}  %{du:cbflags} %2d %-30s %-34s %6u\n",
3756 		&now, inFlags, (int32_t) inInterfaceIndex, inHostname, addrStr, inTTL );
3757 
3758 	if( context->oneShotMode )
3759 	{
3760 		if( inFlags & kDNSServiceFlagsAdd )
3761 		{
3762 			if( inSockAddr->sa_family == AF_INET )	context->needIPv4 = false;
3763 			else									context->needIPv6 = false;
3764 		}
3765 		if( !( inFlags & kDNSServiceFlagsMoreComing ) && !context->needIPv4 && !context->needIPv6 )
3766 		{
3767 			Exit( kExitReason_OneShotDone );
3768 		}
3769 	}
3770 
3771 exit:
3772 	if( err ) exit( 1 );
3773 }
3774 
3775 //===========================================================================================================================
3776 //	QueryRecordCmd
3777 //===========================================================================================================================
3778 
3779 typedef struct
3780 {
3781 	DNSServiceRef		mainRef;		// Main sdRef for shared connection.
3782 	DNSServiceRef		opRef;			// sdRef for the DNSServiceQueryRecord operation.
3783 	const char *		recordName;		// Resource record name argument for DNSServiceQueryRecord().
3784 	DNSServiceFlags		flags;			// Flags argument for DNSServiceQueryRecord().
3785 	uint32_t			ifIndex;		// Interface index argument for DNSServiceQueryRecord().
3786 	int					timeLimitSecs;	// Time limit for the DNSServiceQueryRecord() operation in seconds.
3787 	uint16_t			recordType;		// Resource record type argument for DNSServiceQueryRecord().
3788 	Boolean				printedHeader;	// True if the results header was printed.
3789 	Boolean				oneShotMode;	// True if command is done after the first set of results (one-shot mode).
3790 	Boolean				gotRecord;		// True if in one-shot mode and received at least one record of the desired type.
3791 	Boolean				printRawRData;	// True if RDATA results are not to be formatted when printed.
3792 
3793 }	QueryRecordContext;
3794 
3795 static void	QueryRecordPrintPrologue( const QueryRecordContext *inContext );
3796 static void	QueryRecordContextFree( QueryRecordContext *inContext );
3797 static void DNSSD_API
3798 	QueryRecordCallback(
3799 		DNSServiceRef			inSDRef,
3800 		DNSServiceFlags			inFlags,
3801 		uint32_t				inInterfaceIndex,
3802 		DNSServiceErrorType		inError,
3803 		const char *			inFullName,
3804 		uint16_t				inType,
3805 		uint16_t				inClass,
3806 		uint16_t				inRDataLen,
3807 		const void *			inRDataPtr,
3808 		uint32_t				inTTL,
3809 		void *					inContext );
3810 
QueryRecordCmd(void)3811 static void	QueryRecordCmd( void )
3812 {
3813 	OSStatus					err;
3814 	DNSServiceRef				sdRef;
3815 	QueryRecordContext *		context			= NULL;
3816 	dispatch_source_t			signalSource	= NULL;
3817 	int							useMainConnection;
3818 
3819 	// Set up SIGINT handler.
3820 
3821 	signal( SIGINT, SIG_IGN );
3822 	err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
3823 	require_noerr( err, exit );
3824 	dispatch_resume( signalSource );
3825 
3826 	// Create context.
3827 
3828 	context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
3829 	require_action( context, exit, err = kNoMemoryErr );
3830 
3831 	// Check command parameters.
3832 
3833 	if( gQueryRecord_TimeLimitSecs < 0 )
3834 	{
3835 		FPrintF( stderr, "Invalid time limit: %d seconds.\n", gQueryRecord_TimeLimitSecs );
3836 		err = kParamErr;
3837 		goto exit;
3838 	}
3839 
3840 #if( MDNSRESPONDER_PROJECT )
3841 	if( gFallbackDNSService )
3842 	{
3843 		err = _SetDefaultFallbackDNSService( gFallbackDNSService );
3844 		require_noerr_quiet( err, exit );
3845 	}
3846 #endif
3847 	// Create main connection.
3848 
3849 	if( gConnectionOpt )
3850 	{
3851 		err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
3852 		require_noerr_quiet( err, exit );
3853 		useMainConnection = true;
3854 	}
3855 	else
3856 	{
3857 		useMainConnection = false;
3858 	}
3859 
3860 	// Get flags.
3861 
3862 	context->flags = GetDNSSDFlagsFromOpts();
3863 	if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
3864 
3865 	// Get interface.
3866 
3867 	err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
3868 	require_noerr_quiet( err, exit );
3869 
3870 	// Get record type.
3871 
3872 	err = RecordTypeFromArgString( gQueryRecord_Type, &context->recordType );
3873 	require_noerr( err, exit );
3874 
3875 	// Set remaining parameters.
3876 
3877 	context->recordName		= gQueryRecord_Name;
3878 	context->timeLimitSecs	= gQueryRecord_TimeLimitSecs;
3879 	context->oneShotMode	= gQueryRecord_OneShot	? true : false;
3880 	context->printRawRData	= gQueryRecord_RawRData	? true : false;
3881 
3882 	// Print prologue.
3883 
3884 	QueryRecordPrintPrologue( context );
3885 
3886 	// Start operation.
3887 
3888 	sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
3889 	err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
3890 		kDNSServiceClass_IN, QueryRecordCallback, context );
3891 	require_noerr( err, exit );
3892 
3893 	context->opRef = sdRef;
3894 	if( !useMainConnection )
3895 	{
3896 		err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
3897 		require_noerr( err, exit );
3898 	}
3899 
3900 	// Set time limit.
3901 
3902 	if( context->timeLimitSecs > 0 )
3903 	{
3904 		dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_TimeLimit,
3905 			Exit );
3906 	}
3907 	dispatch_main();
3908 
3909 exit:
3910 	dispatch_source_forget( &signalSource );
3911 	if( context ) QueryRecordContextFree( context );
3912 	if( err ) exit( 1 );
3913 }
3914 
3915 //===========================================================================================================================
3916 //	QueryRecordContextFree
3917 //===========================================================================================================================
3918 
QueryRecordContextFree(QueryRecordContext * inContext)3919 static void	QueryRecordContextFree( QueryRecordContext *inContext )
3920 {
3921 	DNSServiceForget( &inContext->opRef );
3922 	DNSServiceForget( &inContext->mainRef );
3923 	free( inContext );
3924 }
3925 
3926 //===========================================================================================================================
3927 //	QueryRecordPrintPrologue
3928 //===========================================================================================================================
3929 
QueryRecordPrintPrologue(const QueryRecordContext * inContext)3930 static void	QueryRecordPrintPrologue( const QueryRecordContext *inContext )
3931 {
3932 	const int		timeLimitSecs = inContext->timeLimitSecs;
3933 	char			ifName[ kInterfaceNameBufLen ];
3934 
3935 	InterfaceIndexToName( inContext->ifIndex, ifName );
3936 
3937 	FPrintF( stdout, "Flags:       %#{flags}\n",	inContext->flags, kDNSServiceFlagsDescriptors );
3938 	FPrintF( stdout, "Interface:   %d (%s)\n",		(int32_t) inContext->ifIndex, ifName );
3939 	FPrintF( stdout, "Name:        %s\n",			inContext->recordName );
3940 	FPrintF( stdout, "Type:        %s (%u)\n",		RecordTypeToString( inContext->recordType ), inContext->recordType );
3941 	FPrintF( stdout, "Mode:        %s\n",			inContext->oneShotMode ? "one-shot" : "continuous" );
3942 	FPrintF( stdout, "Time limit:  " );
3943 	if( timeLimitSecs > 0 )	FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
3944 	else					FPrintF( stdout, "∞\n" );
3945 	FPrintF( stdout, "Start time:  %{du:time}\n",	NULL );
3946 	FPrintF( stdout, "---\n" );
3947 
3948 }
3949 
3950 //===========================================================================================================================
3951 //	QueryRecordCallback
3952 //===========================================================================================================================
3953 
3954 static void DNSSD_API
QueryRecordCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inFullName,uint16_t inType,uint16_t inClass,uint16_t inRDataLen,const void * inRDataPtr,uint32_t inTTL,void * inContext)3955 	QueryRecordCallback(
3956 		DNSServiceRef			inSDRef,
3957 		DNSServiceFlags			inFlags,
3958 		uint32_t				inInterfaceIndex,
3959 		DNSServiceErrorType		inError,
3960 		const char *			inFullName,
3961 		uint16_t				inType,
3962 		uint16_t				inClass,
3963 		uint16_t				inRDataLen,
3964 		const void *			inRDataPtr,
3965 		uint32_t				inTTL,
3966 		void *					inContext )
3967 {
3968 	QueryRecordContext * const		context		= (QueryRecordContext *) inContext;
3969 	struct timeval					now;
3970 	OSStatus						err;
3971 	char *							rdataStrMem	= NULL;
3972 	const char *					rdataStr;
3973 
3974 	Unused( inSDRef );
3975 
3976 	gettimeofday( &now, NULL );
3977 
3978 	switch( inError )
3979 	{
3980 		case kDNSServiceErr_NoError:
3981 			if( !context->printRawRData ) DNSRecordDataToString( inRDataPtr, inRDataLen, inType, &rdataStrMem );
3982 			if( !rdataStrMem )
3983 			{
3984 				ASPrintF( &rdataStrMem, "%#H", inRDataPtr, (int) inRDataLen, INT_MAX );
3985 				require_action( rdataStrMem, exit, err = kNoMemoryErr );
3986 			}
3987 			rdataStr = rdataStrMem;
3988 			break;
3989 
3990 		case kDNSServiceErr_NoSuchRecord:
3991 			rdataStr = kNoSuchRecordStr;
3992 			break;
3993 
3994 		case kDNSServiceErr_NoSuchName:
3995 			rdataStr = kNoSuchNameStr;
3996 			break;
3997 
3998 		case kDNSServiceErr_Timeout:
3999 			Exit( kExitReason_Timeout );
4000 
4001 		default:
4002 			err = inError;
4003 			goto exit;
4004 	}
4005 	if( !context->printedHeader )
4006 	{
4007 		FPrintF( stdout, "%-26s  %-16s IF %-32s %-5s %-5s %6s RData\n",
4008 			"Timestamp", "Flags", "Name", "Type", "Class", "TTL" );
4009 		context->printedHeader = true;
4010 	}
4011 	FPrintF( stdout, "%{du:time}  %{du:cbflags} %2d %-32s %-5s %?-5s%?5u %6u %s\n",
4012 		&now, inFlags, (int32_t) inInterfaceIndex, inFullName, RecordTypeToString( inType ),
4013 		( inClass == kDNSServiceClass_IN ), "IN", ( inClass != kDNSServiceClass_IN ), inClass, inTTL, rdataStr );
4014 
4015 	if( context->oneShotMode )
4016 	{
4017 		if( ( inFlags & kDNSServiceFlagsAdd ) &&
4018 			( ( context->recordType == kDNSServiceType_ANY ) || ( context->recordType == inType ) ) )
4019 		{
4020 			context->gotRecord = true;
4021 		}
4022 		if( !( inFlags & kDNSServiceFlagsMoreComing ) && context->gotRecord ) Exit( kExitReason_OneShotDone );
4023 	}
4024 	err = kNoErr;
4025 
4026 exit:
4027 	ForgetMem( &rdataStrMem );
4028 	if( err ) ErrQuit( 1, "error: %#m\n", err );
4029 }
4030 
4031 //===========================================================================================================================
4032 //	RegisterCmd
4033 //===========================================================================================================================
4034 
4035 typedef struct
4036 {
4037 	DNSRecordRef		recordRef;	// Reference returned by DNSServiceAddRecord().
4038 	uint8_t *			dataPtr;	// Record data.
4039 	size_t				dataLen;	// Record data length.
4040 	uint32_t			ttl;		// Record TTL value.
4041 	uint16_t			type;		// Record type.
4042 
4043 }	ExtraRecord;
4044 
4045 typedef struct
4046 {
4047 	DNSServiceRef		opRef;				// sdRef for DNSServiceRegister operation.
4048 	const char *		name;				// Service name argument for DNSServiceRegister().
4049 	const char *		type;				// Service type argument for DNSServiceRegister().
4050 	const char *		domain;				// Domain in which advertise the service.
4051 	uint8_t *			txtPtr;				// Service TXT record data. (malloc'd)
4052 	size_t				txtLen;				// Service TXT record data len.
4053 	ExtraRecord *		extraRecords;		// Array of extra records to add to registered service.
4054 	size_t				extraRecordsCount;	// Number of extra records.
4055 	uint8_t *			updateTXTPtr;		// Pointer to record data for TXT record update. (malloc'd)
4056 	size_t				updateTXTLen;		// Length of record data for TXT record update.
4057 	uint32_t			updateTTL;			// TTL of updated TXT record.
4058 	int					updateDelayMs;		// Post-registration TXT record update delay in milliseconds.
4059 	DNSServiceFlags		flags;				// Flags argument for DNSServiceRegister().
4060 	uint32_t			ifIndex;			// Interface index argument for DNSServiceRegister().
4061 	int					lifetimeMs;			// Lifetime of the record registration in milliseconds.
4062 	uint16_t			port;				// Service instance's port number.
4063 	Boolean				printedHeader;		// True if results header was printed.
4064 	Boolean				didRegister;		// True if service was registered.
4065 
4066 }	RegisterContext;
4067 
4068 static void	RegisterPrintPrologue( const RegisterContext *inContext );
4069 static void	RegisterContextFree( RegisterContext *inContext );
4070 static void DNSSD_API
4071 	RegisterCallback(
4072 		DNSServiceRef		inSDRef,
4073 		DNSServiceFlags		inFlags,
4074 		DNSServiceErrorType	inError,
4075 		const char *		inName,
4076 		const char *		inType,
4077 		const char *		inDomain,
4078 		void *				inContext );
4079 static void	RegisterUpdate( void *inContext );
4080 
RegisterCmd(void)4081 static void	RegisterCmd( void )
4082 {
4083 	OSStatus				err;
4084 	RegisterContext *		context			= NULL;
4085 	dispatch_source_t		signalSource	= NULL;
4086 
4087 	// Set up SIGINT handler.
4088 
4089 	signal( SIGINT, SIG_IGN );
4090 	err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
4091 	require_noerr( err, exit );
4092 	dispatch_resume( signalSource );
4093 
4094 	// Create context.
4095 
4096 	context = (RegisterContext *) calloc( 1, sizeof( *context ) );
4097 	require_action( context, exit, err = kNoMemoryErr );
4098 
4099 	// Check command parameters.
4100 
4101 	if( ( gRegister_Port < 0 ) || ( gRegister_Port > UINT16_MAX ) )
4102 	{
4103 		FPrintF( stderr, "Port number %d is out-of-range.\n", gRegister_Port );
4104 		err = kParamErr;
4105 		goto exit;
4106 	}
4107 
4108 	if( ( gAddRecord_DataCount != gAddRecord_TypesCount ) || ( gAddRecord_TTLsCount != gAddRecord_TypesCount ) )
4109 	{
4110 		FPrintF( stderr, "There are missing additional record parameters.\n" );
4111 		err = kParamErr;
4112 		goto exit;
4113 	}
4114 
4115 	// Get flags.
4116 
4117 	context->flags = GetDNSSDFlagsFromOpts();
4118 
4119 	// Get interface index.
4120 
4121 	err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4122 	require_noerr_quiet( err, exit );
4123 
4124 	// Get TXT record data.
4125 
4126 	if( gRegister_TXT )
4127 	{
4128 		err = RecordDataFromArgString( gRegister_TXT, &context->txtPtr, &context->txtLen );
4129 		require_noerr_quiet( err, exit );
4130 	}
4131 
4132 	// Set remaining parameters.
4133 
4134 	context->name		= gRegister_Name;
4135 	context->type		= gRegister_Type;
4136 	context->domain		= gRegister_Domain;
4137 	context->port		= (uint16_t) gRegister_Port;
4138 	context->lifetimeMs	= gRegister_LifetimeMs;
4139 
4140 	if( gAddRecord_TypesCount > 0 )
4141 	{
4142 		size_t		i;
4143 
4144 		context->extraRecords = (ExtraRecord *) calloc( gAddRecord_TypesCount, sizeof( ExtraRecord ) );
4145 		require_action( context, exit, err = kNoMemoryErr );
4146 		context->extraRecordsCount = gAddRecord_TypesCount;
4147 
4148 		for( i = 0; i < gAddRecord_TypesCount; ++i )
4149 		{
4150 			ExtraRecord * const		extraRecord = &context->extraRecords[ i ];
4151 
4152 			err = RecordTypeFromArgString( gAddRecord_Types[ i ], &extraRecord->type );
4153 			require_noerr( err, exit );
4154 
4155 			err = StringToUInt32( gAddRecord_TTLs[ i ], &extraRecord->ttl );
4156 			if( err )
4157 			{
4158 				FPrintF( stderr, "Invalid TTL value: %s\n", gAddRecord_TTLs[ i ] );
4159 				err = kParamErr;
4160 				goto exit;
4161 			}
4162 
4163 			err = RecordDataFromArgString( gAddRecord_Data[ i ], &extraRecord->dataPtr, &extraRecord->dataLen );
4164 			require_noerr_quiet( err, exit );
4165 		}
4166 	}
4167 
4168 	if( gUpdateRecord_Data )
4169 	{
4170 		err = RecordDataFromArgString( gUpdateRecord_Data, &context->updateTXTPtr, &context->updateTXTLen );
4171 		require_noerr_quiet( err, exit );
4172 
4173 		context->updateTTL		= (uint32_t) gUpdateRecord_TTL;
4174 		context->updateDelayMs	= gUpdateRecord_DelayMs;
4175 	}
4176 
4177 	// Print prologue.
4178 
4179 	RegisterPrintPrologue( context );
4180 
4181 	// Start operation.
4182 
4183 	err = DNSServiceRegister( &context->opRef, context->flags, context->ifIndex, context->name, context->type,
4184 		context->domain, NULL, htons( context->port ), (uint16_t) context->txtLen, context->txtPtr,
4185 		RegisterCallback, context );
4186 	ForgetMem( &context->txtPtr );
4187 	require_noerr( err, exit );
4188 
4189 	err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4190 	require_noerr( err, exit );
4191 
4192 	dispatch_main();
4193 
4194 exit:
4195 	dispatch_source_forget( &signalSource );
4196 	if( context ) RegisterContextFree( context );
4197 	if( err ) exit( 1 );
4198 }
4199 
4200 //===========================================================================================================================
4201 //	RegisterPrintPrologue
4202 //===========================================================================================================================
4203 
RegisterPrintPrologue(const RegisterContext * inContext)4204 static void	RegisterPrintPrologue( const RegisterContext *inContext )
4205 {
4206 	size_t		i;
4207 	int			infinite;
4208 	char		ifName[ kInterfaceNameBufLen ];
4209 
4210 	InterfaceIndexToName( inContext->ifIndex, ifName );
4211 
4212 	FPrintF( stdout, "Flags:      %#{flags}\n",	inContext->flags, kDNSServiceFlagsDescriptors );
4213 	FPrintF( stdout, "Interface:  %d (%s)\n",	(int32_t) inContext->ifIndex, ifName );
4214 	FPrintF( stdout, "Name:       %s\n",		inContext->name ? inContext->name : "<NULL>" );
4215 	FPrintF( stdout, "Type:       %s\n",		inContext->type );
4216 	FPrintF( stdout, "Domain:     %s\n",		inContext->domain ? inContext->domain : "<NULL> (default domains)" );
4217 	FPrintF( stdout, "Port:       %u\n",		inContext->port );
4218 	FPrintF( stdout, "TXT data:   %#{txt}\n",	inContext->txtPtr, inContext->txtLen );
4219 	infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
4220 	FPrintF( stdout, "Lifetime:   %?s%?d ms\n",	infinite, "∞", !infinite, inContext->lifetimeMs );
4221 	if( inContext->updateTXTPtr )
4222 	{
4223 		FPrintF( stdout, "\nUpdate record:\n" );
4224 		FPrintF( stdout, "    Delay:    %d ms\n",	( inContext->updateDelayMs > 0 ) ? inContext->updateDelayMs : 0 );
4225 		FPrintF( stdout, "    TTL:      %u%?s\n",
4226 			inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
4227 		FPrintF( stdout, "    TXT data: %#{txt}\n",	inContext->updateTXTPtr, inContext->updateTXTLen );
4228 	}
4229 	if( inContext->extraRecordsCount > 0 ) FPrintF( stdout, "\n" );
4230 	for( i = 0; i < inContext->extraRecordsCount; ++i )
4231 	{
4232 		const ExtraRecord *		record = &inContext->extraRecords[ i ];
4233 
4234 		FPrintF( stdout, "Extra record %zu:\n",		i + 1 );
4235 		FPrintF( stdout, "    Type:  %s (%u)\n",	RecordTypeToString( record->type ), record->type );
4236 		FPrintF( stdout, "    TTL:   %u%?s\n",		record->ttl, record->ttl == 0, " (system will use a default value.)" );
4237 		FPrintF( stdout, "    RData: %#H\n\n",		record->dataPtr, (int) record->dataLen, INT_MAX );
4238 	}
4239 	FPrintF( stdout, "Start time: %{du:time}\n",	NULL );
4240 	FPrintF( stdout, "---\n" );
4241 }
4242 
4243 //===========================================================================================================================
4244 //	RegisterContextFree
4245 //===========================================================================================================================
4246 
RegisterContextFree(RegisterContext * inContext)4247 static void	RegisterContextFree( RegisterContext *inContext )
4248 {
4249 	ExtraRecord *					record;
4250 	const ExtraRecord * const		end = inContext->extraRecords + inContext->extraRecordsCount;
4251 
4252 	DNSServiceForget( &inContext->opRef );
4253 	ForgetMem( &inContext->txtPtr );
4254 	for( record = inContext->extraRecords; record < end; ++record )
4255 	{
4256 		check( !record->recordRef );
4257 		ForgetMem( &record->dataPtr );
4258 	}
4259 	ForgetMem( &inContext->extraRecords );
4260 	ForgetMem( &inContext->updateTXTPtr );
4261 	free( inContext );
4262 }
4263 
4264 //===========================================================================================================================
4265 //	RegisterCallback
4266 //===========================================================================================================================
4267 
4268 static void DNSSD_API
RegisterCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,DNSServiceErrorType inError,const char * inName,const char * inType,const char * inDomain,void * inContext)4269 	RegisterCallback(
4270 		DNSServiceRef		inSDRef,
4271 		DNSServiceFlags		inFlags,
4272 		DNSServiceErrorType	inError,
4273 		const char *		inName,
4274 		const char *		inType,
4275 		const char *		inDomain,
4276 		void *				inContext )
4277 {
4278 	RegisterContext * const		context = (RegisterContext *) inContext;
4279 	OSStatus					err;
4280 	struct timeval				now;
4281 
4282 	Unused( inSDRef );
4283 
4284 	gettimeofday( &now, NULL );
4285 
4286 	if( !context->printedHeader )
4287 	{
4288 		FPrintF( stdout, "%-26s  %-16s Service\n", "Timestamp", "Flags" );
4289 		context->printedHeader = true;
4290 	}
4291 	FPrintF( stdout, "%{du:time}  %{du:cbflags} %s.%s%s %?#m\n", &now, inFlags, inName, inType, inDomain, inError, inError );
4292 
4293 	require_noerr_action_quiet( inError, exit, err = inError );
4294 
4295 	if( !context->didRegister && ( inFlags & kDNSServiceFlagsAdd ) )
4296 	{
4297 		context->didRegister = true;
4298 		if( context->updateTXTPtr )
4299 		{
4300 			if( context->updateDelayMs > 0 )
4301 			{
4302 				dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
4303 					context, RegisterUpdate );
4304 			}
4305 			else
4306 			{
4307 				RegisterUpdate( context );
4308 			}
4309 		}
4310 		if( context->extraRecordsCount > 0 )
4311 		{
4312 			ExtraRecord *					record;
4313 			const ExtraRecord * const		end = context->extraRecords + context->extraRecordsCount;
4314 
4315 			for( record = context->extraRecords; record < end; ++record )
4316 			{
4317 				err = DNSServiceAddRecord( context->opRef, &record->recordRef, 0, record->type,
4318 					(uint16_t) record->dataLen, record->dataPtr, record->ttl );
4319 				require_noerr( err, exit );
4320 			}
4321 		}
4322 		if( context->lifetimeMs == 0 )
4323 		{
4324 			Exit( kExitReason_TimeLimit );
4325 		}
4326 		else if( context->lifetimeMs > 0 )
4327 		{
4328 			dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
4329 				kExitReason_TimeLimit, Exit );
4330 		}
4331 	}
4332 	err = kNoErr;
4333 
4334 exit:
4335 	if( err ) exit( 1 );
4336 }
4337 
4338 //===========================================================================================================================
4339 //	RegisterUpdate
4340 //===========================================================================================================================
4341 
RegisterUpdate(void * inContext)4342 static void	RegisterUpdate( void *inContext )
4343 {
4344 	OSStatus					err;
4345 	RegisterContext * const		context = (RegisterContext *) inContext;
4346 
4347 	err = DNSServiceUpdateRecord( context->opRef, NULL, 0, (uint16_t) context->updateTXTLen, context->updateTXTPtr,
4348 		context->updateTTL );
4349 	require_noerr( err, exit );
4350 
4351 exit:
4352 	if( err ) exit( 1 );
4353 }
4354 
4355 //===========================================================================================================================
4356 //	RegisterRecordCmd
4357 //===========================================================================================================================
4358 
4359 typedef struct
4360 {
4361 	DNSServiceRef		conRef;			// sdRef to be initialized by DNSServiceCreateConnection().
4362 	DNSRecordRef		recordRef;		// Registered record reference.
4363 	const char *		recordName;		// Name of resource record.
4364 	uint8_t *			dataPtr;		// Pointer to resource record data.
4365 	size_t				dataLen;		// Length of resource record data.
4366 	uint32_t			ttl;			// TTL value of resource record in seconds.
4367 	uint32_t			ifIndex;		// Interface index argument for DNSServiceRegisterRecord().
4368 	DNSServiceFlags		flags;			// Flags argument for DNSServiceRegisterRecord().
4369 	int					lifetimeMs;		// Lifetime of the record registration in milliseconds.
4370 	uint16_t			recordType;		// Resource record type.
4371 	uint8_t *			updateDataPtr;	// Pointer to data for record update. (malloc'd)
4372 	size_t				updateDataLen;	// Length of data for record update.
4373 	uint32_t			updateTTL;		// TTL for updated record.
4374 	int					updateDelayMs;	// Post-registration record update delay in milliseconds.
4375 	Boolean				didRegister;	// True if the record was registered.
4376 
4377 }	RegisterRecordContext;
4378 
4379 static void	RegisterRecordPrintPrologue( const RegisterRecordContext *inContext );
4380 static void	RegisterRecordContextFree( RegisterRecordContext *inContext );
4381 static void DNSSD_API
4382 	RegisterRecordCallback(
4383 		DNSServiceRef		inSDRef,
4384 		DNSRecordRef		inRecordRef,
4385 		DNSServiceFlags		inFlags,
4386 		DNSServiceErrorType	inError,
4387 		void *				inContext );
4388 static void	RegisterRecordUpdate( void *inContext );
4389 
RegisterRecordCmd(void)4390 static void	RegisterRecordCmd( void )
4391 {
4392 	OSStatus					err;
4393 	RegisterRecordContext *		context			= NULL;
4394 	dispatch_source_t			signalSource	= NULL;
4395 
4396 	// Set up SIGINT handler.
4397 
4398 	signal( SIGINT, SIG_IGN );
4399 	err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
4400 	require_noerr( err, exit );
4401 	dispatch_resume( signalSource );
4402 
4403 	// Create context.
4404 
4405 	context = (RegisterRecordContext *) calloc( 1, sizeof( *context ) );
4406 	require_action( context, exit, err = kNoMemoryErr );
4407 
4408 	// Create connection.
4409 
4410 	err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->conRef, NULL );
4411 	require_noerr_quiet( err, exit );
4412 
4413 	// Get flags.
4414 
4415 	context->flags = GetDNSSDFlagsFromOpts();
4416 
4417 	// Get interface.
4418 
4419 	err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4420 	require_noerr_quiet( err, exit );
4421 
4422 	// Get record type.
4423 
4424 	err = RecordTypeFromArgString( gRegisterRecord_Type, &context->recordType );
4425 	require_noerr( err, exit );
4426 
4427 	// Get record data.
4428 
4429 	if( gRegisterRecord_Data )
4430 	{
4431 		err = RecordDataFromArgString( gRegisterRecord_Data, &context->dataPtr, &context->dataLen );
4432 		require_noerr_quiet( err, exit );
4433 	}
4434 
4435 	// Set remaining parameters.
4436 
4437 	context->recordName	= gRegisterRecord_Name;
4438 	context->ttl		= (uint32_t) gRegisterRecord_TTL;
4439 	context->lifetimeMs	= gRegisterRecord_LifetimeMs;
4440 
4441 	// Get update data.
4442 
4443 	if( gRegisterRecord_UpdateData )
4444 	{
4445 		err = RecordDataFromArgString( gRegisterRecord_UpdateData, &context->updateDataPtr, &context->updateDataLen );
4446 		require_noerr_quiet( err, exit );
4447 
4448 		context->updateTTL		= (uint32_t) gRegisterRecord_UpdateTTL;
4449 		context->updateDelayMs	= gRegisterRecord_UpdateDelayMs;
4450 	}
4451 
4452 	// Print prologue.
4453 
4454 	RegisterRecordPrintPrologue( context );
4455 
4456 	// Start operation.
4457 
4458 	err = DNSServiceRegisterRecord( context->conRef, &context->recordRef, context->flags, context->ifIndex,
4459 		context->recordName, context->recordType, kDNSServiceClass_IN, (uint16_t) context->dataLen, context->dataPtr,
4460 		context->ttl, RegisterRecordCallback, context );
4461 	if( err )
4462 	{
4463 		FPrintF( stderr, "DNSServiceRegisterRecord() returned %#m\n", err );
4464 		goto exit;
4465 	}
4466 
4467 	dispatch_main();
4468 
4469 exit:
4470 	dispatch_source_forget( &signalSource );
4471 	if( context ) RegisterRecordContextFree( context );
4472 	if( err ) exit( 1 );
4473 }
4474 
4475 //===========================================================================================================================
4476 //	RegisterRecordPrintPrologue
4477 //===========================================================================================================================
4478 
RegisterRecordPrintPrologue(const RegisterRecordContext * inContext)4479 static void	RegisterRecordPrintPrologue( const RegisterRecordContext *inContext )
4480 {
4481 	int			infinite;
4482 	char		ifName[ kInterfaceNameBufLen ];
4483 
4484 	InterfaceIndexToName( inContext->ifIndex, ifName );
4485 
4486 	FPrintF( stdout, "Flags:       %#{flags}\n",	inContext->flags, kDNSServiceFlagsDescriptors );
4487 	FPrintF( stdout, "Interface:   %d (%s)\n",		(int32_t) inContext->ifIndex, ifName );
4488 	FPrintF( stdout, "Name:        %s\n",			inContext->recordName );
4489 	FPrintF( stdout, "Type:        %s (%u)\n",		RecordTypeToString( inContext->recordType ), inContext->recordType );
4490 	FPrintF( stdout, "TTL:         %u\n",			inContext->ttl );
4491 	FPrintF( stdout, "Data:        %#H\n",			inContext->dataPtr, (int) inContext->dataLen, INT_MAX );
4492 	infinite = ( inContext->lifetimeMs < 0 ) ? true : false;
4493 	FPrintF( stdout, "Lifetime:    %?s%?d ms\n",	infinite, "∞", !infinite, inContext->lifetimeMs );
4494 	if( inContext->updateDataPtr )
4495 	{
4496 		FPrintF( stdout, "\nUpdate record:\n" );
4497 		FPrintF( stdout, "    Delay:    %d ms\n",	( inContext->updateDelayMs >= 0 ) ? inContext->updateDelayMs : 0 );
4498 		FPrintF( stdout, "    TTL:      %u%?s\n",
4499 			inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" );
4500 		FPrintF( stdout, "    RData:    %#H\n",		inContext->updateDataPtr, (int) inContext->updateDataLen, INT_MAX );
4501 	}
4502 	FPrintF( stdout, "Start time:  %{du:time}\n",	NULL );
4503 	FPrintF( stdout, "---\n" );
4504 }
4505 
4506 //===========================================================================================================================
4507 //	RegisterRecordContextFree
4508 //===========================================================================================================================
4509 
RegisterRecordContextFree(RegisterRecordContext * inContext)4510 static void	RegisterRecordContextFree( RegisterRecordContext *inContext )
4511 {
4512 	DNSServiceForget( &inContext->conRef );
4513 	ForgetMem( &inContext->dataPtr );
4514 	ForgetMem( &inContext->updateDataPtr );
4515 	free( inContext );
4516 }
4517 
4518 //===========================================================================================================================
4519 //	RegisterRecordCallback
4520 //===========================================================================================================================
4521 
4522 static void
RegisterRecordCallback(DNSServiceRef inSDRef,DNSRecordRef inRecordRef,DNSServiceFlags inFlags,DNSServiceErrorType inError,void * inContext)4523 	RegisterRecordCallback(
4524 		DNSServiceRef		inSDRef,
4525 		DNSRecordRef		inRecordRef,
4526 		DNSServiceFlags		inFlags,
4527 		DNSServiceErrorType	inError,
4528 		void *				inContext )
4529 {
4530 	RegisterRecordContext *		context = (RegisterRecordContext *) inContext;
4531 	struct timeval				now;
4532 
4533 	Unused( inSDRef );
4534 	Unused( inRecordRef );
4535 	Unused( inFlags );
4536 	Unused( context );
4537 
4538 	gettimeofday( &now, NULL );
4539 	FPrintF( stdout, "%{du:time} Record registration result (error %#m)\n", &now, inError );
4540 
4541 	if( !context->didRegister && !inError )
4542 	{
4543 		context->didRegister = true;
4544 		if( context->updateDataPtr )
4545 		{
4546 			if( context->updateDelayMs > 0 )
4547 			{
4548 				dispatch_after_f( dispatch_time_milliseconds( context->updateDelayMs ), dispatch_get_main_queue(),
4549 					context, RegisterRecordUpdate );
4550 			}
4551 			else
4552 			{
4553 				RegisterRecordUpdate( context );
4554 			}
4555 		}
4556 		if( context->lifetimeMs == 0 )
4557 		{
4558 			Exit( kExitReason_TimeLimit );
4559 		}
4560 		else if( context->lifetimeMs > 0 )
4561 		{
4562 			dispatch_after_f( dispatch_time_milliseconds( context->lifetimeMs ), dispatch_get_main_queue(),
4563 				kExitReason_TimeLimit, Exit );
4564 		}
4565 	}
4566 }
4567 
4568 //===========================================================================================================================
4569 //	RegisterRecordUpdate
4570 //===========================================================================================================================
4571 
RegisterRecordUpdate(void * inContext)4572 static void	RegisterRecordUpdate( void *inContext )
4573 {
4574 	OSStatus							err;
4575 	RegisterRecordContext * const		context = (RegisterRecordContext *) inContext;
4576 
4577 	err = DNSServiceUpdateRecord( context->conRef, context->recordRef, 0, (uint16_t) context->updateDataLen,
4578 		context->updateDataPtr, context->updateTTL );
4579 	require_noerr( err, exit );
4580 
4581 exit:
4582 	if( err ) exit( 1 );
4583 }
4584 
4585 //===========================================================================================================================
4586 //	ResolveCmd
4587 //===========================================================================================================================
4588 
4589 typedef struct
4590 {
4591 	DNSServiceRef		mainRef;		// Main sdRef for shared connections.
4592 	DNSServiceRef		opRef;			// sdRef for the DNSServiceResolve operation.
4593 	DNSServiceFlags		flags;			// Flags argument for DNSServiceResolve().
4594 	const char *		name;			// Service name argument for DNSServiceResolve().
4595 	const char *		type;			// Service type argument for DNSServiceResolve().
4596 	const char *		domain;			// Domain argument for DNSServiceResolve().
4597 	uint32_t			ifIndex;		// Interface index argument for DNSServiceResolve().
4598 	int					timeLimitSecs;	// Time limit for the DNSServiceResolve operation in seconds.
4599 
4600 }	ResolveContext;
4601 
4602 static void	ResolvePrintPrologue( const ResolveContext *inContext );
4603 static void	ResolveContextFree( ResolveContext *inContext );
4604 static void DNSSD_API
4605 	ResolveCallback(
4606 		DNSServiceRef			inSDRef,
4607 		DNSServiceFlags			inFlags,
4608 		uint32_t				inInterfaceIndex,
4609 		DNSServiceErrorType		inError,
4610 		const char *			inFullName,
4611 		const char *			inHostname,
4612 		uint16_t				inPort,
4613 		uint16_t				inTXTLen,
4614 		const unsigned char *	inTXTPtr,
4615 		void *					inContext );
4616 
ResolveCmd(void)4617 static void	ResolveCmd( void )
4618 {
4619 	OSStatus				err;
4620 	DNSServiceRef			sdRef;
4621 	ResolveContext *		context			= NULL;
4622 	dispatch_source_t		signalSource	= NULL;
4623 	int						useMainConnection;
4624 
4625 	// Set up SIGINT handler.
4626 
4627 	signal( SIGINT, SIG_IGN );
4628 	err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
4629 	require_noerr( err, exit );
4630 	dispatch_resume( signalSource );
4631 
4632 	// Create context.
4633 
4634 	context = (ResolveContext *) calloc( 1, sizeof( *context ) );
4635 	require_action( context, exit, err = kNoMemoryErr );
4636 
4637 	// Check command parameters.
4638 
4639 	if( gResolve_TimeLimitSecs < 0 )
4640 	{
4641 		FPrintF( stderr, "Invalid time limit: %d seconds.\n", gResolve_TimeLimitSecs );
4642 		err = kParamErr;
4643 		goto exit;
4644 	}
4645 
4646 	// Create main connection.
4647 
4648 	if( gConnectionOpt )
4649 	{
4650 		err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
4651 		require_noerr_quiet( err, exit );
4652 		useMainConnection = true;
4653 	}
4654 	else
4655 	{
4656 		useMainConnection = false;
4657 	}
4658 
4659 	// Get flags.
4660 
4661 	context->flags = GetDNSSDFlagsFromOpts();
4662 	if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
4663 
4664 	// Get interface index.
4665 
4666 	err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
4667 	require_noerr_quiet( err, exit );
4668 
4669 	// Set remaining parameters.
4670 
4671 	context->name			= gResolve_Name;
4672 	context->type			= gResolve_Type;
4673 	context->domain			= gResolve_Domain;
4674 	context->timeLimitSecs	= gResolve_TimeLimitSecs;
4675 
4676 	// Print prologue.
4677 
4678 	ResolvePrintPrologue( context );
4679 
4680 	// Start operation.
4681 
4682 	sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
4683 	err = DNSServiceResolve( &sdRef, context->flags, context->ifIndex, context->name, context->type, context->domain,
4684 		ResolveCallback, NULL );
4685 	require_noerr( err, exit );
4686 
4687 	context->opRef = sdRef;
4688 	if( !useMainConnection )
4689 	{
4690 		err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
4691 		require_noerr( err, exit );
4692 	}
4693 
4694 	// Set time limit.
4695 
4696 	if( context->timeLimitSecs > 0 )
4697 	{
4698 		dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
4699 			kExitReason_TimeLimit, Exit );
4700 	}
4701 	dispatch_main();
4702 
4703 exit:
4704 	dispatch_source_forget( &signalSource );
4705 	if( context ) ResolveContextFree( context );
4706 	if( err ) exit( 1 );
4707 }
4708 
4709 //===========================================================================================================================
4710 //	ReconfirmCmd
4711 //===========================================================================================================================
4712 
ReconfirmCmd(void)4713 static void	ReconfirmCmd( void )
4714 {
4715 	OSStatus			err;
4716 	uint8_t *			rdataPtr = NULL;
4717 	size_t				rdataLen = 0;
4718 	DNSServiceFlags		flags;
4719 	uint32_t			ifIndex;
4720 	uint16_t			type, class;
4721 	char				ifName[ kInterfaceNameBufLen ];
4722 
4723 	// Get flags.
4724 
4725 	flags = GetDNSSDFlagsFromOpts();
4726 
4727 	// Get interface index.
4728 
4729 	err = InterfaceIndexFromArgString( gInterface, &ifIndex );
4730 	require_noerr_quiet( err, exit );
4731 
4732 	// Get record type.
4733 
4734 	err = RecordTypeFromArgString( gReconfirmRecord_Type, &type );
4735 	require_noerr( err, exit );
4736 
4737 	// Get record data.
4738 
4739 	if( gReconfirmRecord_Data )
4740 	{
4741 		err = RecordDataFromArgString( gReconfirmRecord_Data, &rdataPtr, &rdataLen );
4742 		require_noerr_quiet( err, exit );
4743 	}
4744 
4745 	// Get record class.
4746 
4747 	if( gReconfirmRecord_Class )
4748 	{
4749 		err = RecordClassFromArgString( gReconfirmRecord_Class, &class );
4750 		require_noerr( err, exit );
4751 	}
4752 	else
4753 	{
4754 		class = kDNSServiceClass_IN;
4755 	}
4756 
4757 	// Print prologue.
4758 
4759 	FPrintF( stdout, "Flags:     %#{flags}\n",	flags, kDNSServiceFlagsDescriptors );
4760 	FPrintF( stdout, "Interface: %d (%s)\n",	(int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
4761 	FPrintF( stdout, "Name:      %s\n",			gReconfirmRecord_Name );
4762 	FPrintF( stdout, "Type:      %s (%u)\n",	RecordTypeToString( type ), type );
4763 	FPrintF( stdout, "Class:     %s (%u)\n",	( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
4764 	FPrintF( stdout, "Data:      %#H\n",		rdataPtr, (int) rdataLen, INT_MAX );
4765 	FPrintF( stdout, "---\n" );
4766 
4767 	err = DNSServiceReconfirmRecord( flags, ifIndex, gReconfirmRecord_Name, type, class, (uint16_t) rdataLen, rdataPtr );
4768 	FPrintF( stdout, "Error:     %#m\n", err );
4769 
4770 exit:
4771 	FreeNullSafe( rdataPtr );
4772 	if( err ) exit( 1 );
4773 }
4774 
4775 //===========================================================================================================================
4776 //	ResolvePrintPrologue
4777 //===========================================================================================================================
4778 
ResolvePrintPrologue(const ResolveContext * inContext)4779 static void	ResolvePrintPrologue( const ResolveContext *inContext )
4780 {
4781 	const int		timeLimitSecs = inContext->timeLimitSecs;
4782 	char			ifName[ kInterfaceNameBufLen ];
4783 
4784 	InterfaceIndexToName( inContext->ifIndex, ifName );
4785 
4786 	FPrintF( stdout, "Flags:      %#{flags}\n",		inContext->flags, kDNSServiceFlagsDescriptors );
4787 	FPrintF( stdout, "Interface:  %d (%s)\n",		(int32_t) inContext->ifIndex, ifName );
4788 	FPrintF( stdout, "Name:       %s\n",			inContext->name );
4789 	FPrintF( stdout, "Type:       %s\n",			inContext->type );
4790 	FPrintF( stdout, "Domain:     %s\n",			inContext->domain );
4791 	FPrintF( stdout, "Time limit: " );
4792 	if( timeLimitSecs > 0 )	FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
4793 	else					FPrintF( stdout, "∞\n" );
4794 	FPrintF( stdout, "Start time: %{du:time}\n",	NULL );
4795 	FPrintF( stdout, "---\n" );
4796 }
4797 
4798 //===========================================================================================================================
4799 //	ResolveContextFree
4800 //===========================================================================================================================
4801 
ResolveContextFree(ResolveContext * inContext)4802 static void	ResolveContextFree( ResolveContext *inContext )
4803 {
4804 	DNSServiceForget( &inContext->opRef );
4805 	DNSServiceForget( &inContext->mainRef );
4806 	free( inContext );
4807 }
4808 
4809 //===========================================================================================================================
4810 //	ResolveCallback
4811 //===========================================================================================================================
4812 
4813 static void DNSSD_API
ResolveCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inFullName,const char * inHostname,uint16_t inPort,uint16_t inTXTLen,const unsigned char * inTXTPtr,void * inContext)4814 	ResolveCallback(
4815 		DNSServiceRef			inSDRef,
4816 		DNSServiceFlags			inFlags,
4817 		uint32_t				inInterfaceIndex,
4818 		DNSServiceErrorType		inError,
4819 		const char *			inFullName,
4820 		const char *			inHostname,
4821 		uint16_t				inPort,
4822 		uint16_t				inTXTLen,
4823 		const unsigned char *	inTXTPtr,
4824 		void *					inContext )
4825 {
4826 	struct timeval		now;
4827 	char				errorStr[ 64 ];
4828 
4829 	Unused( inSDRef );
4830 	Unused( inFlags );
4831 	Unused( inContext );
4832 
4833 	gettimeofday( &now, NULL );
4834 
4835 	if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError );
4836 
4837 	FPrintF( stdout, "%{du:time}: %s can be reached at %s:%u (interface %d)%?s\n",
4838 		&now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr );
4839 	if( inTXTLen == 1 )
4840 	{
4841 		FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX );
4842 	}
4843 	else
4844 	{
4845 		FPrintF( stdout, " TXT record: %#{txt}\n", inTXTPtr, (size_t) inTXTLen );
4846 	}
4847 }
4848 
4849 //===========================================================================================================================
4850 //	GetAddrInfoPOSIXCmd
4851 //===========================================================================================================================
4852 
4853 #define AddressFamilyStr( X ) (				\
4854 	( (X) == AF_INET )		? "inet"	:	\
4855 	( (X) == AF_INET6 )		? "inet6"	:	\
4856 	( (X) == AF_UNSPEC )	? "unspec"	:	\
4857 							  "???" )
4858 
4859 typedef struct
4860 {
4861     unsigned int		flag;
4862     const char *        str;
4863 
4864 }   FlagStringPair;
4865 
4866 #define CaseFlagStringify( X )		{ (X), # X }
4867 
4868 const FlagStringPair		kGAIPOSIXFlagStringPairs[] =
4869 {
4870 #if( defined( AI_UNUSABLE ) )
4871 	CaseFlagStringify( AI_UNUSABLE ),
4872 #endif
4873 	CaseFlagStringify( AI_NUMERICSERV ),
4874 	CaseFlagStringify( AI_V4MAPPED ),
4875 	CaseFlagStringify( AI_ADDRCONFIG ),
4876 #if( defined( AI_V4MAPPED_CFG ) )
4877 	CaseFlagStringify( AI_V4MAPPED_CFG ),
4878 #endif
4879 	CaseFlagStringify( AI_ALL ),
4880 	CaseFlagStringify( AI_NUMERICHOST ),
4881 	CaseFlagStringify( AI_CANONNAME ),
4882 	CaseFlagStringify( AI_PASSIVE ),
4883 	{ 0, NULL }
4884 };
4885 
GetAddrInfoPOSIXCmd(void)4886 static void	GetAddrInfoPOSIXCmd( void )
4887 {
4888 	OSStatus					err;
4889 	struct addrinfo				hints;
4890 	struct timeval				now;
4891 	const struct addrinfo *		addrInfo;
4892 	struct addrinfo *			addrInfoList = NULL;
4893 	const FlagStringPair *		pair;
4894 
4895 	memset( &hints, 0, sizeof( hints ) );
4896 	hints.ai_socktype = SOCK_STREAM;
4897 
4898 	// Set hints address family.
4899 
4900 	if( !gGAIPOSIX_Family )										hints.ai_family = AF_UNSPEC;
4901 	else if( strcasecmp( gGAIPOSIX_Family, "inet" ) == 0 )		hints.ai_family = AF_INET;
4902 	else if( strcasecmp( gGAIPOSIX_Family, "inet6" ) == 0 )		hints.ai_family = AF_INET6;
4903 	else if( strcasecmp( gGAIPOSIX_Family, "unspec" ) == 0 )	hints.ai_family = AF_UNSPEC;
4904 	else
4905 	{
4906 		FPrintF( stderr, "Invalid address family: %s.\n", gGAIPOSIX_Family );
4907 		err = kParamErr;
4908 		goto exit;
4909 	}
4910 
4911 	// Set hints flags.
4912 
4913 	if( gGAIPOSIXFlag_AddrConfig )	hints.ai_flags |= AI_ADDRCONFIG;
4914 	if( gGAIPOSIXFlag_All )			hints.ai_flags |= AI_ALL;
4915 	if( gGAIPOSIXFlag_CanonName )	hints.ai_flags |= AI_CANONNAME;
4916 	if( gGAIPOSIXFlag_NumericHost )	hints.ai_flags |= AI_NUMERICHOST;
4917 	if( gGAIPOSIXFlag_NumericServ )	hints.ai_flags |= AI_NUMERICSERV;
4918 	if( gGAIPOSIXFlag_Passive )		hints.ai_flags |= AI_PASSIVE;
4919 	if( gGAIPOSIXFlag_V4Mapped )	hints.ai_flags |= AI_V4MAPPED;
4920 #if( defined( AI_V4MAPPED_CFG ) )
4921 	if( gGAIPOSIXFlag_V4MappedCFG )	hints.ai_flags |= AI_V4MAPPED_CFG;
4922 #endif
4923 #if( defined( AI_DEFAULT ) )
4924 	if( gGAIPOSIXFlag_Default )		hints.ai_flags |= AI_DEFAULT;
4925 #endif
4926 #if( defined( AI_UNUSABLE ) )
4927 	if( gGAIPOSIXFlag_Unusable )	hints.ai_flags |= AI_UNUSABLE;
4928 #endif
4929 
4930 #if( MDNSRESPONDER_PROJECT )
4931 	if( gFallbackDNSService )
4932 	{
4933 		err = _SetDefaultFallbackDNSService( gFallbackDNSService );
4934 		require_noerr_quiet( err, exit );
4935 	}
4936 #endif
4937 	// Print prologue.
4938 
4939 	FPrintF( stdout, "Hostname:       %s\n",	gGAIPOSIX_HostName );
4940 	FPrintF( stdout, "Servname:       %s\n",	gGAIPOSIX_ServName );
4941 	FPrintF( stdout, "Address family: %s\n",	AddressFamilyStr( hints.ai_family ) );
4942 	FPrintF( stdout, "Flags:          0x%X < ",	hints.ai_flags );
4943 	for( pair = kGAIPOSIXFlagStringPairs; pair->str != NULL; ++pair )
4944 	{
4945 		if( ( (unsigned int) hints.ai_flags ) & pair->flag ) FPrintF( stdout, "%s ", pair->str );
4946 	}
4947 	FPrintF( stdout, ">\n" );
4948 	FPrintF( stdout, "Start time:     %{du:time}\n", NULL );
4949 	FPrintF( stdout, "---\n" );
4950 
4951 	// Call getaddrinfo().
4952 
4953 	err = getaddrinfo( gGAIPOSIX_HostName, gGAIPOSIX_ServName, &hints, &addrInfoList );
4954 	gettimeofday( &now, NULL );
4955 	if( err )
4956 	{
4957 		FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
4958 	}
4959 	else
4960 	{
4961 		int		addrCount = 0;
4962 
4963 		for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next ) { ++addrCount; }
4964 
4965 		FPrintF( stdout, "Addresses (%d total):\n", addrCount );
4966 		for( addrInfo = addrInfoList; addrInfo; addrInfo = addrInfo->ai_next )
4967 		{
4968 			FPrintF( stdout, "%##a\n", addrInfo->ai_addr );
4969 		}
4970 	}
4971 	FPrintF( stdout, "---\n" );
4972 	FPrintF( stdout, "End time:       %{du:time}\n", &now );
4973 
4974 exit:
4975 	if( addrInfoList ) freeaddrinfo( addrInfoList );
4976 	if( err ) exit( 1 );
4977 }
4978 
4979 //===========================================================================================================================
4980 //	ReverseLookupCmd
4981 //===========================================================================================================================
4982 
ReverseLookupCmd(void)4983 static void	ReverseLookupCmd( void )
4984 {
4985 	OSStatus					err;
4986 	QueryRecordContext *		context			= NULL;
4987 	DNSServiceRef				sdRef;
4988 	dispatch_source_t			signalSource	= NULL;
4989 	uint32_t					ipv4Addr;
4990 	uint8_t						ipv6Addr[ 16 ];
4991 	char						recordName[ kReverseIPv6DomainNameBufLen ];
4992 	int							useMainConnection;
4993 	const char *				endPtr;
4994 
4995 	// Set up SIGINT handler.
4996 
4997 	signal( SIGINT, SIG_IGN );
4998 	err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
4999 	require_noerr( err, exit );
5000 	dispatch_resume( signalSource );
5001 
5002 	// Create context.
5003 
5004 	context = (QueryRecordContext *) calloc( 1, sizeof( *context ) );
5005 	require_action( context, exit, err = kNoMemoryErr );
5006 
5007 	// Check command parameters.
5008 
5009 	if( gReverseLookup_TimeLimitSecs < 0 )
5010 	{
5011 		FPrintF( stderr, "Invalid time limit: %d s.\n", gReverseLookup_TimeLimitSecs );
5012 		err = kParamErr;
5013 		goto exit;
5014 	}
5015 
5016 	// Create main connection.
5017 
5018 	if( gConnectionOpt )
5019 	{
5020 		err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
5021 		require_noerr_quiet( err, exit );
5022 		useMainConnection = true;
5023 	}
5024 	else
5025 	{
5026 		useMainConnection = false;
5027 	}
5028 
5029 	// Get flags.
5030 
5031 	context->flags = GetDNSSDFlagsFromOpts();
5032 	if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
5033 
5034 	// Get interface index.
5035 
5036 	err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
5037 	require_noerr_quiet( err, exit );
5038 
5039 	// Create reverse lookup record name.
5040 
5041 	err = _StringToIPv4Address( gReverseLookup_IPAddr, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix,
5042 		&ipv4Addr, NULL, NULL, NULL, &endPtr );
5043 	if( err || ( *endPtr != '\0' ) )
5044 	{
5045 		err = _StringToIPv6Address( gReverseLookup_IPAddr,
5046 			kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
5047 			ipv6Addr, NULL, NULL, NULL, &endPtr );
5048 		if( err || ( *endPtr != '\0' ) )
5049 		{
5050 			FPrintF( stderr, "Invalid IP address: \"%s\".\n", gReverseLookup_IPAddr );
5051 			err = kParamErr;
5052 			goto exit;
5053 		}
5054 		_WriteReverseIPv6DomainNameString( ipv6Addr, recordName );
5055 	}
5056 	else
5057 	{
5058 		_WriteReverseIPv4DomainNameString( ipv4Addr, recordName );
5059 	}
5060 
5061 	// Set remaining parameters.
5062 
5063 	context->recordName		= recordName;
5064 	context->recordType		= kDNSServiceType_PTR;
5065 	context->timeLimitSecs	= gReverseLookup_TimeLimitSecs;
5066 	context->oneShotMode	= gReverseLookup_OneShot ? true : false;
5067 
5068 	// Print prologue.
5069 
5070 	QueryRecordPrintPrologue( context );
5071 
5072 	// Start operation.
5073 
5074 	sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
5075 	err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType,
5076 		kDNSServiceClass_IN, QueryRecordCallback, context );
5077 	require_noerr( err, exit );
5078 
5079 	context->opRef = sdRef;
5080 	if( !useMainConnection )
5081 	{
5082 		err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
5083 		require_noerr( err, exit );
5084 	}
5085 
5086 	// Set time limit.
5087 
5088 	if( context->timeLimitSecs > 0 )
5089 	{
5090 		dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(),
5091 			kExitReason_TimeLimit, Exit );
5092 	}
5093 	dispatch_main();
5094 
5095 exit:
5096 	dispatch_source_forget( &signalSource );
5097 	if( context ) QueryRecordContextFree( context );
5098 	if( err ) exit( 1 );
5099 }
5100 
5101 //===========================================================================================================================
5102 //	PortMappingCmd
5103 //===========================================================================================================================
5104 
5105 typedef struct
5106 {
5107 	DNSServiceRef			mainRef;		// Main sdRef for shared connection.
5108 	DNSServiceRef			opRef;			// sdRef for the DNSServiceNATPortMappingCreate operation.
5109 	DNSServiceFlags			flags;			// Flags for DNSServiceNATPortMappingCreate operation.
5110 	uint32_t				ifIndex;		// Interface index argument for DNSServiceNATPortMappingCreate operation.
5111 	DNSServiceProtocol		protocols;		// Protocols argument for DNSServiceNATPortMappingCreate operation.
5112 	uint32_t				ttl;			// TTL argument for DNSServiceNATPortMappingCreate operation.
5113 	uint16_t				internalPort;	// Internal port argument for DNSServiceNATPortMappingCreate operation.
5114 	uint16_t				externalPort;	// External port argument for DNSServiceNATPortMappingCreate operation.
5115 	Boolean					printedHeader;	// True if results header was printed.
5116 
5117 }	PortMappingContext;
5118 
5119 static void	PortMappingPrintPrologue( const PortMappingContext *inContext );
5120 static void	PortMappingContextFree( PortMappingContext *inContext );
5121 static void DNSSD_API
5122 	PortMappingCallback(
5123 		DNSServiceRef		inSDRef,
5124 		DNSServiceFlags		inFlags,
5125 		uint32_t			inInterfaceIndex,
5126 		DNSServiceErrorType	inError,
5127 		uint32_t			inExternalIPv4Address,
5128 		DNSServiceProtocol	inProtocol,
5129 		uint16_t			inInternalPort,
5130 		uint16_t			inExternalPort,
5131 		uint32_t			inTTL,
5132 		void *				inContext );
5133 
PortMappingCmd(void)5134 static void	PortMappingCmd( void )
5135 {
5136 	OSStatus					err;
5137 	PortMappingContext *		context			= NULL;
5138 	DNSServiceRef				sdRef;
5139 	dispatch_source_t			signalSource	= NULL;
5140 	int							useMainConnection;
5141 
5142 	// Set up SIGINT handler.
5143 
5144 	signal( SIGINT, SIG_IGN );
5145 	err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
5146 	require_noerr( err, exit );
5147 	dispatch_resume( signalSource );
5148 
5149 	// Create context.
5150 
5151 	context = (PortMappingContext *) calloc( 1, sizeof( *context ) );
5152 	require_action( context, exit, err = kNoMemoryErr );
5153 
5154 	// Check command parameters.
5155 
5156 	if( ( gPortMapping_InternalPort < 0 ) || ( gPortMapping_InternalPort > UINT16_MAX ) )
5157 	{
5158 		FPrintF( stderr, "Internal port number %d is out-of-range.\n", gPortMapping_InternalPort );
5159 		err = kParamErr;
5160 		goto exit;
5161 	}
5162 
5163 	if( ( gPortMapping_ExternalPort < 0 ) || ( gPortMapping_ExternalPort > UINT16_MAX ) )
5164 	{
5165 		FPrintF( stderr, "External port number %d is out-of-range.\n", gPortMapping_ExternalPort );
5166 		err = kParamErr;
5167 		goto exit;
5168 	}
5169 
5170 	// Create main connection.
5171 
5172 	if( gConnectionOpt )
5173 	{
5174 		err = CreateConnectionFromArgString( gConnectionOpt, dispatch_get_main_queue(), &context->mainRef, NULL );
5175 		require_noerr_quiet( err, exit );
5176 		useMainConnection = true;
5177 	}
5178 	else
5179 	{
5180 		useMainConnection = false;
5181 	}
5182 
5183 	// Get flags.
5184 
5185 	context->flags = GetDNSSDFlagsFromOpts();
5186 	if( useMainConnection ) context->flags |= kDNSServiceFlagsShareConnection;
5187 
5188 	// Get interface index.
5189 
5190 	err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
5191 	require_noerr_quiet( err, exit );
5192 
5193 	// Set remaining parameters.
5194 
5195 	if( gPortMapping_ProtocolTCP ) context->protocols |= kDNSServiceProtocol_TCP;
5196 	if( gPortMapping_ProtocolUDP ) context->protocols |= kDNSServiceProtocol_UDP;
5197 	context->ttl			= (uint32_t) gPortMapping_TTL;
5198 	context->internalPort	= (uint16_t) gPortMapping_InternalPort;
5199 	context->externalPort	= (uint16_t) gPortMapping_ExternalPort;
5200 
5201 	// Print prologue.
5202 
5203 	PortMappingPrintPrologue( context );
5204 
5205 	// Start operation.
5206 
5207 	sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
5208 	err = DNSServiceNATPortMappingCreate( &sdRef, context->flags, context->ifIndex, context->protocols,
5209 		htons( context->internalPort ), htons( context->externalPort ), context->ttl, PortMappingCallback, context );
5210 	require_noerr( err, exit );
5211 
5212 	context->opRef = sdRef;
5213 	if( !useMainConnection )
5214 	{
5215 		err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
5216 		require_noerr( err, exit );
5217 	}
5218 
5219 	dispatch_main();
5220 
5221 exit:
5222 	dispatch_source_forget( &signalSource );
5223 	if( context ) PortMappingContextFree( context );
5224 	if( err ) exit( 1 );
5225 }
5226 
5227 //===========================================================================================================================
5228 //	PortMappingPrintPrologue
5229 //===========================================================================================================================
5230 
PortMappingPrintPrologue(const PortMappingContext * inContext)5231 static void	PortMappingPrintPrologue( const PortMappingContext *inContext )
5232 {
5233 	char		ifName[ kInterfaceNameBufLen ];
5234 
5235 	InterfaceIndexToName( inContext->ifIndex, ifName );
5236 
5237 	FPrintF( stdout, "Flags:         %#{flags}\n",		inContext->flags, kDNSServiceFlagsDescriptors );
5238 	FPrintF( stdout, "Interface:     %d (%s)\n",		(int32_t) inContext->ifIndex, ifName );
5239 	FPrintF( stdout, "Protocols:     %#{flags}\n",		inContext->protocols, kDNSServiceProtocolDescriptors );
5240 	FPrintF( stdout, "Internal Port: %u\n",				inContext->internalPort );
5241 	FPrintF( stdout, "External Port: %u\n",				inContext->externalPort );
5242 	FPrintF( stdout, "TTL:           %u%?s\n",			inContext->ttl, !inContext->ttl,
5243 		" (system will use a default value.)" );
5244 	FPrintF( stdout, "Start time:    %{du:time}\n",	NULL );
5245 	FPrintF( stdout, "---\n" );
5246 
5247 }
5248 
5249 //===========================================================================================================================
5250 //	PortMappingContextFree
5251 //===========================================================================================================================
5252 
PortMappingContextFree(PortMappingContext * inContext)5253 static void	PortMappingContextFree( PortMappingContext *inContext )
5254 {
5255 	DNSServiceForget( &inContext->opRef );
5256 	DNSServiceForget( &inContext->mainRef );
5257 	free( inContext );
5258 }
5259 
5260 //===========================================================================================================================
5261 //	PortMappingCallback
5262 //===========================================================================================================================
5263 
5264 static void DNSSD_API
PortMappingCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,uint32_t inExternalIPv4Address,DNSServiceProtocol inProtocol,uint16_t inInternalPort,uint16_t inExternalPort,uint32_t inTTL,void * inContext)5265 	PortMappingCallback(
5266 		DNSServiceRef		inSDRef,
5267 		DNSServiceFlags		inFlags,
5268 		uint32_t			inInterfaceIndex,
5269 		DNSServiceErrorType	inError,
5270 		uint32_t			inExternalIPv4Address,
5271 		DNSServiceProtocol	inProtocol,
5272 		uint16_t			inInternalPort,
5273 		uint16_t			inExternalPort,
5274 		uint32_t			inTTL,
5275 		void *				inContext )
5276 {
5277 	PortMappingContext * const		context = (PortMappingContext *) inContext;
5278 	struct timeval					now;
5279 	char							errorStr[ 128 ];
5280 
5281 	Unused( inSDRef );
5282 	Unused( inFlags );
5283 
5284 	gettimeofday( &now, NULL );
5285 
5286 	if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " (error: %#m)", inError );
5287 	if( !context->printedHeader )
5288 	{
5289 		FPrintF( stdout, "%-26s  IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" );
5290 		context->printedHeader = true;
5291 	}
5292 	FPrintF( stdout, "%{du:time}  %2u %7u %15.4a %7u %6u %#{flags}%?s\n",
5293 		&now, inInterfaceIndex, ntohs( inInternalPort), &inExternalIPv4Address, ntohs( inExternalPort ), inTTL,
5294 		inProtocol, kDNSServiceProtocolDescriptors, inError, errorStr );
5295 }
5296 
5297 #if( TARGET_OS_DARWIN )
5298 //===========================================================================================================================
5299 //	RegisterKACmd
5300 //===========================================================================================================================
5301 
5302 typedef struct
5303 {
5304 	dispatch_queue_t			queue;			// Serial queue for command's events.
5305 	dispatch_semaphore_t		doneSem;		// Semaphore to signal when underlying command operation is done.
5306 	sockaddr_ip					local;			// Connection's local IP address and port.
5307 	sockaddr_ip					remote;			// Connection's remote IP address and port.
5308 	DNSServiceFlags				flags;			// Flags to pass to DNSServiceSleepKeepalive_sockaddr().
5309 	unsigned int				timeout;		// Timeout to pass to DNSServiceSleepKeepalive_sockaddr().
5310 	DNSServiceRef				keepalive;		// DNSServiceSleepKeepalive_sockaddr operation.
5311 	dispatch_source_t			sourceSigInt;	// Dispatch source for SIGINT.
5312 	dispatch_source_t			sourceSigTerm;	// Dispatch source for SIGTERM.
5313 	OSStatus					error;			// Command's error.
5314 
5315 }	RegisterKACmdContext;
5316 
5317 static void		_RegisterKACmdFree( RegisterKACmdContext *inCmd );
5318 static void		_RegisterKACmdStart( void *inContext );
5319 static OSStatus	_RegisterKACmdGetIPAddressArgument( const char *inArgStr, const char *inArgName, sockaddr_ip *outSA );
5320 
RegisterKACmd(void)5321 static void	RegisterKACmd( void )
5322 {
5323 	OSStatus					err;
5324 	RegisterKACmdContext *		cmd = NULL;
5325 
5326 	cmd = (RegisterKACmdContext *) calloc( 1, sizeof( *cmd ) );
5327 	require_action( cmd, exit, err = kNoMemoryErr );
5328 
5329 	err = _RegisterKACmdGetIPAddressArgument( gRegisterKA_LocalAddress, "local IP address", &cmd->local );
5330 	require_noerr_quiet( err, exit );
5331 
5332 	err = _RegisterKACmdGetIPAddressArgument( gRegisterKA_RemoteAddress, "remote IP address", &cmd->remote );
5333 	require_noerr_quiet( err, exit );
5334 
5335 	err = CheckIntegerArgument( gRegisterKA_Timeout, "timeout", 0, INT_MAX );
5336 	require_noerr_quiet( err, exit );
5337 
5338 	cmd->flags		= GetDNSSDFlagsFromOpts();
5339 	cmd->timeout	= (unsigned int) gRegisterKA_Timeout;
5340 
5341 	// Start command.
5342 
5343 	cmd->queue = dispatch_queue_create( "com.apple.dnssdutil.registerka-command", DISPATCH_QUEUE_SERIAL );
5344 	require_action( cmd->queue, exit, err = kNoResourcesErr );
5345 
5346 	cmd->doneSem = dispatch_semaphore_create( 0 );
5347 	require_action( cmd->doneSem, exit, err = kNoResourcesErr );
5348 
5349 	dispatch_async_f( cmd->queue, cmd, _RegisterKACmdStart );
5350     dispatch_semaphore_wait( cmd->doneSem, DISPATCH_TIME_FOREVER );
5351 	if( cmd->error ) err = cmd->error;
5352 
5353 	FPrintF( stdout, "---\n" );
5354 	FPrintF( stdout, "End time:   %{du:time}\n", NULL );
5355 
5356 exit:
5357 	if( cmd ) _RegisterKACmdFree( cmd );
5358 	gExitCode = err ? 1 : 0;
5359 }
5360 
5361 //===========================================================================================================================
5362 
_RegisterKACmdFree(RegisterKACmdContext * inCmd)5363 static void	_RegisterKACmdFree( RegisterKACmdContext *inCmd )
5364 {
5365 	check( !inCmd->keepalive );
5366 	check( !inCmd->sourceSigInt );
5367 	check( !inCmd->sourceSigTerm );
5368 	dispatch_forget( &inCmd->queue );
5369 	dispatch_forget( &inCmd->doneSem );
5370 	free( inCmd );
5371 }
5372 
5373 //===========================================================================================================================
5374 
5375 static void				_RegisterKACmdStop( RegisterKACmdContext *inCmd, OSStatus inError );
5376 static void				_RegisterKACmdSignalHandler( void *inContext );
5377 static void DNSSD_API	_RegisterKACmdKeepaliveCallback( DNSServiceRef inSDRef, DNSServiceErrorType inError, void *inCtx );
5378 
_RegisterKACmdStart(void * inContext)5379 static void	_RegisterKACmdStart( void *inContext )
5380 {
5381 	OSStatus							err;
5382 	RegisterKACmdContext * const		cmd = (RegisterKACmdContext *) inContext;
5383 
5384 	signal( SIGINT, SIG_IGN );
5385 	err = DispatchSignalSourceCreate( SIGINT, cmd->queue, _RegisterKACmdSignalHandler, cmd, &cmd->sourceSigInt );
5386 	require_noerr( err, exit );
5387 	dispatch_resume( cmd->sourceSigInt );
5388 
5389 	signal( SIGTERM, SIG_IGN );
5390 	err = DispatchSignalSourceCreate( SIGTERM, cmd->queue, _RegisterKACmdSignalHandler, cmd, &cmd->sourceSigTerm );
5391 	require_noerr( err, exit );
5392 	dispatch_resume( cmd->sourceSigTerm );
5393 
5394 	FPrintF( stdout, "Flags:      %#{flags}\n",		cmd->flags, kDNSServiceFlagsDescriptors );
5395 	FPrintF( stdout, "Local:      %##a\n",			&cmd->local.sa );
5396 	FPrintF( stdout, "Remote:     %##a\n",			&cmd->remote.sa );
5397 	FPrintF( stdout, "Timeout:    %u\n",			cmd->timeout );
5398 	FPrintF( stdout, "Start time: %{du:time}\n",	NULL );
5399 	FPrintF( stdout, "---\n" );
5400 
5401 	if( __builtin_available( macOS 10.15.4, iOS 13.2.2, watchOS 6.2, tvOS 13.2, * ) )
5402 	{
5403 		err = DNSServiceSleepKeepalive_sockaddr( &cmd->keepalive, cmd->flags, &cmd->local.sa, &cmd->remote.sa, cmd->timeout,
5404 			_RegisterKACmdKeepaliveCallback, cmd );
5405 		require_noerr( err, exit );
5406 	}
5407 	else
5408 	{
5409 		FPrintF( stderr, "error: DNSServiceSleepKeepalive_sockaddr() is not available on this OS.\n" );
5410 		err = kUnsupportedErr;
5411 		goto exit;
5412 	}
5413 	err = DNSServiceSetDispatchQueue( cmd->keepalive, cmd->queue );
5414 	require_noerr( err, exit );
5415 
5416 exit:
5417 	if( err ) _RegisterKACmdStop( cmd, err );
5418 }
5419 
5420 //===========================================================================================================================
5421 
_RegisterKACmdGetIPAddressArgument(const char * inArgStr,const char * inArgName,sockaddr_ip * outSA)5422 static OSStatus	_RegisterKACmdGetIPAddressArgument( const char *inArgStr, const char *inArgName, sockaddr_ip *outSA )
5423 {
5424 	OSStatus		err;
5425 	sockaddr_ip		sip;
5426 
5427 	err = StringToSockAddr( inArgStr, &sip, sizeof( sip ), NULL );
5428 	if( !err && ( ( sip.sa.sa_family == AF_INET ) || ( sip.sa.sa_family == AF_INET6 ) ) )
5429 	{
5430 		if( outSA ) SockAddrCopy( &sip, outSA );
5431 	}
5432 	else
5433 	{
5434 		FPrintF( stderr, "error: Invalid %s: '%s'\n", inArgName, inArgStr );
5435 		err = kParamErr;
5436 	}
5437 	return( err );
5438 }
5439 
5440 //===========================================================================================================================
5441 
_RegisterKACmdStop(RegisterKACmdContext * inCmd,OSStatus inError)5442 static void	_RegisterKACmdStop( RegisterKACmdContext *inCmd, OSStatus inError )
5443 {
5444 	if( !inCmd->error ) inCmd->error = inError;
5445 	DNSServiceForget( &inCmd->keepalive );
5446 	dispatch_source_forget( &inCmd->sourceSigInt );
5447 	dispatch_source_forget( &inCmd->sourceSigTerm );
5448 	dispatch_semaphore_signal( inCmd->doneSem );
5449 }
5450 
5451 //===========================================================================================================================
5452 
_RegisterKACmdSignalHandler(void * inContext)5453 static void	_RegisterKACmdSignalHandler( void *inContext )
5454 {
5455 	RegisterKACmdContext * const		cmd = (RegisterKACmdContext *) inContext;
5456 
5457 	_RegisterKACmdStop( cmd, kNoErr );
5458 }
5459 
5460 //===========================================================================================================================
5461 
_RegisterKACmdKeepaliveCallback(DNSServiceRef inSDRef,DNSServiceErrorType inError,void * inCtx)5462 static void DNSSD_API	_RegisterKACmdKeepaliveCallback( DNSServiceRef inSDRef, DNSServiceErrorType inError, void *inCtx )
5463 {
5464 	RegisterKACmdContext * const		cmd = (RegisterKACmdContext *) inCtx;
5465 
5466 	Unused( inSDRef );
5467 
5468 	FPrintF( stdout, "%{du:time} Record registration result: %#m\n", NULL, inError );
5469 	if( !cmd->error ) cmd->error = inError;
5470 }
5471 #endif	// TARGET_OS_DARWIN
5472 
5473 //===========================================================================================================================
5474 //	BrowseAllCmd
5475 //===========================================================================================================================
5476 
5477 typedef struct BrowseAllConnection		BrowseAllConnection;
5478 
5479 typedef struct
5480 {
5481 	ServiceBrowserRef			browser;				// Service browser.
5482 	ServiceBrowserResults *		results;				// Results from the service browser.
5483 	BrowseAllConnection *		connectionList;			// List of connections.
5484 	dispatch_source_t			connectionTimer;		// Timer for connection timeout.
5485 	int							connectionPendingCount;	// Number of pending connections.
5486 	int							connectionTimeoutSecs;	// Timeout value for connections in seconds.
5487 
5488 }	BrowseAllContext;
5489 
5490 struct BrowseAllConnection
5491 {
5492 	BrowseAllConnection *		next;				// Next connection object in list.
5493 	sockaddr_ip					sip;				// IPv4 or IPv6 address to connect to.
5494 	uint16_t					port;				// TCP port to connect to.
5495 	AsyncConnectionRef			asyncCnx;			// AsyncConnection object to handle the actual connection.
5496 	OSStatus					status;				// Status of connection. NoErr means connection succeeded.
5497 	CFTimeInterval				connectTimeSecs;	// Time it took to connect in seconds.
5498 	int32_t						refCount;			// This object's reference count.
5499 	BrowseAllContext *			context;			// Back pointer to parent context.
5500 };
5501 
5502 static void	_BrowseAllContextFree( BrowseAllContext *inContext );
5503 static void	_BrowseAllServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext );
5504 static OSStatus
5505 	_BrowseAllConnectionCreate(
5506 		const struct sockaddr *	inSockAddr,
5507 		uint16_t				inPort,
5508 		BrowseAllContext *		inContext,
5509 		BrowseAllConnection **	outConnection );
5510 static void _BrowseAllConnectionRetain( BrowseAllConnection *inConnection );
5511 static void	_BrowseAllConnectionRelease( BrowseAllConnection *inConnection );
5512 static void	_BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg );
5513 static void	_BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg );
5514 static void	_BrowseAllExit( void *inContext );
5515 
5516 static Boolean	_IsServiceTypeTCP( const char *inServiceType );
5517 
BrowseAllCmd(void)5518 static void	BrowseAllCmd( void )
5519 {
5520 	OSStatus				err;
5521 	BrowseAllContext *		context = NULL;
5522 	size_t					i;
5523 	uint32_t				ifIndex;
5524 	char					ifName[ kInterfaceNameBufLen ];
5525 
5526 	// Check parameters.
5527 
5528 	if( gBrowseAll_BrowseTimeSecs <= 0 )
5529 	{
5530 		FPrintF( stdout, "Invalid browse time: %d seconds.\n", gBrowseAll_BrowseTimeSecs );
5531 		err = kParamErr;
5532 		goto exit;
5533 	}
5534 
5535 	context = (BrowseAllContext *) calloc( 1, sizeof( *context ) );
5536 	require_action( context, exit, err = kNoMemoryErr );
5537 
5538 	context->connectionTimeoutSecs	= gBrowseAll_ConnectTimeout;
5539 #if( TARGET_OS_POSIX )
5540 	// Increase the open file descriptor limit for connection sockets.
5541 
5542 	if( context->connectionTimeoutSecs > 0 )
5543 	{
5544 		struct rlimit		fdLimits;
5545 
5546 		err = getrlimit( RLIMIT_NOFILE, &fdLimits );
5547 		err = map_global_noerr_errno( err );
5548 		require_noerr( err, exit );
5549 
5550 		if( fdLimits.rlim_cur < 4096 )
5551 		{
5552 			fdLimits.rlim_cur = 4096;
5553 			err = setrlimit( RLIMIT_NOFILE, &fdLimits );
5554 			err = map_global_noerr_errno( err );
5555 			require_noerr( err, exit );
5556 		}
5557 	}
5558 #endif
5559 
5560 	// Get interface index.
5561 
5562 	err = InterfaceIndexFromArgString( gInterface, &ifIndex );
5563 	require_noerr_quiet( err, exit );
5564 
5565 	// Print prologue.
5566 
5567 	FPrintF( stdout, "Interface:       %d (%s)\n",	(int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
5568 	FPrintF( stdout, "Service types:   ");
5569 	if( gBrowseAll_ServiceTypesCount > 0 )
5570 	{
5571 		FPrintF( stdout, "%s", gBrowseAll_ServiceTypes[ 0 ] );
5572 		for( i = 1; i < gBrowseAll_ServiceTypesCount; ++i )
5573 		{
5574 			FPrintF( stdout, ", %s", gBrowseAll_ServiceTypes[ i ] );
5575 		}
5576 		FPrintF( stdout, "\n" );
5577 	}
5578 	else
5579 	{
5580 		FPrintF( stdout, "all services\n" );
5581 	}
5582 	FPrintF( stdout, "Domain:          %s\n", gBrowseAll_Domain ? gBrowseAll_Domain : "default domains" );
5583 	FPrintF( stdout, "Browse time:     %d second%?c\n", gBrowseAll_BrowseTimeSecs, gBrowseAll_BrowseTimeSecs != 1, 's' );
5584 	FPrintF( stdout, "Connect timeout: %d second%?c\n",
5585 		context->connectionTimeoutSecs, context->connectionTimeoutSecs != 1, 's' );
5586 	FPrintF( stdout, "IncludeAWDL:     %s\n", gDNSSDFlag_IncludeAWDL ? "yes" : "no" );
5587 	FPrintF( stdout, "Start time:      %{du:time}\n", NULL );
5588 	FPrintF( stdout, "---\n" );
5589 
5590 	err = ServiceBrowserCreate( dispatch_get_main_queue(), ifIndex, gBrowseAll_Domain,
5591 		(unsigned int) gBrowseAll_BrowseTimeSecs, gDNSSDFlag_IncludeAWDL ? true : false, &context->browser );
5592 	require_noerr( err, exit );
5593 
5594 	for( i = 0; i < gBrowseAll_ServiceTypesCount; ++i )
5595 	{
5596 		err = ServiceBrowserAddServiceType( context->browser, gBrowseAll_ServiceTypes[ i ] );
5597 		require_noerr( err, exit );
5598 	}
5599 	ServiceBrowserSetCallback( context->browser, _BrowseAllServiceBrowserCallback, context );
5600 	ServiceBrowserStart( context->browser );
5601 	dispatch_main();
5602 
5603 exit:
5604 	if( context ) _BrowseAllContextFree( context );
5605 	if( err ) exit( 1 );
5606 }
5607 
5608 //===========================================================================================================================
5609 //	_BrowseAllContextFree
5610 //===========================================================================================================================
5611 
_BrowseAllContextFree(BrowseAllContext * inContext)5612 static void	_BrowseAllContextFree( BrowseAllContext *inContext )
5613 {
5614 	check( !inContext->browser );
5615 	check( !inContext->connectionTimer );
5616 	check( !inContext->connectionList );
5617 	ForgetServiceBrowserResults( &inContext->results );
5618 	free( inContext );
5619 }
5620 
5621 //===========================================================================================================================
5622 //	_BrowseAllServiceBrowserCallback
5623 //===========================================================================================================================
5624 
5625 #define kDiscardProtocolPort		9
5626 
_BrowseAllServiceBrowserCallback(ServiceBrowserResults * inResults,OSStatus inError,void * inContext)5627 static void	_BrowseAllServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext )
5628 {
5629 	OSStatus						err;
5630 	BrowseAllContext * const		context = (BrowseAllContext *) inContext;
5631 	SBRDomain *						domain;
5632 	SBRServiceType *				type;
5633 	SBRServiceInstance *			instance;
5634 	SBRIPAddress *					ipaddr;
5635 
5636 	Unused( inError );
5637 
5638 	require_action( inResults, exit, err = kUnexpectedErr );
5639 
5640 	check( !context->results );
5641 	context->results = inResults;
5642 	ServiceBrowserResultsRetain( context->results );
5643 
5644 	check( context->connectionPendingCount == 0 );
5645 	if( context->connectionTimeoutSecs > 0 )
5646 	{
5647 		BrowseAllConnection *			connection;
5648 		BrowseAllConnection **			connectionPtr = &context->connectionList;
5649 		char							destination[ kSockAddrStringMaxSize ];
5650 
5651 		for( domain = context->results->domainList; domain; domain = domain->next )
5652 		{
5653 			for( type = domain->typeList; type; type = type->next )
5654 			{
5655 				if( !_IsServiceTypeTCP( type->name ) ) continue;
5656 				for( instance = type->instanceList; instance; instance = instance->next )
5657 				{
5658 					if( instance->port == kDiscardProtocolPort ) continue;
5659 					for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
5660 					{
5661 						err = _BrowseAllConnectionCreate( &ipaddr->sip.sa, instance->port, context, &connection );
5662 						require_noerr( err, exit );
5663 
5664 						*connectionPtr = connection;
5665 						 connectionPtr = &connection->next;
5666 
5667 						err = SockAddrToString( &ipaddr->sip, kSockAddrStringFlagsNoPort, destination );
5668 						check_noerr( err );
5669 						if( !err )
5670 						{
5671 							err = AsyncConnection_Connect( &connection->asyncCnx, destination, -instance->port,
5672 								kAsyncConnectionFlag_P2P, kAsyncConnectionNoTimeout,
5673 								kSocketBufferSize_DontSet, kSocketBufferSize_DontSet,
5674 								_BrowseAllConnectionProgress, connection, _BrowseAllConnectionHandler, connection,
5675 								dispatch_get_main_queue() );
5676 							check_noerr( err );
5677 						}
5678 						if( !err )
5679 						{
5680 							_BrowseAllConnectionRetain( connection );
5681 							connection->status = kInProgressErr;
5682 							++context->connectionPendingCount;
5683 						}
5684 						else
5685 						{
5686 							connection->status = err;
5687 						}
5688 					}
5689 				}
5690 			}
5691 		}
5692 	}
5693 
5694 	if( context->connectionPendingCount > 0 )
5695 	{
5696 		check( !context->connectionTimer );
5697 		err = DispatchTimerCreate( dispatch_time_seconds( context->connectionTimeoutSecs ), DISPATCH_TIME_FOREVER,
5698 			100 * kNanosecondsPerMillisecond, NULL, _BrowseAllExit, NULL, context, &context->connectionTimer );
5699 		require_noerr( err, exit );
5700 		dispatch_resume( context->connectionTimer );
5701 	}
5702 	else
5703 	{
5704 		dispatch_async_f( dispatch_get_main_queue(), context, _BrowseAllExit );
5705 	}
5706 	err = kNoErr;
5707 
5708 exit:
5709 	ForgetCF( &context->browser );
5710 	if( err ) exit( 1 );
5711 }
5712 
5713 //===========================================================================================================================
5714 //	_BrowseAllConnectionCreate
5715 //===========================================================================================================================
5716 
5717 static OSStatus
_BrowseAllConnectionCreate(const struct sockaddr * inSockAddr,uint16_t inPort,BrowseAllContext * inContext,BrowseAllConnection ** outConnection)5718 	_BrowseAllConnectionCreate(
5719 		const struct sockaddr *	inSockAddr,
5720 		uint16_t				inPort,
5721 		BrowseAllContext *		inContext,
5722 		BrowseAllConnection **	outConnection )
5723 {
5724 	OSStatus					err;
5725 	BrowseAllConnection *		obj;
5726 
5727 	obj = (BrowseAllConnection *) calloc( 1, sizeof( *obj ) );
5728 	require_action( obj, exit, err = kNoMemoryErr );
5729 
5730 	obj->refCount	= 1;
5731 	SockAddrCopy( inSockAddr, &obj->sip );
5732 	obj->port		= inPort;
5733 	obj->context	= inContext;
5734 
5735 	*outConnection = obj;
5736 	err = kNoErr;
5737 
5738 exit:
5739 	return( err );
5740 }
5741 
5742 //===========================================================================================================================
5743 //	_BrowseAllConnectionRetain
5744 //===========================================================================================================================
5745 
_BrowseAllConnectionRetain(BrowseAllConnection * inConnection)5746 static void _BrowseAllConnectionRetain( BrowseAllConnection *inConnection )
5747 {
5748 	++inConnection->refCount;
5749 }
5750 
5751 //===========================================================================================================================
5752 //	_BrowseAllConnectionRelease
5753 //===========================================================================================================================
5754 
_BrowseAllConnectionRelease(BrowseAllConnection * inConnection)5755 static void	_BrowseAllConnectionRelease( BrowseAllConnection *inConnection )
5756 {
5757 	if( --inConnection->refCount == 0 ) free( inConnection );
5758 }
5759 
5760 //===========================================================================================================================
5761 //	_BrowseAllConnectionProgress
5762 //===========================================================================================================================
5763 
_BrowseAllConnectionProgress(int inPhase,const void * inDetails,void * inArg)5764 static void	_BrowseAllConnectionProgress( int inPhase, const void *inDetails, void *inArg )
5765 {
5766 	BrowseAllConnection * const		connection = (BrowseAllConnection *) inArg;
5767 
5768 	if( inPhase == kAsyncConnectionPhase_Connected )
5769 	{
5770 		const AsyncConnectedInfo * const		info = (AsyncConnectedInfo *) inDetails;
5771 
5772 		connection->connectTimeSecs = info->connectSecs;
5773 	}
5774 }
5775 
5776 //===========================================================================================================================
5777 //	_BrowseAllConnectionHandler
5778 //===========================================================================================================================
5779 
_BrowseAllConnectionHandler(SocketRef inSock,OSStatus inError,void * inArg)5780 static void	_BrowseAllConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg )
5781 {
5782 	BrowseAllConnection * const		connection	= (BrowseAllConnection *) inArg;
5783 	BrowseAllContext * const		context		= connection->context;
5784 
5785 	connection->status = inError;
5786 	ForgetSocket( &inSock );
5787 	if( context )
5788 	{
5789 		check( context->connectionPendingCount > 0 );
5790 		if( ( --context->connectionPendingCount == 0 ) && context->connectionTimer )
5791 		{
5792 			dispatch_source_forget( &context->connectionTimer );
5793 			dispatch_async_f( dispatch_get_main_queue(), context, _BrowseAllExit );
5794 		}
5795 	}
5796 	_BrowseAllConnectionRelease( connection );
5797 }
5798 
5799 //===========================================================================================================================
5800 //	_BrowseAllExit
5801 //===========================================================================================================================
5802 
5803 #define Indent( X )		( (X) * 4 ), ""
5804 
_BrowseAllExit(void * inContext)5805 static void	_BrowseAllExit( void *inContext )
5806 {
5807 	BrowseAllContext * const		context		= (BrowseAllContext *) inContext;
5808 	SBRDomain *						domain;
5809 	SBRServiceType *				type;
5810 	SBRServiceInstance *			instance;
5811 	SBRIPAddress *					ipaddr;
5812 	char							textBuf[ 512 ];
5813 #if( TARGET_OS_POSIX )
5814 	const Boolean					useColor	= isatty( STDOUT_FILENO ) ? true : false;
5815 #endif
5816 
5817 	dispatch_source_forget( &context->connectionTimer );
5818 
5819 	for( domain = context->results->domainList; domain; domain = domain->next )
5820 	{
5821 		FPrintF( stdout, "%s\n\n", domain->name );
5822 
5823 		for( type = domain->typeList; type; type = type->next )
5824 		{
5825 			const char *		description;
5826 			const Boolean		serviceTypeIsTCP = _IsServiceTypeTCP( type->name );
5827 
5828 			description = ServiceTypeDescription( type->name );
5829 			if( description )	FPrintF( stdout, "%*s" "%s (%s)\n\n",	Indent( 1 ), description, type->name );
5830 			else				FPrintF( stdout, "%*s" "%s\n\n",		Indent( 1 ), type->name );
5831 
5832 			for( instance = type->instanceList; instance; instance = instance->next )
5833 			{
5834 				char *				dst = textBuf;
5835 				char * const		lim = &textBuf[ countof( textBuf ) ];
5836 				char				ifname[ IF_NAMESIZE + 1 ];
5837 
5838 				SNPrintF_Add( &dst, lim, "%s via ", instance->name );
5839 				if( instance->ifIndex == 0 )
5840 				{
5841 					SNPrintF_Add( &dst, lim, "the Internet" );
5842 				}
5843 				else if( if_indextoname( instance->ifIndex, ifname ) )
5844 				{
5845 					NetTransportType		netType;
5846 
5847 					SocketGetInterfaceInfo( kInvalidSocketRef, ifname, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &netType );
5848 					SNPrintF_Add( &dst, lim, "%s (%s)",
5849 						( netType == kNetTransportType_Ethernet ) ? "Ethernet" : NetTransportTypeToString( netType ),
5850 						ifname );
5851 				}
5852 				else
5853 				{
5854 					SNPrintF_Add( &dst, lim, "interface index %u", instance->ifIndex );
5855 				}
5856 				FPrintF( stdout, "%*s" "%-55s %4llu.%03llu ms\n\n",
5857 					Indent( 2 ), textBuf, instance->discoverTimeUs / 1000, instance->discoverTimeUs % 1000 );
5858 
5859 				if( instance->hostname )
5860 				{
5861 					SNPrintF( textBuf, sizeof( textBuf ), "%s:%u", instance->hostname, instance->port );
5862 					FPrintF( stdout, "%*s" "%-51s %4llu.%03llu ms\n",
5863 						Indent( 3 ), textBuf, instance->resolveTimeUs / 1000, instance->resolveTimeUs % 1000 );
5864 				}
5865 				else
5866 				{
5867 					FPrintF( stdout, "%*s" "%s:%u\n", Indent( 3 ), instance->hostname, instance->port );
5868 				}
5869 
5870 				for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
5871 				{
5872 					BrowseAllConnection *		conn;
5873 					BrowseAllConnection **		connPtr;
5874 
5875 					FPrintF( stdout, "%*s" "%-##47a %4llu.%03llu ms",
5876 						Indent( 4 ), &ipaddr->sip.sa, ipaddr->resolveTimeUs / 1000, ipaddr->resolveTimeUs % 1000 );
5877 
5878 					conn = NULL;
5879 					if( serviceTypeIsTCP && ( instance->port != kDiscardProtocolPort ) )
5880 					{
5881 						for( connPtr = &context->connectionList; ( conn = *connPtr ) != NULL; connPtr = &conn->next )
5882 						{
5883 							if( ( conn->port == instance->port ) &&
5884 								( SockAddrCompareAddr( &conn->sip, &ipaddr->sip ) == 0 ) ) break;
5885 						}
5886 						if( conn )
5887 						{
5888 							if( conn->status == kInProgressErr ) conn->status = kTimeoutErr;
5889 							*connPtr = conn->next;
5890 							conn->context = NULL;
5891 							AsyncConnection_Forget( &conn->asyncCnx );
5892 						}
5893 					}
5894 
5895 					if( conn )
5896 					{
5897 						if( conn->status == kNoErr )
5898 						{
5899 							FPrintF( stdout, " (%sconnected%s in %.3f ms)\n",
5900 								useColor ? kANSIGreen : "", useColor ? kANSINormal : "", conn->connectTimeSecs * 1000 );
5901 						}
5902 						else
5903 						{
5904 							FPrintF( stdout, " (%scould not connect%s: %m)\n",
5905 								useColor ? kANSIRed : "", useColor ? kANSINormal : "", conn->status );
5906 						}
5907 						_BrowseAllConnectionRelease( conn );
5908 					}
5909 					else
5910 					{
5911 						FPrintF( stdout, " (no connection attempted)\n" );
5912 					}
5913 				}
5914 
5915 				FPrintF( stdout, "\n" );
5916 				if( instance->txtLen == 0 ) continue;
5917 
5918 				FPrintF( stdout, "%*s" "TXT record (%zu byte%?c):\n",
5919 					Indent( 3 ), instance->txtLen, instance->txtLen != 1, 's' );
5920 				if( instance->txtLen > 1 )
5921 				{
5922 					FPrintF( stdout, "%3{txt}", instance->txtPtr, instance->txtLen );
5923 				}
5924 				else
5925 				{
5926 					FPrintF( stdout, "%*s" "%#H\n", Indent( 3 ), instance->txtPtr, (int) instance->txtLen, INT_MAX );
5927 				}
5928 				FPrintF( stdout, "\n" );
5929 			}
5930 			FPrintF( stdout, "\n" );
5931 		}
5932 	}
5933 
5934 	_BrowseAllContextFree( context );
5935 	Exit( NULL );
5936 }
5937 
5938 //===========================================================================================================================
5939 //	_IsServiceTypeTCP
5940 //===========================================================================================================================
5941 
_IsServiceTypeTCP(const char * inServiceType)5942 static Boolean	_IsServiceTypeTCP( const char *inServiceType )
5943 {
5944 	OSStatus			err;
5945 	const uint8_t *		secondLabel;
5946 	uint8_t				name[ kDomainNameLengthMax ];
5947 
5948 	err = DomainNameFromString( name, inServiceType, NULL );
5949 	if( !err )
5950 	{
5951 		secondLabel = DomainNameGetNextLabel( name );
5952 		if( secondLabel && DomainNameEqual( secondLabel, (const uint8_t *) "\x04" "_tcp" ) ) return( true );
5953 	}
5954 	return( false );
5955 }
5956 
5957 //===========================================================================================================================
5958 //	GetNameInfoCmd
5959 //===========================================================================================================================
5960 
5961 const FlagStringPair		kGetNameInfoFlagStringPairs[] =
5962 {
5963 	CaseFlagStringify( NI_NUMERICSCOPE ),
5964 	CaseFlagStringify( NI_DGRAM ),
5965 	CaseFlagStringify( NI_NUMERICSERV ),
5966 	CaseFlagStringify( NI_NAMEREQD ),
5967 	CaseFlagStringify( NI_NUMERICHOST ),
5968 	CaseFlagStringify( NI_NOFQDN ),
5969 	{ 0, NULL }
5970 };
5971 
GetNameInfoCmd(void)5972 static void	GetNameInfoCmd( void )
5973 {
5974 	OSStatus					err;
5975 	sockaddr_ip					sip;
5976 	size_t						sockAddrLen;
5977 	unsigned int				flags;
5978 	const FlagStringPair *		pair;
5979 	struct timeval				now;
5980 	char						host[ NI_MAXHOST ];
5981 	char						serv[ NI_MAXSERV ];
5982 
5983 	err = StringToSockAddr( gGetNameInfo_IPAddress, &sip, sizeof( sip ), &sockAddrLen );
5984 	check_noerr( err );
5985 	if( err )
5986 	{
5987 		FPrintF( stderr, "Failed to convert \"%s\" to a sockaddr.\n", gGetNameInfo_IPAddress );
5988 		goto exit;
5989 	}
5990 
5991 	flags = 0;
5992 	if( gGetNameInfoFlag_DGram )		flags |= NI_DGRAM;
5993 	if( gGetNameInfoFlag_NameReqd )		flags |= NI_NAMEREQD;
5994 	if( gGetNameInfoFlag_NoFQDN )		flags |= NI_NOFQDN;
5995 	if( gGetNameInfoFlag_NumericHost )	flags |= NI_NUMERICHOST;
5996 	if( gGetNameInfoFlag_NumericScope )	flags |= NI_NUMERICSCOPE;
5997 	if( gGetNameInfoFlag_NumericServ )	flags |= NI_NUMERICSERV;
5998 
5999 	// Print prologue.
6000 
6001 	FPrintF( stdout, "SockAddr:   %##a\n",	&sip.sa );
6002 	FPrintF( stdout, "Flags:      0x%X < ",	flags );
6003 	for( pair = kGetNameInfoFlagStringPairs; pair->str != NULL; ++pair )
6004 	{
6005 		if( flags & pair->flag ) FPrintF( stdout, "%s ", pair->str );
6006 	}
6007 	FPrintF( stdout, ">\n" );
6008 	FPrintF( stdout, "Start time: %{du:time}\n", NULL );
6009 	FPrintF( stdout, "---\n" );
6010 
6011 	// Call getnameinfo().
6012 
6013 	err = getnameinfo( &sip.sa, (socklen_t) sockAddrLen, host, (socklen_t) sizeof( host ), serv, (socklen_t) sizeof( serv ),
6014 		(int) flags );
6015 	gettimeofday( &now, NULL );
6016 	if( err )
6017 	{
6018 		FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
6019 	}
6020 	else
6021 	{
6022 		FPrintF( stdout, "host: %s\n", host );
6023 		FPrintF( stdout, "serv: %s\n", serv );
6024 	}
6025 	FPrintF( stdout, "---\n" );
6026 	FPrintF( stdout, "End time:   %{du:time}\n", &now );
6027 
6028 exit:
6029 	gExitCode = err ? 1 : 0;
6030 }
6031 
6032 //===========================================================================================================================
6033 //	GetAddrInfoStressCmd
6034 //===========================================================================================================================
6035 
6036 typedef struct
6037 {
6038 	DNSServiceRef			mainRef;
6039 	DNSServiceRef			sdRef;
6040 	DNSServiceFlags			flags;
6041 	unsigned int			interfaceIndex;
6042 	unsigned int			connectionNumber;
6043 	unsigned int			requestCount;
6044 	unsigned int			requestCountMax;
6045 	unsigned int			requestCountLimit;
6046 	unsigned int			durationMinMs;
6047 	unsigned int			durationMaxMs;
6048 
6049 }	GAIStressContext;
6050 
6051 static void	GetAddrInfoStressEvent( void *inContext );
6052 static void	DNSSD_API
6053 	GetAddrInfoStressCallback(
6054 		DNSServiceRef			inSDRef,
6055 		DNSServiceFlags			inFlags,
6056 		uint32_t				inInterfaceIndex,
6057 		DNSServiceErrorType		inError,
6058 		const char *			inHostname,
6059 		const struct sockaddr *	inSockAddr,
6060 		uint32_t				inTTL,
6061 		void *					inContext );
6062 
GetAddrInfoStressCmd(void)6063 static void	GetAddrInfoStressCmd( void )
6064 {
6065 	OSStatus				err;
6066 	GAIStressContext *		context = NULL;
6067 	int						i;
6068 	DNSServiceFlags			flags;
6069 	uint32_t				ifIndex;
6070 	char					ifName[ kInterfaceNameBufLen ];
6071 
6072 	if( gGAIStress_TestDurationSecs < 0 )
6073 	{
6074 		FPrintF( stdout, "Invalid test duration: %d s.\n", gGAIStress_TestDurationSecs );
6075 		err = kParamErr;
6076 		goto exit;
6077 	}
6078 	if( gGAIStress_ConnectionCount <= 0 )
6079 	{
6080 		FPrintF( stdout, "Invalid simultaneous connection count: %d.\n", gGAIStress_ConnectionCount );
6081 		err = kParamErr;
6082 		goto exit;
6083 	}
6084 	if( gGAIStress_DurationMinMs <= 0 )
6085 	{
6086 		FPrintF( stdout, "Invalid minimum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMinMs );
6087 		err = kParamErr;
6088 		goto exit;
6089 	}
6090 	if( gGAIStress_DurationMaxMs <= 0 )
6091 	{
6092 		FPrintF( stdout, "Invalid maximum DNSServiceGetAddrInfo() duration: %d ms.\n", gGAIStress_DurationMaxMs );
6093 		err = kParamErr;
6094 		goto exit;
6095 	}
6096 	if( gGAIStress_DurationMinMs > gGAIStress_DurationMaxMs )
6097 	{
6098 		FPrintF( stdout, "Invalid minimum and maximum DNSServiceGetAddrInfo() durations: %d ms and %d ms.\n",
6099 			gGAIStress_DurationMinMs, gGAIStress_DurationMaxMs );
6100 		err = kParamErr;
6101 		goto exit;
6102 	}
6103 	if( gGAIStress_RequestCountMax <= 0 )
6104 	{
6105 		FPrintF( stdout, "Invalid maximum request count: %d.\n", gGAIStress_RequestCountMax );
6106 		err = kParamErr;
6107 		goto exit;
6108 	}
6109 
6110 	// Set flags.
6111 
6112 	flags = GetDNSSDFlagsFromOpts();
6113 
6114 	// Set interface index.
6115 
6116 	err = InterfaceIndexFromArgString( gInterface, &ifIndex );
6117 	require_noerr_quiet( err, exit );
6118 
6119 	for( i = 0; i < gGAIStress_ConnectionCount; ++i )
6120 	{
6121 		context = (GAIStressContext *) calloc( 1, sizeof( *context ) );
6122 		require_action( context, exit, err = kNoMemoryErr );
6123 
6124 		context->flags				= flags;
6125 		context->interfaceIndex		= ifIndex;
6126 		context->connectionNumber	= (unsigned int)( i + 1 );
6127 		context->requestCountMax	= (unsigned int) gGAIStress_RequestCountMax;
6128 		context->durationMinMs		= (unsigned int) gGAIStress_DurationMinMs;
6129 		context->durationMaxMs		= (unsigned int) gGAIStress_DurationMaxMs;
6130 
6131 		dispatch_async_f( dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
6132 		context = NULL;
6133 	}
6134 
6135 	if( gGAIStress_TestDurationSecs > 0 )
6136 	{
6137 		dispatch_after_f( dispatch_time_seconds( gGAIStress_TestDurationSecs ), dispatch_get_main_queue(), NULL, Exit );
6138 	}
6139 
6140 	FPrintF( stdout, "Flags:                %#{flags}\n",	flags, kDNSServiceFlagsDescriptors );
6141 	FPrintF( stdout, "Interface:            %d (%s)\n",		(int32_t) ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
6142 	FPrintF( stdout, "Test duration:        " );
6143 	if( gGAIStress_TestDurationSecs == 0 )
6144 	{
6145 		FPrintF( stdout, "∞\n" );
6146 	}
6147 	else
6148 	{
6149 		FPrintF( stdout, "%d s\n", gGAIStress_TestDurationSecs );
6150 	}
6151 	FPrintF( stdout, "Connection count:     %d\n",			gGAIStress_ConnectionCount );
6152 	FPrintF( stdout, "Request duration min: %d ms\n",		gGAIStress_DurationMinMs );
6153 	FPrintF( stdout, "Request duration max: %d ms\n",		gGAIStress_DurationMaxMs );
6154 	FPrintF( stdout, "Request count max:    %d\n",			gGAIStress_RequestCountMax );
6155 	FPrintF( stdout, "Start time:           %{du:time}\n",	NULL);
6156 	FPrintF( stdout, "---\n" );
6157 
6158 	dispatch_main();
6159 
6160 exit:
6161 	FreeNullSafe( context );
6162 	if( err ) exit( 1 );
6163 }
6164 
6165 //===========================================================================================================================
6166 //	GetAddrInfoStressEvent
6167 //===========================================================================================================================
6168 
6169 #define kStressRandStrLen		5
6170 
6171 #define kLowercaseAlphaCharSet		"abcdefghijklmnopqrstuvwxyz"
6172 
GetAddrInfoStressEvent(void * inContext)6173 static void	GetAddrInfoStressEvent( void *inContext )
6174 {
6175 	GAIStressContext * const		context = (GAIStressContext *) inContext;
6176 	OSStatus						err;
6177 	DNSServiceRef					sdRef;
6178 	unsigned int					nextMs;
6179 	char							randomStr[ kStressRandStrLen + 1 ];
6180 	char							hostname[ kStressRandStrLen + 4 + 1 ];
6181 	Boolean							isConnectionNew	= false;
6182 	static Boolean					printedHeader	= false;
6183 
6184 	if( !context->mainRef || ( context->requestCount >= context->requestCountLimit ) )
6185 	{
6186 		DNSServiceForget( &context->mainRef );
6187 		context->sdRef				= NULL;
6188 		context->requestCount		= 0;
6189 		context->requestCountLimit	= RandomRange( 1, context->requestCountMax );
6190 
6191 		err = DNSServiceCreateConnection( &context->mainRef );
6192 		require_noerr( err, exit );
6193 
6194 		err = DNSServiceSetDispatchQueue( context->mainRef, dispatch_get_main_queue() );
6195 		require_noerr( err, exit );
6196 
6197 		isConnectionNew = true;
6198 	}
6199 
6200 	RandomString( kLowercaseAlphaCharSet, sizeof_string( kLowercaseAlphaCharSet ), 2, kStressRandStrLen, randomStr );
6201 	SNPrintF( hostname, sizeof( hostname ), "%s.com", randomStr );
6202 
6203 	nextMs = RandomRange( context->durationMinMs, context->durationMaxMs );
6204 
6205 	if( !printedHeader )
6206 	{
6207 		FPrintF( stdout, "%-26s Conn  Hostname Dur (ms)\n", "Timestamp" );
6208 		printedHeader = true;
6209 	}
6210 	FPrintF( stdout, "%{du:time} %3u%c %9s %8u\n",
6211 		NULL, context->connectionNumber, isConnectionNew ? '*': ' ', hostname, nextMs );
6212 
6213 	DNSServiceForget( &context->sdRef );
6214 	sdRef = context->mainRef;
6215 	err = DNSServiceGetAddrInfo( &sdRef, context->flags | kDNSServiceFlagsShareConnection, context->interfaceIndex,
6216 		kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, hostname, GetAddrInfoStressCallback, NULL );
6217 	require_noerr( err, exit );
6218 	context->sdRef = sdRef;
6219 
6220 	context->requestCount++;
6221 
6222 	dispatch_after_f( dispatch_time_milliseconds( nextMs ), dispatch_get_main_queue(), context, GetAddrInfoStressEvent );
6223 
6224 exit:
6225 	if( err ) exit( 1 );
6226 }
6227 
6228 //===========================================================================================================================
6229 //	GetAddrInfoStressCallback
6230 //===========================================================================================================================
6231 
6232 static void DNSSD_API
GetAddrInfoStressCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inHostname,const struct sockaddr * inSockAddr,uint32_t inTTL,void * inContext)6233 	GetAddrInfoStressCallback(
6234 		DNSServiceRef			inSDRef,
6235 		DNSServiceFlags			inFlags,
6236 		uint32_t				inInterfaceIndex,
6237 		DNSServiceErrorType		inError,
6238 		const char *			inHostname,
6239 		const struct sockaddr *	inSockAddr,
6240 		uint32_t				inTTL,
6241 		void *					inContext )
6242 {
6243 	Unused( inSDRef );
6244 	Unused( inFlags );
6245 	Unused( inInterfaceIndex );
6246 	Unused( inError );
6247 	Unused( inHostname );
6248 	Unused( inSockAddr );
6249 	Unused( inTTL );
6250 	Unused( inContext );
6251 }
6252 
6253 //===========================================================================================================================
6254 //	DNSQueryCmd
6255 //===========================================================================================================================
6256 
6257 typedef struct
6258 {
6259 	sockaddr_ip				serverAddr;
6260 	uint64_t				sendTicks;
6261 	uint8_t *				msgPtr;
6262 	size_t					msgLen;
6263 	size_t					msgOffset;
6264 	const char *			name;
6265 	dispatch_source_t		readSource;
6266 	SocketRef				sock;
6267 	int						timeLimitSecs;
6268 	uint16_t				queryID;
6269 	uint16_t				type;
6270 	Boolean					haveTCPLen;
6271 	Boolean					useTCP;
6272 	Boolean					printRawRData;	// True if RDATA results are not to be formatted.
6273 	uint8_t					msgBuf[ 512 ];
6274 
6275 }	DNSQueryContext;
6276 
6277 static void	DNSQueryPrintPrologue( const DNSQueryContext *inContext );
6278 static void	DNSQueryReadHandler( void *inContext );
6279 static void	DNSQueryCancelHandler( void *inContext );
6280 
DNSQueryCmd(void)6281 static void	DNSQueryCmd( void )
6282 {
6283 	OSStatus				err;
6284 	DNSQueryContext *		context = NULL;
6285 	uint8_t *				msgPtr;
6286 	size_t					msgLen, sendLen;
6287 
6288 	// Check command parameters.
6289 
6290 	if( gDNSQuery_TimeLimitSecs < -1 )
6291 	{
6292 		FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSQuery_TimeLimitSecs );
6293 		err = kParamErr;
6294 		goto exit;
6295 	}
6296 	if( ( gDNSQuery_Flags < INT16_MIN ) || ( gDNSQuery_Flags > UINT16_MAX ) )
6297 	{
6298 		FPrintF( stdout, "DNS flags-and-codes value is out of the unsigned 16-bit range: 0x%08X.\n", gDNSQuery_Flags );
6299 		err = kParamErr;
6300 		goto exit;
6301 	}
6302 
6303 	// Create context.
6304 
6305 	context = (DNSQueryContext *) calloc( 1, sizeof( *context ) );
6306 	require_action( context, exit, err = kNoMemoryErr );
6307 
6308 	context->name			= gDNSQuery_Name;
6309 	context->sock			= kInvalidSocketRef;
6310 	context->timeLimitSecs	= gDNSQuery_TimeLimitSecs;
6311 	context->queryID		= (uint16_t) Random32();
6312 	context->useTCP			= gDNSQuery_UseTCP	 ? true : false;
6313 	context->printRawRData	= gDNSQuery_RawRData ? true : false;
6314 
6315 #if( TARGET_OS_DARWIN )
6316 	if( gDNSQuery_Server )
6317 #endif
6318 	{
6319 		err = StringToSockAddr( gDNSQuery_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
6320 		require_noerr( err, exit );
6321 	}
6322 #if( TARGET_OS_DARWIN )
6323 	else
6324 	{
6325 		err = GetDefaultDNSServer( &context->serverAddr );
6326 		require_noerr( err, exit );
6327 	}
6328 #endif
6329 	if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSPort );
6330 
6331 	err = RecordTypeFromArgString( gDNSQuery_Type, &context->type );
6332 	require_noerr( err, exit );
6333 
6334 	// Write query message.
6335 
6336 	check_compile_time_code( sizeof( context->msgBuf ) >= ( 2 + kDNSQueryMessageMaxLen + sizeof( dns_fixed_fields_opt ) ) );
6337 
6338 	msgPtr = context->useTCP ? &context->msgBuf[ 2 ] : context->msgBuf;
6339 	err = WriteDNSQueryMessage( msgPtr, context->queryID, (uint16_t) gDNSQuery_Flags, context->name, context->type,
6340 		kDNSServiceClass_IN, &msgLen );
6341 	require_noerr( err, exit );
6342 	check( msgLen <= UINT16_MAX );
6343 
6344 	if( gDNSQuery_DNSSEC )
6345 	{
6346 		DNSHeader * const					hdr = (DNSHeader *) msgPtr;
6347 		dns_fixed_fields_opt * const		opt = (dns_fixed_fields_opt *) &msgPtr[ msgLen ];
6348 		unsigned int						flags;
6349 
6350 		memset( opt, 0, sizeof( *opt ) );
6351 		dns_fixed_fields_opt_set_type( opt, kDNSServiceType_OPT );
6352 		dns_fixed_fields_opt_set_udp_payload_size( opt, 512 );
6353 		dns_fixed_fields_opt_set_extended_flags( opt, kDNSExtendedFlag_DNSSECOK );
6354 
6355 		flags = DNSHeaderGetFlags( hdr ) | kDNSHeaderFlag_AuthenticData;
6356 		DNSHeaderSetFlags( hdr, flags );
6357 		DNSHeaderSetAdditionalCount( hdr, 1 );
6358 		msgLen += sizeof( dns_fixed_fields_opt );
6359 	}
6360 	if( gDNSQuery_CheckingDisabled )
6361 	{
6362 		DNSHeader * const		hdr = (DNSHeader *) msgPtr;
6363 		unsigned int			flags;
6364 
6365 		flags = DNSHeaderGetFlags( hdr ) | kDNSHeaderFlag_CheckingDisabled;
6366 		DNSHeaderSetFlags( hdr, flags );
6367 	}
6368 	if( context->useTCP )
6369 	{
6370 		WriteBig16( context->msgBuf, msgLen );
6371 		sendLen = 2 + msgLen;
6372 	}
6373 	else
6374 	{
6375 		sendLen = msgLen;
6376 	}
6377 
6378 	DNSQueryPrintPrologue( context );
6379 
6380 	if( gDNSQuery_Verbose )
6381 	{
6382 		FPrintF( stdout, "DNS message to send:\n\n%{du:dnsmsg}\n", msgPtr, msgLen );
6383 		FPrintF( stdout, "---\n" );
6384 	}
6385 
6386 	if( context->useTCP )
6387 	{
6388 		// Create TCP socket.
6389 
6390 		context->sock = socket( context->serverAddr.sa.sa_family, SOCK_STREAM, IPPROTO_TCP );
6391 		err = map_socket_creation_errno( context->sock );
6392 		require_noerr( err, exit );
6393 
6394 		err = SocketConnect( context->sock, &context->serverAddr, 5 );
6395 		require_noerr( err, exit );
6396 	}
6397 	else
6398 	{
6399 		// Create UDP socket.
6400 
6401 		err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &context->sock );
6402 		require_noerr( err, exit );
6403 	}
6404 
6405 	context->sendTicks = UpTicks();
6406 	err = _SocketWriteAll( context->sock, context->msgBuf, sendLen, 5 );
6407 	require_noerr( err, exit );
6408 
6409 	if( context->timeLimitSecs == 0 ) goto exit;
6410 
6411 	err = DispatchReadSourceCreate( context->sock, NULL, DNSQueryReadHandler, DNSQueryCancelHandler, context,
6412 		&context->readSource );
6413 	require_noerr( err, exit );
6414 	dispatch_resume( context->readSource );
6415 
6416 	if( context->timeLimitSecs > 0 )
6417 	{
6418 		dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6419 			Exit );
6420 	}
6421 	dispatch_main();
6422 
6423 exit:
6424 	if( context )
6425 	{
6426 		dispatch_source_forget( &context->readSource );
6427 		ForgetSocket( &context->sock );
6428 		free( context );
6429 	}
6430 	if( err ) exit( 1 );
6431 }
6432 
6433 //===========================================================================================================================
6434 //	DNSQueryPrintPrologue
6435 //===========================================================================================================================
6436 
DNSQueryPrintPrologue(const DNSQueryContext * inContext)6437 static void	DNSQueryPrintPrologue( const DNSQueryContext *inContext )
6438 {
6439 	const int		timeLimitSecs = inContext->timeLimitSecs;
6440 
6441 	FPrintF( stdout, "Name:        %s\n",		inContext->name );
6442 	FPrintF( stdout, "Type:        %s (%u)\n",	RecordTypeToString( inContext->type ), inContext->type );
6443 	FPrintF( stdout, "Server:      %##a\n",		&inContext->serverAddr );
6444 	FPrintF( stdout, "Transport:   %s\n",		inContext->useTCP ? "TCP" : "UDP" );
6445 	FPrintF( stdout, "Time limit:  " );
6446 	if( timeLimitSecs >= 0 )	FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
6447 	else						FPrintF( stdout, "∞\n" );
6448 	FPrintF( stdout, "Start time:  %{du:time}\n", NULL );
6449 	FPrintF( stdout, "---\n" );
6450 }
6451 
6452 //===========================================================================================================================
6453 //	DNSQueryReadHandler
6454 //===========================================================================================================================
6455 
DNSQueryReadHandler(void * inContext)6456 static void	DNSQueryReadHandler( void *inContext )
6457 {
6458 	OSStatus					err;
6459 	struct timeval				now;
6460 	const uint64_t				nowTicks	= UpTicks();
6461 	DNSQueryContext * const		context		= (DNSQueryContext *) inContext;
6462 
6463 	gettimeofday( &now, NULL );
6464 
6465 	if( context->useTCP )
6466 	{
6467 		if( !context->haveTCPLen )
6468 		{
6469 			err = SocketReadData( context->sock, &context->msgBuf, 2, &context->msgOffset );
6470 			if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
6471 			require_noerr( err, exit );
6472 
6473 			context->msgOffset	= 0;
6474 			context->msgLen		= ReadBig16( context->msgBuf );
6475 			context->haveTCPLen	= true;
6476 			if( context->msgLen <= sizeof( context->msgBuf ) )
6477 			{
6478 				context->msgPtr = context->msgBuf;
6479 			}
6480 			else
6481 			{
6482 				context->msgPtr = (uint8_t *) malloc( context->msgLen );
6483 				require_action( context->msgPtr, exit, err = kNoMemoryErr );
6484 			}
6485 		}
6486 
6487 		err = SocketReadData( context->sock, context->msgPtr, context->msgLen, &context->msgOffset );
6488 		if( err == EWOULDBLOCK ) { err = kNoErr; goto exit; }
6489 		require_noerr( err, exit );
6490 		context->msgOffset	= 0;
6491 		context->haveTCPLen	= false;
6492 	}
6493 	else
6494 	{
6495 		sockaddr_ip		fromAddr;
6496 
6497 		context->msgPtr = context->msgBuf;
6498 		err = SocketRecvFrom( context->sock, context->msgPtr, sizeof( context->msgBuf ), &context->msgLen, &fromAddr,
6499 			sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6500 		require_noerr( err, exit );
6501 
6502 		check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6503 	}
6504 
6505 	FPrintF( stdout, "Receive time: %{du:time}\n",	&now );
6506 	FPrintF( stdout, "Source:       %##a\n",		&context->serverAddr );
6507 	FPrintF( stdout, "Message size: %zu\n",			context->msgLen );
6508 	FPrintF( stdout, "RTT:          %llu ms\n\n",	UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6509 	if( context->printRawRData )	FPrintF( stdout, "%{du:rdnsmsg}\n", context->msgPtr, context->msgLen );
6510 	else							FPrintF( stdout, "%{du:dnsmsg}\n",  context->msgPtr, context->msgLen );
6511 
6512 	if( ( context->msgLen >= kDNSHeaderLength ) && ( DNSHeaderGetID( (DNSHeader *) context->msgPtr ) == context->queryID ) )
6513 	{
6514 		Exit( kExitReason_ReceivedResponse );
6515 	}
6516 
6517 exit:
6518 	if( err ) dispatch_source_forget( &context->readSource );
6519 }
6520 
6521 //===========================================================================================================================
6522 //	DNSQueryCancelHandler
6523 //===========================================================================================================================
6524 
DNSQueryCancelHandler(void * inContext)6525 static void	DNSQueryCancelHandler( void *inContext )
6526 {
6527 	DNSQueryContext * const		context = (DNSQueryContext *) inContext;
6528 
6529 	check( !context->readSource );
6530 	ForgetSocket( &context->sock );
6531 	if( context->msgPtr != context->msgBuf ) ForgetMem( &context->msgPtr );
6532 	free( context );
6533 	dispatch_async_f( dispatch_get_main_queue(), NULL, Exit );
6534 }
6535 
6536 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
6537 //===========================================================================================================================
6538 //	DNSCryptCmd
6539 //===========================================================================================================================
6540 
6541 #define kDNSCryptPort		443
6542 
6543 #define kDNSCryptMinPadLength				8
6544 #define kDNSCryptMaxPadLength				256
6545 #define kDNSCryptBlockSize					64
6546 #define kDNSCryptCertMinimumLength			124
6547 #define kDNSCryptClientMagicLength			8
6548 #define kDNSCryptResolverMagicLength		8
6549 #define kDNSCryptHalfNonceLength			12
6550 #define kDNSCryptCertMagicLength			4
6551 
6552 check_compile_time( ( kDNSCryptHalfNonceLength * 2 ) == crypto_box_NONCEBYTES );
6553 
6554 static const uint8_t		kDNSCryptCertMagic[ kDNSCryptCertMagicLength ] = { 'D', 'N', 'S', 'C' };
6555 static const uint8_t		kDNSCryptResolverMagic[ kDNSCryptResolverMagicLength ] =
6556 {
6557 	0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38
6558 };
6559 
6560 typedef struct
6561 {
6562 	uint8_t		certMagic[ kDNSCryptCertMagicLength ];
6563 	uint8_t		esVersion[ 2 ];
6564 	uint8_t		minorVersion[ 2 ];
6565 	uint8_t		signature[ crypto_sign_BYTES ];
6566 	uint8_t		publicKey[ crypto_box_PUBLICKEYBYTES ];
6567 	uint8_t		clientMagic[ kDNSCryptClientMagicLength ];
6568 	uint8_t		serial[ 4 ];
6569 	uint8_t		startTime[ 4 ];
6570 	uint8_t		endTime[ 4 ];
6571 	uint8_t		extensions[ 1 ];	// Variably-sized extension data.
6572 
6573 }	DNSCryptCert;
6574 
6575 check_compile_time( offsetof( DNSCryptCert, extensions ) == kDNSCryptCertMinimumLength );
6576 
6577 typedef struct
6578 {
6579 	uint8_t		clientMagic[ kDNSCryptClientMagicLength ];
6580 	uint8_t		clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
6581 	uint8_t		clientNonce[ kDNSCryptHalfNonceLength ];
6582 	uint8_t		poly1305MAC[ 16 ];
6583 
6584 }	DNSCryptQueryHeader;
6585 
6586 check_compile_time( sizeof( DNSCryptQueryHeader ) == 68 );
6587 check_compile_time( sizeof( DNSCryptQueryHeader ) >= crypto_box_ZEROBYTES );
6588 check_compile_time( ( sizeof( DNSCryptQueryHeader ) - crypto_box_ZEROBYTES + crypto_box_BOXZEROBYTES ) ==
6589 	offsetof( DNSCryptQueryHeader, poly1305MAC ) );
6590 
6591 typedef struct
6592 {
6593 	uint8_t		resolverMagic[ kDNSCryptResolverMagicLength ];
6594 	uint8_t		clientNonce[ kDNSCryptHalfNonceLength ];
6595 	uint8_t		resolverNonce[ kDNSCryptHalfNonceLength ];
6596 	uint8_t		poly1305MAC[ 16 ];
6597 
6598 }	DNSCryptResponseHeader;
6599 
6600 check_compile_time( sizeof( DNSCryptResponseHeader ) == 48 );
6601 check_compile_time( offsetof( DNSCryptResponseHeader, poly1305MAC ) >= crypto_box_BOXZEROBYTES );
6602 check_compile_time( ( offsetof( DNSCryptResponseHeader, poly1305MAC ) - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES ) ==
6603 	sizeof( DNSCryptResponseHeader ) );
6604 
6605 typedef struct
6606 {
6607 	sockaddr_ip				serverAddr;
6608 	uint64_t				sendTicks;
6609 	const char *			providerName;
6610 	const char *			qname;
6611 	const uint8_t *			certPtr;
6612 	size_t					certLen;
6613 	dispatch_source_t		readSource;
6614 	size_t					msgLen;
6615 	int						timeLimitSecs;
6616 	uint16_t				queryID;
6617 	uint16_t				qtype;
6618 	Boolean					printRawRData;
6619 	uint8_t					serverPublicSignKey[ crypto_sign_PUBLICKEYBYTES ];
6620 	uint8_t					serverPublicKey[ crypto_box_PUBLICKEYBYTES ];
6621 	uint8_t					clientPublicKey[ crypto_box_PUBLICKEYBYTES ];
6622 	uint8_t					clientSecretKey[ crypto_box_SECRETKEYBYTES ];
6623 	uint8_t					clientMagic[ kDNSCryptClientMagicLength ];
6624 	uint8_t					clientNonce[ kDNSCryptHalfNonceLength ];
6625 	uint8_t					nmKey[ crypto_box_BEFORENMBYTES ];
6626 	uint8_t					msgBuf[ 512 ];
6627 
6628 }	DNSCryptContext;
6629 
6630 static void		DNSCryptReceiveCertHandler( void *inContext );
6631 static void		DNSCryptReceiveResponseHandler( void *inContext );
6632 static void		DNSCryptProceed( void *inContext );
6633 static OSStatus	DNSCryptProcessCert( DNSCryptContext *inContext );
6634 static OSStatus	DNSCryptBuildQuery( DNSCryptContext *inContext );
6635 static OSStatus	DNSCryptSendQuery( DNSCryptContext *inContext );
6636 static void		DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen );
6637 
DNSCryptCmd(void)6638 static void	DNSCryptCmd( void )
6639 {
6640 	OSStatus				err;
6641 	DNSCryptContext *		context		= NULL;
6642 	size_t					writtenBytes;
6643 	size_t					totalBytes;
6644 	SocketContext *			sockCtx;
6645 	SocketRef				sock		= kInvalidSocketRef;
6646 	const char *			ptr;
6647 
6648 	// Check command parameters.
6649 
6650 	if( gDNSCrypt_TimeLimitSecs < -1 )
6651 	{
6652 		FPrintF( stdout, "Invalid time limit: %d seconds.\n", gDNSCrypt_TimeLimitSecs );
6653 		err = kParamErr;
6654 		goto exit;
6655 	}
6656 
6657 	// Create context.
6658 
6659 	context = (DNSCryptContext *) calloc( 1, sizeof( *context ) );
6660 	require_action( context, exit, err = kNoMemoryErr );
6661 
6662 	context->providerName	= gDNSCrypt_ProviderName;
6663 	context->qname			= gDNSCrypt_Name;
6664 	context->timeLimitSecs	= gDNSCrypt_TimeLimitSecs;
6665 	context->printRawRData	= gDNSCrypt_RawRData ? true : false;
6666 
6667 	err = crypto_box_keypair( context->clientPublicKey, context->clientSecretKey );
6668 	require_noerr( err, exit );
6669 
6670 	err = HexToData( gDNSCrypt_ProviderKey, kSizeCString, kHexToData_DefaultFlags,
6671 		context->serverPublicSignKey, sizeof( context->serverPublicSignKey ), &writtenBytes, &totalBytes, &ptr );
6672 	if( err || ( *ptr != '\0' ) )
6673 	{
6674 		FPrintF( stderr, "Failed to parse public signing key hex string (%s).\n", gDNSCrypt_ProviderKey );
6675 		goto exit;
6676 	}
6677 	else if( totalBytes != sizeof( context->serverPublicSignKey ) )
6678 	{
6679 		FPrintF( stderr, "Public signing key contains incorrect number of hex bytes (%zu != %zu)\n",
6680 			totalBytes, sizeof( context->serverPublicSignKey ) );
6681 		err = kSizeErr;
6682 		goto exit;
6683 	}
6684 	check( writtenBytes == totalBytes );
6685 
6686 	err = StringToSockAddr( gDNSCrypt_Server, &context->serverAddr, sizeof( context->serverAddr ), NULL );
6687 	require_noerr( err, exit );
6688 	if( SockAddrGetPort( &context->serverAddr ) == 0 ) SockAddrSetPort( &context->serverAddr, kDNSCryptPort );
6689 
6690 	err = RecordTypeFromArgString( gDNSCrypt_Type, &context->qtype );
6691 	require_noerr( err, exit );
6692 
6693 	// Write query message.
6694 
6695 	context->queryID = (uint16_t) Random32();
6696 	err = WriteDNSQueryMessage( context->msgBuf, context->queryID, kDNSHeaderFlag_RecursionDesired, context->providerName,
6697 		kDNSServiceType_TXT, kDNSServiceClass_IN, &context->msgLen );
6698 	require_noerr( err, exit );
6699 
6700 	// Create UDP socket.
6701 
6702 	err = UDPClientSocketOpen( AF_UNSPEC, &context->serverAddr, 0, -1, NULL, &sock );
6703 	require_noerr( err, exit );
6704 
6705 	// Send DNS query.
6706 
6707 	context->sendTicks = UpTicks();
6708 	err = _SocketWriteAll( sock, context->msgBuf, context->msgLen, 5 );
6709 	require_noerr( err, exit );
6710 
6711 	sockCtx = SocketContextCreate( sock, context, &err );
6712 	require_noerr( err, exit );
6713 	sock = kInvalidSocketRef;
6714 
6715 	err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveCertHandler, SocketContextCancelHandler, sockCtx,
6716 		&context->readSource );
6717 	if( err ) ForgetSocketContext( &sockCtx );
6718 	require_noerr( err, exit );
6719 
6720 	dispatch_resume( context->readSource );
6721 
6722 	if( context->timeLimitSecs > 0 )
6723 	{
6724 		dispatch_after_f( dispatch_time_seconds( context->timeLimitSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
6725 			Exit );
6726 	}
6727 	dispatch_main();
6728 
6729 exit:
6730 	if( context ) free( context );
6731 	ForgetSocket( &sock );
6732 	if( err ) exit( 1 );
6733 }
6734 
6735 //===========================================================================================================================
6736 //	DNSCryptReceiveCertHandler
6737 //===========================================================================================================================
6738 
DNSCryptReceiveCertHandler(void * inContext)6739 static void	DNSCryptReceiveCertHandler( void *inContext )
6740 {
6741 	OSStatus					err;
6742 	struct timeval				now;
6743 	const uint64_t				nowTicks	= UpTicks();
6744 	SocketContext * const		sockCtx		= (SocketContext *) inContext;
6745 	DNSCryptContext * const		context		= (DNSCryptContext *) sockCtx->userContext;
6746 	const DNSHeader *			hdr;
6747 	sockaddr_ip					fromAddr;
6748 	const uint8_t *				ptr;
6749 	const uint8_t *				txtPtr;
6750 	size_t						txtLen;
6751 	unsigned int				answerCount, i;
6752 	uint8_t						targetName[ kDomainNameLengthMax ];
6753 
6754 	gettimeofday( &now, NULL );
6755 
6756 	dispatch_source_forget( &context->readSource );
6757 
6758 	err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
6759 		&fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6760 	require_noerr( err, exit );
6761 	check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6762 
6763 	FPrintF( stdout, "Receive time: %{du:time}\n",	&now );
6764 	FPrintF( stdout, "Source:       %##a\n",		&context->serverAddr );
6765 	FPrintF( stdout, "Message size: %zu\n",			context->msgLen );
6766 	FPrintF( stdout, "RTT:          %llu ms\n\n",	UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6767 	if( context->printRawRData )	FPrintF( stdout, "%{du:rdnsmsg}\n", context->msgBuf, context->msgLen );
6768 	else							FPrintF( stdout, "%{du:dnsmsg}\n",  context->msgBuf, context->msgLen );
6769 
6770 	require_action_quiet( context->msgLen >= kDNSHeaderLength, exit, err = kSizeErr );
6771 
6772 	hdr = (DNSHeader *) context->msgBuf;
6773 	require_action_quiet( DNSHeaderGetID( hdr ) == context->queryID, exit, err = kMismatchErr );
6774 
6775 	err = DNSMessageGetAnswerSection( context->msgBuf, context->msgLen, &ptr );
6776 	require_noerr( err, exit );
6777 
6778 	err = DomainNameFromString( targetName, context->providerName, NULL );
6779 	require_noerr( err, exit );
6780 
6781 	answerCount = DNSHeaderGetAnswerCount( hdr );
6782 	for( i = 0; i < answerCount; ++i )
6783 	{
6784 		uint16_t		type;
6785 		uint16_t		class;
6786 		uint8_t			name[ kDomainNameLengthMax ];
6787 
6788 		err = DNSMessageExtractRecord( context->msgBuf, context->msgLen, ptr, name, &type, &class, NULL, &txtPtr, &txtLen,
6789 			&ptr );
6790 		require_noerr( err, exit );
6791 
6792 		if( ( type == kDNSServiceType_TXT ) && ( class == kDNSServiceClass_IN ) && DomainNameEqual( name, targetName ) )
6793 		{
6794 			break;
6795 		}
6796 	}
6797 
6798 	if( txtLen < ( 1 + kDNSCryptCertMinimumLength ) )
6799 	{
6800 		FPrintF( stderr, "TXT record length is too short (%u < %u)\n", txtLen, kDNSCryptCertMinimumLength + 1 );
6801 		err = kSizeErr;
6802 		goto exit;
6803 	}
6804 	if( txtPtr[ 0 ] < kDNSCryptCertMinimumLength )
6805 	{
6806 		FPrintF( stderr, "TXT record value length is too short (%u < %u)\n", txtPtr[ 0 ], kDNSCryptCertMinimumLength );
6807 		err = kSizeErr;
6808 		goto exit;
6809 	}
6810 
6811 	context->certLen = txtPtr[ 0 ];
6812 	context->certPtr = &txtPtr[ 1 ];
6813 
6814 	dispatch_async_f( dispatch_get_main_queue(), context, DNSCryptProceed );
6815 
6816 exit:
6817 	if( err ) Exit( NULL );
6818 }
6819 
6820 //===========================================================================================================================
6821 //	DNSCryptReceiveResponseHandler
6822 //===========================================================================================================================
6823 
DNSCryptReceiveResponseHandler(void * inContext)6824 static void	DNSCryptReceiveResponseHandler( void *inContext )
6825 {
6826 	OSStatus						err;
6827 	struct timeval					now;
6828 	const uint64_t					nowTicks	= UpTicks();
6829 	SocketContext * const			sockCtx		= (SocketContext *) inContext;
6830 	DNSCryptContext * const			context		= (DNSCryptContext *) sockCtx->userContext;
6831 	sockaddr_ip						fromAddr;
6832 	DNSCryptResponseHeader *		hdr;
6833 	const uint8_t *					end;
6834 	uint8_t *						ciphertext;
6835 	uint8_t *						plaintext;
6836 	const uint8_t *					response;
6837 	uint8_t							nonce[ crypto_box_NONCEBYTES ];
6838 
6839 	gettimeofday( &now, NULL );
6840 
6841 	dispatch_source_forget( &context->readSource );
6842 
6843 	err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
6844 		&fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
6845 	require_noerr( err, exit );
6846 	check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
6847 
6848 	FPrintF( stdout, "Receive time: %{du:time}\n",	&now );
6849 	FPrintF( stdout, "Source:       %##a\n",		&context->serverAddr );
6850 	FPrintF( stdout, "Message size: %zu\n",			context->msgLen );
6851 	FPrintF( stdout, "RTT:          %llu ms\n\n",	UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
6852 
6853 	if( context->msgLen < sizeof( DNSCryptResponseHeader ) )
6854 	{
6855 		FPrintF( stderr, "DNSCrypt response is too short.\n" );
6856 		err = kSizeErr;
6857 		goto exit;
6858 	}
6859 
6860 	hdr = (DNSCryptResponseHeader *) context->msgBuf;
6861 
6862 	if( memcmp( hdr->resolverMagic, kDNSCryptResolverMagic, kDNSCryptResolverMagicLength ) != 0 )
6863 	{
6864 		FPrintF( stderr, "DNSCrypt response resolver magic %#H != %#H\n",
6865 			hdr->resolverMagic,		kDNSCryptResolverMagicLength, INT_MAX,
6866 			kDNSCryptResolverMagic, kDNSCryptResolverMagicLength, INT_MAX );
6867 		err = kValueErr;
6868 		goto exit;
6869 	}
6870 
6871 	if( memcmp( hdr->clientNonce, context->clientNonce, kDNSCryptHalfNonceLength ) != 0 )
6872 	{
6873 		FPrintF( stderr, "DNSCrypt response client nonce mismatch.\n" );
6874 		err = kValueErr;
6875 		goto exit;
6876 	}
6877 
6878 	memcpy( nonce, hdr->clientNonce, crypto_box_NONCEBYTES );
6879 
6880 	ciphertext = hdr->poly1305MAC - crypto_box_BOXZEROBYTES;
6881 	memset( ciphertext, 0, crypto_box_BOXZEROBYTES );
6882 
6883 	plaintext = (uint8_t *)( hdr + 1 ) - crypto_box_ZEROBYTES;
6884 	check( plaintext == ciphertext );
6885 
6886 	end = context->msgBuf + context->msgLen;
6887 
6888 	err = crypto_box_open_afternm( plaintext, ciphertext, (size_t)( end - ciphertext ), nonce, context->nmKey );
6889 	require_noerr( err, exit );
6890 
6891 	response = plaintext + crypto_box_ZEROBYTES;
6892 	if( context->printRawRData )	FPrintF( stdout, "%{du:rdnsmsg}\n", response, (size_t)( end - response ) );
6893 	else							FPrintF( stdout, "%{du:dnsmsg}\n",  response, (size_t)( end - response ) );
6894 	Exit( kExitReason_ReceivedResponse );
6895 
6896 exit:
6897 	if( err ) Exit( NULL );
6898 }
6899 
6900 //===========================================================================================================================
6901 //	DNSCryptProceed
6902 //===========================================================================================================================
6903 
DNSCryptProceed(void * inContext)6904 static void	DNSCryptProceed( void *inContext )
6905 {
6906 	OSStatus					err;
6907 	DNSCryptContext * const		context = (DNSCryptContext *) inContext;
6908 
6909 	err = DNSCryptProcessCert( context );
6910 	require_noerr_quiet( err, exit );
6911 
6912 	err = DNSCryptBuildQuery( context );
6913 	require_noerr_quiet( err, exit );
6914 
6915 	err = DNSCryptSendQuery( context );
6916 	require_noerr_quiet( err, exit );
6917 
6918 exit:
6919 	if( err ) Exit( NULL );
6920 }
6921 
6922 //===========================================================================================================================
6923 //	DNSCryptProcessCert
6924 //===========================================================================================================================
6925 
DNSCryptProcessCert(DNSCryptContext * inContext)6926 static OSStatus	DNSCryptProcessCert( DNSCryptContext *inContext )
6927 {
6928 	OSStatus						err;
6929 	const DNSCryptCert * const		cert	= (DNSCryptCert *) inContext->certPtr;
6930 	const uint8_t * const			certEnd	= inContext->certPtr + inContext->certLen;
6931 	struct timeval					now;
6932 	time_t							startTimeSecs, endTimeSecs;
6933 	size_t							signedLen;
6934 	uint8_t *						tempBuf;
6935 	unsigned long long				tempLen;
6936 
6937 	DNSCryptPrintCertificate( cert, inContext->certLen );
6938 
6939 	if( memcmp( cert->certMagic, kDNSCryptCertMagic, kDNSCryptCertMagicLength ) != 0 )
6940 	{
6941 		FPrintF( stderr, "DNSCrypt certificate magic %#H != %#H\n",
6942 			cert->certMagic,	kDNSCryptCertMagicLength, INT_MAX,
6943 			kDNSCryptCertMagic, kDNSCryptCertMagicLength, INT_MAX );
6944 		err = kValueErr;
6945 		goto exit;
6946 	}
6947 
6948 	startTimeSecs	= (time_t) ReadBig32( cert->startTime );
6949 	endTimeSecs		= (time_t) ReadBig32( cert->endTime );
6950 
6951 	gettimeofday( &now, NULL );
6952 	if( now.tv_sec < startTimeSecs )
6953 	{
6954 		FPrintF( stderr, "DNSCrypt certificate start time is in the future.\n" );
6955 		err = kDateErr;
6956 		goto exit;
6957 	}
6958 	if( now.tv_sec >= endTimeSecs )
6959 	{
6960 		FPrintF( stderr, "DNSCrypt certificate has expired.\n" );
6961 		err = kDateErr;
6962 		goto exit;
6963 	}
6964 
6965 	signedLen = (size_t)( certEnd - cert->signature );
6966 	tempBuf = (uint8_t *) malloc( signedLen );
6967 	require_action( tempBuf, exit, err = kNoMemoryErr );
6968 	err = crypto_sign_open( tempBuf, &tempLen, cert->signature, signedLen, inContext->serverPublicSignKey );
6969 	free( tempBuf );
6970 	if( err )
6971 	{
6972 		FPrintF( stderr, "DNSCrypt certificate failed verification.\n" );
6973 		err = kAuthenticationErr;
6974 		goto exit;
6975 	}
6976 
6977 	memcpy( inContext->serverPublicKey,	cert->publicKey,	crypto_box_PUBLICKEYBYTES );
6978 	memcpy( inContext->clientMagic,		cert->clientMagic,	kDNSCryptClientMagicLength );
6979 
6980 	err = crypto_box_beforenm( inContext->nmKey, inContext->serverPublicKey, inContext->clientSecretKey );
6981 	require_noerr( err, exit );
6982 
6983 	inContext->certPtr	= NULL;
6984 	inContext->certLen	= 0;
6985 	inContext->msgLen	= 0;
6986 
6987 exit:
6988 	return( err );
6989 }
6990 
6991 //===========================================================================================================================
6992 //	DNSCryptBuildQuery
6993 //===========================================================================================================================
6994 
6995 static OSStatus	DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen );
6996 
DNSCryptBuildQuery(DNSCryptContext * inContext)6997 static OSStatus	DNSCryptBuildQuery( DNSCryptContext *inContext )
6998 {
6999 	OSStatus						err;
7000 	DNSCryptQueryHeader * const		hdr			= (DNSCryptQueryHeader *) inContext->msgBuf;
7001 	uint8_t * const					queryPtr	= (uint8_t *)( hdr + 1 );
7002 	size_t							queryLen;
7003 	size_t							paddedQueryLen;
7004 	const uint8_t * const			msgLimit	= inContext->msgBuf + sizeof( inContext->msgBuf );
7005 	const uint8_t *					padLimit;
7006 	uint8_t							nonce[ crypto_box_NONCEBYTES ];
7007 
7008 	check_compile_time_code( sizeof( inContext->msgBuf ) >= ( sizeof( DNSCryptQueryHeader ) + kDNSQueryMessageMaxLen ) );
7009 
7010 	inContext->queryID = (uint16_t) Random32();
7011 	err = WriteDNSQueryMessage( queryPtr, inContext->queryID, kDNSHeaderFlag_RecursionDesired, inContext->qname,
7012 		inContext->qtype, kDNSServiceClass_IN, &queryLen );
7013 	require_noerr( err, exit );
7014 
7015 	padLimit = &queryPtr[ queryLen + kDNSCryptMaxPadLength ];
7016 	if( padLimit > msgLimit ) padLimit = msgLimit;
7017 
7018 	err = DNSCryptPadQuery( queryPtr, queryLen, (size_t)( padLimit - queryPtr ), &paddedQueryLen );
7019 	require_noerr( err, exit );
7020 
7021 	memset( queryPtr - crypto_box_ZEROBYTES, 0, crypto_box_ZEROBYTES );
7022 	RandomBytes( inContext->clientNonce, kDNSCryptHalfNonceLength );
7023 	memcpy( nonce, inContext->clientNonce, kDNSCryptHalfNonceLength );
7024 	memset( &nonce[ kDNSCryptHalfNonceLength ], 0, kDNSCryptHalfNonceLength );
7025 
7026 	err = crypto_box_afternm( queryPtr - crypto_box_ZEROBYTES, queryPtr - crypto_box_ZEROBYTES,
7027 		paddedQueryLen + crypto_box_ZEROBYTES, nonce, inContext->nmKey );
7028 	require_noerr( err, exit );
7029 
7030 	memcpy( hdr->clientMagic,		inContext->clientMagic,		kDNSCryptClientMagicLength );
7031 	memcpy( hdr->clientPublicKey,	inContext->clientPublicKey,	crypto_box_PUBLICKEYBYTES );
7032 	memcpy( hdr->clientNonce,		nonce,						kDNSCryptHalfNonceLength );
7033 
7034 	inContext->msgLen = (size_t)( &queryPtr[ paddedQueryLen ] - inContext->msgBuf );
7035 
7036 exit:
7037 	return( err );
7038 }
7039 
DNSCryptPadQuery(uint8_t * inMsgPtr,size_t inMsgLen,size_t inMaxLen,size_t * outPaddedLen)7040 static OSStatus	DNSCryptPadQuery( uint8_t *inMsgPtr, size_t inMsgLen, size_t inMaxLen, size_t *outPaddedLen )
7041 {
7042 	OSStatus		err;
7043 	size_t			paddedLen;
7044 
7045 	require_action_quiet( ( inMsgLen + kDNSCryptMinPadLength ) <= inMaxLen, exit, err = kSizeErr );
7046 
7047 	paddedLen = inMsgLen + kDNSCryptMinPadLength +
7048 		arc4random_uniform( (uint32_t)( inMaxLen - ( inMsgLen + kDNSCryptMinPadLength ) + 1 ) );
7049 	paddedLen += ( kDNSCryptBlockSize - ( paddedLen % kDNSCryptBlockSize ) );
7050 	if( paddedLen > inMaxLen ) paddedLen = inMaxLen;
7051 
7052 	inMsgPtr[ inMsgLen ] = 0x80;
7053 	memset( &inMsgPtr[ inMsgLen + 1 ], 0, paddedLen - ( inMsgLen + 1 ) );
7054 
7055 	if( outPaddedLen ) *outPaddedLen = paddedLen;
7056 	err = kNoErr;
7057 
7058 exit:
7059 	return( err );
7060 }
7061 
7062 //===========================================================================================================================
7063 //	DNSCryptSendQuery
7064 //===========================================================================================================================
7065 
DNSCryptSendQuery(DNSCryptContext * inContext)7066 static OSStatus	DNSCryptSendQuery( DNSCryptContext *inContext )
7067 {
7068 	OSStatus			err;
7069 	SocketContext *		sockCtx;
7070 	SocketRef			sock = kInvalidSocketRef;
7071 
7072 	check( inContext->msgLen > 0 );
7073 	check( !inContext->readSource );
7074 
7075 	err = UDPClientSocketOpen( AF_UNSPEC, &inContext->serverAddr, 0, -1, NULL, &sock );
7076 	require_noerr( err, exit );
7077 
7078 	inContext->sendTicks = UpTicks();
7079 	err = _SocketWriteAll( sock, inContext->msgBuf, inContext->msgLen, 5 );
7080 	require_noerr( err, exit );
7081 
7082 	sockCtx = SocketContextCreate( sock, inContext, &err );
7083 	require_noerr( err, exit );
7084 	sock = kInvalidSocketRef;
7085 
7086 	err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveResponseHandler, SocketContextCancelHandler, sockCtx,
7087 		&inContext->readSource );
7088 	if( err ) ForgetSocketContext( &sockCtx );
7089 	require_noerr( err, exit );
7090 
7091 	dispatch_resume( inContext->readSource );
7092 
7093 exit:
7094 	ForgetSocket( &sock );
7095 	return( err );
7096 }
7097 
7098 //===========================================================================================================================
7099 //	DNSCryptPrintCertificate
7100 //===========================================================================================================================
7101 
7102 #define kCertTimeStrBufLen		32
7103 
7104 static char *	CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] );
7105 
DNSCryptPrintCertificate(const DNSCryptCert * inCert,size_t inLen)7106 static void	DNSCryptPrintCertificate( const DNSCryptCert *inCert, size_t inLen )
7107 {
7108 	time_t		startTime, endTime;
7109 	int			extLen;
7110 	char		timeBuf[ kCertTimeStrBufLen ];
7111 
7112 	check( inLen >= kDNSCryptCertMinimumLength );
7113 
7114 	startTime	= (time_t) ReadBig32( inCert->startTime );
7115 	endTime		= (time_t) ReadBig32( inCert->endTime );
7116 
7117 	FPrintF( stdout, "DNSCrypt certificate (%zu bytes):\n", inLen );
7118 	FPrintF( stdout, "Cert Magic:    %#H\n", inCert->certMagic, kDNSCryptCertMagicLength, INT_MAX );
7119 	FPrintF( stdout, "ES Version:    %u\n",	ReadBig16( inCert->esVersion ) );
7120 	FPrintF( stdout, "Minor Version: %u\n",	ReadBig16( inCert->minorVersion ) );
7121 	FPrintF( stdout, "Signature:     %H\n",	inCert->signature, crypto_sign_BYTES / 2, INT_MAX );
7122 	FPrintF( stdout, "               %H\n",	&inCert->signature[ crypto_sign_BYTES / 2 ], crypto_sign_BYTES / 2, INT_MAX );
7123 	FPrintF( stdout, "Public Key:    %H\n", inCert->publicKey, sizeof( inCert->publicKey ), INT_MAX );
7124 	FPrintF( stdout, "Client Magic:  %H\n", inCert->clientMagic, kDNSCryptClientMagicLength, INT_MAX );
7125 	FPrintF( stdout, "Serial:        %u\n",	ReadBig32( inCert->serial ) );
7126 	FPrintF( stdout, "Start Time:    %u (%s)\n", (uint32_t) startTime, CertTimeStr( startTime, timeBuf ) );
7127 	FPrintF( stdout, "End Time:      %u (%s)\n", (uint32_t) endTime, CertTimeStr( endTime, timeBuf ) );
7128 
7129 	if( inLen > kDNSCryptCertMinimumLength )
7130 	{
7131 		extLen = (int)( inLen - kDNSCryptCertMinimumLength );
7132 		FPrintF( stdout, "Extensions:    %.1H\n", inCert->extensions, extLen, extLen );
7133 	}
7134 	FPrintF( stdout, "\n" );
7135 }
7136 
CertTimeStr(time_t inTime,char inBuffer[kCertTimeStrBufLen])7137 static char *	CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] )
7138 {
7139 	struct tm *		tm;
7140 
7141 	tm = localtime( &inTime );
7142 	if( !tm )
7143 	{
7144 		dlogassert( "localtime() returned a NULL pointer.\n" );
7145 		*inBuffer = '\0';
7146 	}
7147 	else
7148 	{
7149 		strftime( inBuffer, kCertTimeStrBufLen, "%a %b %d %H:%M:%S %Z %Y", tm );
7150 	}
7151 
7152 	return( inBuffer );
7153 }
7154 
7155 #endif	// DNSSDUTIL_INCLUDE_DNSCRYPT
7156 
7157 //===========================================================================================================================
7158 //	MDNSQueryCmd
7159 //===========================================================================================================================
7160 
7161 typedef struct
7162 {
7163 	const char *			qnameStr;							// Name (QNAME) of the record being queried as a C string.
7164 	dispatch_source_t		readSourceV4;						// Read dispatch source for IPv4 socket.
7165 	dispatch_source_t		readSourceV6;						// Read dispatch source for IPv6 socket.
7166 	int						localPort;							// The port number to which the sockets are bound.
7167 	int						receiveSecs;						// After send, the amount of time to spend receiving.
7168 	uint32_t				ifIndex;							// Index of the interface over which to send the query.
7169 	uint16_t				qtype;								// The type (QTYPE) of the record being queried.
7170 	Boolean					isQU;								// True if the query is QU, i.e., requests unicast responses.
7171 	Boolean					allResponses;						// True if all mDNS messages received should be printed.
7172 	Boolean					printRawRData;						// True if RDATA should be printed as hexdumps.
7173 	Boolean					useIPv4;							// True if the query should be sent via IPv4 multicast.
7174 	Boolean					useIPv6;							// True if the query should be sent via IPv6 multicast.
7175 	char					ifName[ IF_NAMESIZE + 1 ];			// Name of the interface over which to send the query.
7176 	uint8_t					qname[ kDomainNameLengthMax ];		// Buffer to hold the QNAME in DNS label format.
7177 	uint8_t					msgBuf[ kMDNSMessageSizeMax ];		// mDNS message buffer.
7178 
7179 }	MDNSQueryContext;
7180 
7181 static void	MDNSQueryPrintPrologue( const MDNSQueryContext *inContext );
7182 static void	MDNSQueryReadHandler( void *inContext );
7183 
MDNSQueryCmd(void)7184 static void	MDNSQueryCmd( void )
7185 {
7186 	OSStatus				err;
7187 	MDNSQueryContext *		context;
7188 	SocketRef				sockV4 = kInvalidSocketRef;
7189 	SocketRef				sockV6 = kInvalidSocketRef;
7190 	ssize_t					n;
7191 	const char *			ifname;
7192 	size_t					msgLen;
7193 	unsigned int			sendCount;
7194 
7195 	// Check command parameters.
7196 
7197 	if( gMDNSQuery_ReceiveSecs < -1 )
7198 	{
7199 		FPrintF( stdout, "Invalid receive time value: %d seconds.\n", gMDNSQuery_ReceiveSecs );
7200 		err = kParamErr;
7201 		goto exit;
7202 	}
7203 
7204 	context = (MDNSQueryContext *) calloc( 1, sizeof( *context ) );
7205 	require_action( context, exit, err = kNoMemoryErr );
7206 
7207 	context->qnameStr		= gMDNSQuery_Name;
7208 	context->receiveSecs	= gMDNSQuery_ReceiveSecs;
7209 	context->isQU			= gMDNSQuery_IsQU		  ? true : false;
7210 	context->allResponses	= gMDNSQuery_AllResponses ? true : false;
7211 	context->printRawRData	= gMDNSQuery_RawRData	  ? true : false;
7212 	context->useIPv4		= ( gMDNSQuery_UseIPv4 || !gMDNSQuery_UseIPv6 ) ? true : false;
7213 	context->useIPv6		= ( gMDNSQuery_UseIPv6 || !gMDNSQuery_UseIPv4 ) ? true : false;
7214 
7215 	err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
7216 	require_noerr_quiet( err, exit );
7217 
7218 	ifname = if_indextoname( context->ifIndex, context->ifName );
7219 	require_action( ifname, exit, err = kNameErr );
7220 
7221 	err = RecordTypeFromArgString( gMDNSQuery_Type, &context->qtype );
7222 	require_noerr( err, exit );
7223 
7224 	// Set up IPv4 socket.
7225 
7226 	if( context->useIPv4 )
7227 	{
7228 		err = CreateMulticastSocket( GetMDNSMulticastAddrV4(),
7229 			gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
7230 			ifname, context->ifIndex, !context->isQU, &context->localPort, &sockV4 );
7231 		require_noerr( err, exit );
7232 	}
7233 
7234 	// Set up IPv6 socket.
7235 
7236 	if( context->useIPv6 )
7237 	{
7238 		err = CreateMulticastSocket( GetMDNSMulticastAddrV6(),
7239 			gMDNSQuery_SourcePort ? gMDNSQuery_SourcePort : ( context->isQU ? context->localPort : kMDNSPort ),
7240 			ifname, context->ifIndex, !context->isQU, &context->localPort, &sockV6 );
7241 		require_noerr( err, exit );
7242 	}
7243 
7244 	// Craft mDNS query message.
7245 
7246 	check_compile_time_code( sizeof( context->msgBuf ) >= kDNSQueryMessageMaxLen );
7247 	err = WriteDNSQueryMessage( context->msgBuf, kDefaultMDNSMessageID, kDefaultMDNSQueryFlags, context->qnameStr,
7248 		context->qtype, context->isQU ? ( kDNSServiceClass_IN | kMDNSClassUnicastResponseBit ) : kDNSServiceClass_IN,
7249 		&msgLen );
7250 	require_noerr( err, exit );
7251 
7252 	// Print prologue.
7253 
7254 	MDNSQueryPrintPrologue( context );
7255 
7256 	// Send mDNS query message.
7257 
7258 	sendCount = 0;
7259 	if( IsValidSocket( sockV4 ) )
7260 	{
7261 		const struct sockaddr * const		mcastAddr4 = GetMDNSMulticastAddrV4();
7262 
7263 		n = sendto( sockV4, context->msgBuf, msgLen, 0, mcastAddr4, SockAddrGetSize( mcastAddr4 ) );
7264 		err = map_socket_value_errno( sockV4, n == (ssize_t) msgLen, n );
7265 		if( err )
7266 		{
7267 			FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
7268 			ForgetSocket( &sockV4 );
7269 		}
7270 		else
7271 		{
7272 			++sendCount;
7273 		}
7274 	}
7275 	if( IsValidSocket( sockV6 ) )
7276 	{
7277 		const struct sockaddr * const		mcastAddr6 = GetMDNSMulticastAddrV6();
7278 
7279 		n = sendto( sockV6, context->msgBuf, msgLen, 0, mcastAddr6, SockAddrGetSize( mcastAddr6 ) );
7280 		err = map_socket_value_errno( sockV6, n == (ssize_t) msgLen, n );
7281 		if( err )
7282 		{
7283 			FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
7284 			ForgetSocket( &sockV6 );
7285 		}
7286 		else
7287 		{
7288 			++sendCount;
7289 		}
7290 	}
7291 	require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
7292 
7293 	// If there's no wait period after the send, then exit.
7294 
7295 	if( context->receiveSecs == 0 ) goto exit;
7296 
7297 	// Create dispatch read sources for socket(s).
7298 
7299 	if( IsValidSocket( sockV4 ) )
7300 	{
7301 		SocketContext *		sockCtx;
7302 
7303 		sockCtx = SocketContextCreate( sockV4, context, &err );
7304 		require_noerr( err, exit );
7305 		sockV4 = kInvalidSocketRef;
7306 
7307 		err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
7308 			&context->readSourceV4 );
7309 		if( err ) ForgetSocketContext( &sockCtx );
7310 		require_noerr( err, exit );
7311 
7312 		dispatch_resume( context->readSourceV4 );
7313 	}
7314 
7315 	if( IsValidSocket( sockV6 ) )
7316 	{
7317 		SocketContext *		sockCtx;
7318 
7319 		sockCtx = SocketContextCreate( sockV6, context, &err );
7320 		require_noerr( err, exit );
7321 		sockV6 = kInvalidSocketRef;
7322 
7323 		err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
7324 			&context->readSourceV6 );
7325 		if( err ) ForgetSocketContext( &sockCtx );
7326 		require_noerr( err, exit );
7327 
7328 		dispatch_resume( context->readSourceV6 );
7329 	}
7330 
7331 	if( context->receiveSecs > 0 )
7332 	{
7333 		dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
7334 			Exit );
7335 	}
7336 	dispatch_main();
7337 
7338 exit:
7339 	ForgetSocket( &sockV4 );
7340 	ForgetSocket( &sockV6 );
7341 	if( err ) exit( 1 );
7342 }
7343 
7344 //===========================================================================================================================
7345 //	MDNSColliderCmd
7346 //===========================================================================================================================
7347 
7348 static void	_MDNSColliderCmdStopHandler( void *inContext, OSStatus inError );
7349 
MDNSColliderCmd(void)7350 static void	MDNSColliderCmd( void )
7351 {
7352 	OSStatus					err;
7353 	MDNSColliderRef				collider = NULL;
7354 	uint8_t *					rdataPtr = NULL;
7355 	size_t						rdataLen = 0;
7356 	const char *				ifname;
7357 	uint32_t					ifIndex;
7358 	MDNSColliderProtocols		protocols;
7359 	uint16_t					type;
7360 	char						ifName[ IF_NAMESIZE + 1 ];
7361 	uint8_t						name[ kDomainNameLengthMax ];
7362 
7363 	err = InterfaceIndexFromArgString( gInterface, &ifIndex );
7364 	require_noerr_quiet( err, exit );
7365 
7366 	ifname = if_indextoname( ifIndex, ifName );
7367 	if( !ifname )
7368 	{
7369 		FPrintF( stderr, "error: Invalid interface name or index: %s\n", gInterface );
7370 		err = kNameErr;
7371 		goto exit;
7372 	}
7373 
7374 	err = DomainNameFromString( name, gMDNSCollider_Name, NULL );
7375 	if( err )
7376 	{
7377 		FPrintF( stderr, "error: Invalid record name: %s\n", gMDNSCollider_Name );
7378 		goto exit;
7379 	}
7380 
7381 	err = RecordTypeFromArgString( gMDNSCollider_Type, &type );
7382 	require_noerr_quiet( err, exit );
7383 
7384 	if( gMDNSCollider_RecordData )
7385 	{
7386 		err = RecordDataFromArgString( gMDNSCollider_RecordData, &rdataPtr, &rdataLen );
7387 		require_noerr_quiet( err, exit );
7388 	}
7389 
7390 	err = MDNSColliderCreate( dispatch_get_main_queue(), &collider );
7391 	require_noerr( err, exit );
7392 
7393 	err = MDNSColliderSetProgram( collider, gMDNSCollider_Program );
7394 	if( err )
7395 	{
7396 		FPrintF( stderr, "error: Failed to set program string: '%s'\n", gMDNSCollider_Program );
7397 		goto exit;
7398 	}
7399 
7400 	err = MDNSColliderSetRecord( collider, name, type, rdataPtr, rdataLen );
7401 	require_noerr( err, exit );
7402 	ForgetMem( &rdataPtr );
7403 
7404 	protocols = kMDNSColliderProtocol_None;
7405 	if( gMDNSCollider_UseIPv4 || !gMDNSCollider_UseIPv6 ) protocols |= kMDNSColliderProtocol_IPv4;
7406 	if( gMDNSCollider_UseIPv6 || !gMDNSCollider_UseIPv4 ) protocols |= kMDNSColliderProtocol_IPv6;
7407 	MDNSColliderSetProtocols( collider, protocols );
7408 	MDNSColliderSetInterfaceIndex( collider, ifIndex );
7409 	MDNSColliderSetStopHandler( collider, _MDNSColliderCmdStopHandler, collider );
7410 
7411 	err = MDNSColliderStart( collider );
7412 	require_noerr( err, exit );
7413 
7414 	dispatch_main();
7415 
7416 exit:
7417 	FreeNullSafe( rdataPtr );
7418 	CFReleaseNullSafe( collider );
7419 	if( err ) exit( 1 );
7420 }
7421 
_MDNSColliderCmdStopHandler(void * inContext,OSStatus inError)7422 static void	_MDNSColliderCmdStopHandler( void *inContext, OSStatus inError )
7423 {
7424 	MDNSColliderRef const		collider = (MDNSColliderRef) inContext;
7425 
7426 	CFRelease( collider );
7427 	exit( inError ? 1 : 0 );
7428 }
7429 
7430 //===========================================================================================================================
7431 //	MDNSQueryPrintPrologue
7432 //===========================================================================================================================
7433 
MDNSQueryPrintPrologue(const MDNSQueryContext * inContext)7434 static void	MDNSQueryPrintPrologue( const MDNSQueryContext *inContext )
7435 {
7436 	const int		receiveSecs = inContext->receiveSecs;
7437 
7438 	FPrintF( stdout, "Interface:        %d (%s)\n",		(int32_t) inContext->ifIndex, inContext->ifName );
7439 	FPrintF( stdout, "Name:             %s\n",			inContext->qnameStr );
7440 	FPrintF( stdout, "Type:             %s (%u)\n",		RecordTypeToString( inContext->qtype ), inContext->qtype );
7441 	FPrintF( stdout, "Class:            IN (%s)\n",		inContext->isQU ? "QU" : "QM" );
7442 	FPrintF( stdout, "Local port:       %d\n",			inContext->localPort );
7443 	FPrintF( stdout, "IP protocols:     %?s%?s%?s\n",
7444 		inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
7445 	FPrintF( stdout, "Receive duration: " );
7446 	if( receiveSecs >= 0 )	FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
7447 	else					FPrintF( stdout, "∞\n" );
7448 	FPrintF( stdout, "Start time:       %{du:time}\n",	NULL );
7449 }
7450 
7451 //===========================================================================================================================
7452 //	MDNSQueryReadHandler
7453 //===========================================================================================================================
7454 
MDNSQueryReadHandler(void * inContext)7455 static void	MDNSQueryReadHandler( void *inContext )
7456 {
7457 	OSStatus						err;
7458 	struct timeval					now;
7459 	SocketContext * const			sockCtx = (SocketContext *) inContext;
7460 	MDNSQueryContext * const		context = (MDNSQueryContext *) sockCtx->userContext;
7461 	size_t							msgLen;
7462 	sockaddr_ip						fromAddr;
7463 	Boolean							foundAnswer	= false;
7464 
7465 	gettimeofday( &now, NULL );
7466 
7467 	err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &fromAddr,
7468 		sizeof( fromAddr ), NULL, NULL, NULL, NULL );
7469 	require_noerr( err, exit );
7470 
7471 	if( !context->allResponses && ( msgLen >= kDNSHeaderLength ) )
7472 	{
7473 		const uint8_t *				ptr;
7474 		const DNSHeader * const		hdr = (DNSHeader *) context->msgBuf;
7475 		unsigned int				rrCount, i;
7476 		uint16_t					type, class;
7477 		uint8_t						name[ kDomainNameLengthMax ];
7478 
7479 		err = DNSMessageGetAnswerSection( context->msgBuf, msgLen, &ptr );
7480 		require_noerr( err, exit );
7481 
7482 		if( context->qname[ 0 ] == 0 )
7483 		{
7484 			err = DomainNameAppendString( context->qname, context->qnameStr, NULL );
7485 			require_noerr( err, exit );
7486 		}
7487 
7488 		rrCount = DNSHeaderGetAnswerCount( hdr ) + DNSHeaderGetAuthorityCount( hdr ) + DNSHeaderGetAdditionalCount( hdr );
7489 		for( i = 0; i < rrCount; ++i )
7490 		{
7491 			err = DNSMessageExtractRecord( context->msgBuf, msgLen, ptr, name, &type, &class, NULL, NULL, NULL, &ptr );
7492 			require_noerr( err, exit );
7493 
7494 			if( ( ( context->qtype == kDNSServiceType_ANY ) || ( type == context->qtype ) ) &&
7495 				DomainNameEqual( name, context->qname ) )
7496 			{
7497 				foundAnswer = true;
7498 				break;
7499 			}
7500 		}
7501 	}
7502 	if( context->allResponses || foundAnswer )
7503 	{
7504 		FPrintF( stdout, "---\n" );
7505 		FPrintF( stdout, "Receive time: %{du:time}\n",	&now );
7506 		FPrintF( stdout, "Source:       %##a\n",		&fromAddr );
7507 		FPrintF( stdout, "Message size: %zu\n\n",		msgLen );
7508 		if( context->printRawRData )	FPrintF( stdout, "%#{du:rdnsmsg}\n", context->msgBuf, msgLen );
7509 		else							FPrintF( stdout, "%#{du:dnsmsg}\n",  context->msgBuf, msgLen );
7510 	}
7511 
7512 exit:
7513 	if( err ) exit( 1 );
7514 }
7515 
7516 #if( TARGET_OS_DARWIN )
7517 //===========================================================================================================================
7518 //	PIDToUUIDCmd
7519 //===========================================================================================================================
7520 
PIDToUUIDCmd(void)7521 static void	PIDToUUIDCmd( void )
7522 {
7523 	OSStatus							err;
7524 	int									n;
7525 	struct proc_uniqidentifierinfo		info;
7526 
7527 	n = proc_pidinfo( gPIDToUUID_PID, PROC_PIDUNIQIDENTIFIERINFO, 1, &info, sizeof( info ) );
7528 	require_action_quiet( n == (int) sizeof( info ), exit, err = kUnknownErr );
7529 
7530 	FPrintF( stdout, "%#U\n", info.p_uuid );
7531 	err = kNoErr;
7532 
7533 exit:
7534 	if( err ) exit( 1 );
7535 }
7536 #endif
7537 
7538 //===========================================================================================================================
7539 //	DNSServerCmd
7540 //===========================================================================================================================
7541 
7542 typedef struct
7543 {
7544 	DNSServerRef			server;			// Reference to the DNS server.
7545 	dispatch_queue_t		queue;			// Serial queue for server.
7546 	sockaddr_ip *			addrArray;		// Server's addresses.
7547 	size_t					addrCount;		// Count of server's addresses.
7548 	dispatch_source_t		sourceSigInt;	// Dispatch source for SIGINT.
7549 	dispatch_source_t		sourceSigTerm;	// Dispatch source for SIGTERM.
7550 	const char *			domainOverride;	// If non-NULL, server is to use this domain instead of "d.test.".
7551 	dispatch_semaphore_t	doneSem;		// Semaphore to signal when the server is done.
7552 	OSStatus				error;			// Error encounted while running server.
7553 	Boolean					loopbackOnly;	// True if the server should be bound to the loopback interface.
7554 #if( TARGET_OS_DARWIN )
7555 	dispatch_source_t		processMonitor;	// Process monitor source for process being followed, if any.
7556 	pid_t					followPID;		// PID of process being followed, if any. If it exits, we exit.
7557 	Boolean					addedResolver;	// True if a resolver entry was added to the system DNS settings.
7558 #endif
7559 
7560 }	DNSServerCmdContext;
7561 
7562 static void		_DNSServerCmdStart( void *inCtx );
7563 static void		_DNSServerCmdStop( DNSServerCmdContext *inCmd, OSStatus inError );
7564 #if( TARGET_OS_DARWIN )
7565 static OSStatus	_DNSServerCmdAddExtraLoopbackAddrs( sockaddr_ip *inAddrArray, size_t inAddrCount, uint16_t inPort );
7566 #endif
7567 static void		_DNSServerCmdContextFree( DNSServerCmdContext *inCmd );
7568 static void		_DNSServerCmdServerStartHandler( const sockaddr_ip *inAddrArray, size_t inAddrCount, void *inCtx );
7569 static void		_DNSServerCmdServerStopHandler( OSStatus inError, void *inCtx );
7570 static void		_DNSServerCmdSIGINTHandler( void *inCtx );
7571 static void		_DNSServerCmdSIGTERMHandler( void *inCtx );
7572 static void		_DNSServerCmdShutdown( DNSServerCmdContext *inCtx, int inSignal );
7573 #if( TARGET_OS_DARWIN )
7574 static void		_DNSServerCmdFollowedProcessHandler( void *inCtx );
7575 static OSStatus	_DNSServerCmdLoopbackResolverAdd( const char *inDomain, const sockaddr_ip *inAddrArray, size_t inAddrCount );
7576 static OSStatus	_DNSServerCmdLoopbackResolverRemove( void );
7577 #endif
7578 
7579 ulog_define_ex( kDNSSDUtilIdentifier, DNSServer, kLogLevelInfo, kLogFlags_None, "DNSServer", NULL );
7580 #define ds_ulog( LEVEL, ... )		ulog( &log_category_from_name( DNSServer ), (LEVEL), __VA_ARGS__ )
7581 
DNSServerCmd(void)7582 static void	DNSServerCmd( void )
7583 {
7584 	OSStatus					err;
7585 	DNSServerCmdContext *		cmd = NULL;
7586 	sockaddr_ip *				sip;
7587 	size_t						addrCount;
7588 	Boolean						listenOnV4, listenOnV6;
7589 #if( TARGET_OS_DARWIN )
7590 	size_t                      extraLoopbackV6Count;
7591 #endif
7592 
7593 	// Check command arguments.
7594 
7595 	if( gDNSServer_Foreground ) LogControl( "DNSServer:output=file;stdout,DNSServer:flags=time;prefix" );
7596 	err = CheckIntegerArgument( gDNSServer_ResponseDelayMs, "response delay (ms)", 0, INT_MAX );
7597 	require_noerr_quiet( err, exit );
7598 
7599 	err = CheckIntegerArgument( gDNSServer_DefaultTTL, "default TTL", 0, INT32_MAX );
7600 	require_noerr_quiet( err, exit );
7601 
7602 	err = CheckIntegerArgument( gDNSServer_Port, "port number", 0, UINT16_MAX );
7603 	require_noerr_quiet( err, exit );
7604 
7605 	listenOnV4 = ( gDNSServer_ListenOnV4 || !gDNSServer_ListenOnV6 ) ? true : false;
7606 	listenOnV6 = ( gDNSServer_ListenOnV6 || !gDNSServer_ListenOnV4 ) ? true : false;
7607 #if( TARGET_OS_DARWIN )
7608 	if( gDNSServer_LoopbackOnly && listenOnV6 )
7609 	{
7610 		err = CheckIntegerArgument( gDNSServer_ExtraV6Count, "extra IPv6", 0, 100 );
7611 		require_noerr_quiet( err, exit );
7612 		extraLoopbackV6Count = (size_t) gDNSServer_ExtraV6Count;
7613 		if( extraLoopbackV6Count > 0 )
7614 		{
7615 			err = CheckRootUser();
7616 			require_noerr_quiet( err, exit );
7617 		}
7618 	}
7619 	else
7620 	{
7621 		extraLoopbackV6Count = 0;
7622 	}
7623 #endif
7624 	cmd = (DNSServerCmdContext *) calloc( 1, sizeof( *cmd ) );
7625 	require_action( cmd, exit, err = kNoMemoryErr );
7626 
7627 	cmd->domainOverride	= gDNSServer_DomainOverride;
7628 	cmd->loopbackOnly	= gDNSServer_LoopbackOnly ? true : false;
7629 #if( TARGET_OS_DARWIN )
7630 	cmd->followPID		= -1;
7631 	if( gDNSServer_FollowPID )
7632 	{
7633 		cmd->followPID = _StringToPID( gDNSServer_FollowPID, &err );
7634 		if( err || ( cmd->followPID < 0 ) )
7635 		{
7636 			FPrintF( stderr, "error: Invalid follow PID: %s\n", gDNSServer_FollowPID );
7637 			err = kParamErr;
7638 			goto exit;
7639 		}
7640 	}
7641 #endif
7642 
7643 	// Set up IP addresses.
7644 
7645 	if( listenOnV4 ) ++cmd->addrCount;
7646 	if( listenOnV6 ) ++cmd->addrCount;
7647 #if( TARGET_OS_DARWIN )
7648 	cmd->addrCount += extraLoopbackV6Count;
7649 #endif
7650 	check( cmd->addrCount > 0 );
7651 	cmd->addrArray = (sockaddr_ip *) calloc( cmd->addrCount, sizeof( *cmd->addrArray ) );
7652 	require_action( cmd->addrArray, exit, err = kNoMemoryErr );
7653 
7654 	addrCount = 0;
7655 	if( listenOnV4 )
7656 	{
7657 		sip = &cmd->addrArray[ addrCount++ ];
7658 		_SockAddrInitIPv4( &sip->v4, cmd->loopbackOnly ? INADDR_LOOPBACK : INADDR_ANY, (uint16_t) gDNSServer_Port );
7659 	}
7660 	if( listenOnV6 )
7661 	{
7662 		const struct in6_addr * const		addr = cmd->loopbackOnly ? &in6addr_loopback : &in6addr_any;
7663 
7664 		sip = &cmd->addrArray[ addrCount++ ];
7665 		_SockAddrInitIPv6( &sip->v6, addr->s6_addr, 0, (uint16_t) gDNSServer_Port );
7666 	}
7667 #if( TARGET_OS_DARWIN )
7668 	if( extraLoopbackV6Count > 0 )
7669 	{
7670 		err = _DNSServerCmdAddExtraLoopbackAddrs( &cmd->addrArray[ addrCount ], extraLoopbackV6Count,
7671 			(uint16_t) gDNSServer_Port );
7672 		require_noerr( err, exit );
7673 		addrCount += extraLoopbackV6Count;
7674 	}
7675 #endif
7676 	check( addrCount == cmd->addrCount );
7677 
7678 	// Start command.
7679 
7680 	cmd->queue = dispatch_queue_create( "com.apple.dnssdutil.server-command", DISPATCH_QUEUE_SERIAL );
7681 	require_action( cmd->queue, exit, err = kNoResourcesErr );
7682 
7683 	cmd->doneSem = dispatch_semaphore_create( 0 );
7684 	require_action( cmd->doneSem, exit, err = kNoResourcesErr );
7685 
7686 	dispatch_async_f( cmd->queue, cmd, _DNSServerCmdStart );
7687     dispatch_semaphore_wait( cmd->doneSem, DISPATCH_TIME_FOREVER );
7688 
7689 exit:
7690 	if( err ) FPrintF( stderr, "Failed to start DNS server: %#m\n", err );
7691 	if( cmd ) _DNSServerCmdContextFree( cmd );
7692 	gExitCode = err ? 1 : 0;
7693 }
7694 
7695 //===========================================================================================================================
7696 
_DNSServerCmdStart(void * inCtx)7697 static void	_DNSServerCmdStart( void *inCtx )
7698 {
7699 	OSStatus						err;
7700 	DNSServerCmdContext * const		cmd = (DNSServerCmdContext *) inCtx;
7701 	size_t							i;
7702 
7703 	signal( SIGINT, SIG_IGN );
7704 	err = DispatchSignalSourceCreate( SIGINT, cmd->queue, _DNSServerCmdSIGINTHandler, cmd, &cmd->sourceSigInt );
7705 	require_noerr( err, exit );
7706 	dispatch_resume( cmd->sourceSigInt );
7707 
7708 	signal( SIGTERM, SIG_IGN );
7709 	err = DispatchSignalSourceCreate( SIGTERM, cmd->queue, _DNSServerCmdSIGTERMHandler, cmd, &cmd->sourceSigTerm );
7710 	require_noerr( err, exit );
7711 	dispatch_resume( cmd->sourceSigTerm );
7712 
7713 #if( TARGET_OS_DARWIN )
7714 	if( cmd->followPID >= 0 )
7715 	{
7716 		err = DispatchProcessMonitorCreate( cmd->followPID, DISPATCH_PROC_EXIT, cmd->queue,
7717 			_DNSServerCmdFollowedProcessHandler, NULL, cmd, &cmd->processMonitor );
7718 		require_noerr( err, exit );
7719 		dispatch_resume( cmd->processMonitor );
7720 	}
7721 #endif
7722 	err = _DNSServerCreate( cmd->queue, _DNSServerCmdServerStartHandler, _DNSServerCmdServerStopHandler, cmd,
7723 		(unsigned int) gDNSServer_ResponseDelayMs, (uint32_t) gDNSServer_DefaultTTL, cmd->addrArray, cmd->addrCount,
7724 		cmd->domainOverride, gDNSServer_BadUDPMode ? true : false, &cmd->server );
7725 	require_noerr( err, exit );
7726 
7727 	for( i = 0; i < gDNSServer_IgnoredQTypesCount; ++i )
7728 	{
7729 		uint16_t		qtype;
7730 
7731 		err = RecordTypeFromArgString( gDNSServer_IgnoredQTypes[ i ], &qtype );
7732 		require_noerr( err, exit );
7733 
7734 		err = _DNSServerSetIgnoredQType( cmd->server, qtype );
7735 		require_noerr( err, exit );
7736 	}
7737 	_DNSServerStart( cmd->server );
7738 
7739 exit:
7740 	if( err ) _DNSServerCmdStop( cmd, err );
7741 }
7742 
7743 //===========================================================================================================================
7744 
_DNSServerCmdStop(DNSServerCmdContext * inCmd,OSStatus inError)7745 static void	_DNSServerCmdStop( DNSServerCmdContext *inCmd, OSStatus inError )
7746 {
7747 	if( !inCmd->error ) inCmd->error = inError;
7748 	check( !inCmd->server );
7749 	dispatch_source_forget( &inCmd->sourceSigInt );
7750 	dispatch_source_forget( &inCmd->sourceSigTerm );
7751 #if( TARGET_OS_DARWIN )
7752 	dispatch_source_forget( &inCmd->processMonitor );
7753 #endif
7754 	dispatch_semaphore_signal( inCmd->doneSem );
7755 }
7756 
7757 #if( TARGET_OS_DARWIN )
7758 //===========================================================================================================================
7759 
_DNSServerCmdAddExtraLoopbackAddrs(sockaddr_ip * inAddrArray,size_t inAddrCount,uint16_t inPort)7760 static OSStatus	_DNSServerCmdAddExtraLoopbackAddrs( sockaddr_ip *inAddrArray, size_t inAddrCount, uint16_t inPort )
7761 {
7762 	OSStatus		err;
7763 	uint8_t			addrV6[ 16 ];
7764 	size_t			i;
7765 
7766 	check_compile_time_code( sizeof( kExtraLoopbackIPv6Prefix ) == 8 );
7767 	memcpy( addrV6, kExtraLoopbackIPv6Prefix, 8 );	// 64-bit prefix
7768 	RandomBytes( &addrV6[ 8 ], 4 );					// 32-bit random
7769 	WriteBig32( &addrV6[ 12 ], 2 );					// 16-bit base offset starting at 2
7770 	for( i = 0; i < inAddrCount; ++i )
7771 	{
7772 		struct sockaddr_in6 * const		sin6 = &inAddrArray[ i ].v6;
7773 
7774 		err = _InterfaceIPv6AddressAdd( "lo0", addrV6, kExtraLoopbackIPv6PrefixBitLen );
7775 		require_noerr( err, exit );
7776 
7777 		_SockAddrInitIPv6( sin6, addrV6, 0, inPort );
7778 		BigEndianIntegerIncrement( addrV6, sizeof( addrV6 ) );
7779 	}
7780 	err = kNoErr;
7781 
7782 exit:
7783 	return( err );
7784 }
7785 #endif
7786 
7787 //===========================================================================================================================
7788 
_DNSServerCmdContextFree(DNSServerCmdContext * inCmd)7789 static void	_DNSServerCmdContextFree( DNSServerCmdContext *inCmd )
7790 {
7791 #if( TARGET_OS_DARWIN )
7792 	size_t		i;
7793 #endif
7794 
7795 	check( !inCmd->server );
7796 	check( !inCmd->sourceSigInt );
7797 	check( !inCmd->sourceSigTerm );
7798 #if( TARGET_OS_DARWIN )
7799 	check( !inCmd->processMonitor );
7800 	for( i = 0; i < inCmd->addrCount; ++i )
7801 	{
7802 		OSStatus								err;
7803 		const struct sockaddr_in6 * const		sin6 = &inCmd->addrArray[ i ].v6;
7804 		int										cmp;
7805 
7806 		if( sin6->sin6_family != AF_INET6 ) continue;
7807 		cmp = memcmp( sin6->sin6_addr.s6_addr, kExtraLoopbackIPv6Prefix, sizeof( kExtraLoopbackIPv6Prefix ) );
7808 		if( cmp != 0 ) continue;
7809 		err = _InterfaceIPv6AddressRemove( "lo0", sin6->sin6_addr.s6_addr );
7810 		check_noerr( err );
7811 	}
7812 #endif
7813 	dispatch_forget( &inCmd->queue );
7814 	dispatch_forget( &inCmd->doneSem );
7815 	ForgetMem( &inCmd->addrArray );
7816 	free( inCmd );
7817 }
7818 
7819 //===========================================================================================================================
7820 
_DNSServerCmdServerStartHandler(const sockaddr_ip * inServerArray,size_t inServerCount,void * inCtx)7821 static void	_DNSServerCmdServerStartHandler( const sockaddr_ip *inServerArray, size_t inServerCount, void *inCtx )
7822 {
7823 #if( TARGET_OS_DARWIN )
7824 	OSStatus						err;
7825 	DNSServerCmdContext * const		cmd		= (DNSServerCmdContext *) inCtx;
7826 	const char * const				domain	= cmd->domainOverride ? cmd->domainOverride : "d.test.";
7827 
7828 	if( cmd->loopbackOnly )
7829 	{
7830 		err = _DNSServerCmdLoopbackResolverAdd( domain, inServerArray, inServerCount );
7831 		if( err )
7832 		{
7833 			ds_ulog( kLogLevelError, "Failed to add loopback resolver to DNS configuration for \"%s\" domain: %#m\n",
7834 				domain, err );
7835 			cmd->error = err;
7836 			DNSServerForget( &cmd->server );
7837 		}
7838 		else
7839 		{
7840 			cmd->addedResolver = true;
7841 		}
7842 	}
7843 #else
7844 	Unused( inServerArray );
7845 	Unused( inServerCount );
7846 	Unused( inCtx );
7847 #endif
7848 }
7849 
7850 //===========================================================================================================================
7851 
_DNSServerCmdServerStopHandler(OSStatus inError,void * inCtx)7852 static void	_DNSServerCmdServerStopHandler( OSStatus inError, void *inCtx )
7853 {
7854 	DNSServerCmdContext * const		cmd = (DNSServerCmdContext *) inCtx;
7855 
7856 	if( inError ) ds_ulog( kLogLevelError, "The server stopped unexpectedly with error: %#m.\n", inError );
7857 #if( TARGET_OS_DARWIN )
7858 	if( cmd->addedResolver )
7859 	{
7860 		OSStatus		err;
7861 		err = _DNSServerCmdLoopbackResolverRemove();
7862 		if( err ) ds_ulog( kLogLevelError, "Failed to remove loopback resolver from DNS configuration: %#m\n", err );
7863 		if( !err ) cmd->addedResolver = false;
7864 	}
7865 #endif
7866 	_DNSServerCmdStop( cmd, cmd->error ? cmd->error : inError );
7867 }
7868 
7869 //===========================================================================================================================
7870 
_DNSServerCmdSIGINTHandler(void * inCtx)7871 static void	_DNSServerCmdSIGINTHandler( void *inCtx )
7872 {
7873 	_DNSServerCmdShutdown( (DNSServerCmdContext *) inCtx, SIGINT );
7874 }
7875 
7876 //===========================================================================================================================
7877 
_DNSServerCmdSIGTERMHandler(void * inCtx)7878 static void	_DNSServerCmdSIGTERMHandler( void *inCtx )
7879 {
7880 	_DNSServerCmdShutdown( (DNSServerCmdContext *) inCtx, SIGTERM );
7881 }
7882 
7883 //===========================================================================================================================
7884 
_DNSServerCmdShutdown(DNSServerCmdContext * inCmd,int inSignal)7885 static void	_DNSServerCmdShutdown( DNSServerCmdContext *inCmd, int inSignal )
7886 {
7887 	dispatch_source_forget( &inCmd->sourceSigInt );
7888 	dispatch_source_forget( &inCmd->sourceSigTerm );
7889 #if( TARGET_OS_DARWIN )
7890 	dispatch_source_forget( &inCmd->processMonitor );
7891 	if( inSignal == 0 )
7892 	{
7893 		ds_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited\n", (int64_t) inCmd->followPID );
7894 	}
7895 	else
7896 #endif
7897 	{
7898 		const char *		sigName;
7899 
7900 		switch( inSignal )
7901 		{
7902 			case SIGINT:	sigName = "SIGINT";		break;
7903 			case SIGTERM:	sigName = "SIGTERM";	break;
7904 			default:		sigName = "???";		break;
7905 		}
7906 		ds_ulog( kLogLevelNotice, "Exiting: received signal %d (%s)\n", inSignal, sigName );
7907 	}
7908 	DNSServerForget( &inCmd->server );
7909 }
7910 
7911 #if( TARGET_OS_DARWIN )
7912 //===========================================================================================================================
7913 
_DNSServerCmdFollowedProcessHandler(void * inCtx)7914 static void	_DNSServerCmdFollowedProcessHandler( void *inCtx )
7915 {
7916 	DNSServerCmdContext * const		cmd = (DNSServerCmdContext *) inCtx;
7917 
7918 	if( dispatch_source_get_data( cmd->processMonitor ) & DISPATCH_PROC_EXIT ) _DNSServerCmdShutdown( cmd, 0 );
7919 }
7920 //===========================================================================================================================
7921 
7922 #define kDNSServerServiceID		CFSTR( "com.apple.dnssdutil.server" )
7923 
_DNSServerCmdLoopbackResolverAdd(const char * inDomain,const sockaddr_ip * inAddrArray,size_t inAddrCount)7924 static OSStatus	_DNSServerCmdLoopbackResolverAdd( const char *inDomain, const sockaddr_ip *inAddrArray, size_t inAddrCount )
7925 {
7926 	OSStatus				err;
7927 	CFPropertyListRef		plist	= NULL;
7928 	SCDynamicStoreRef		store	= NULL;
7929 	CFStringRef				key		= NULL;
7930 	CFMutableArrayRef		addresses;
7931 	size_t					i;
7932 	Boolean					ok;
7933 	char					dnssecDomainStr[ kDNSServiceMaxDomainName ];
7934 
7935 	require_action_quiet( inAddrCount > 0, exit, err = kCountErr );
7936 
7937 	err = DomainNameToString( kDNSServerDomain_DNSSEC, NULL, dnssecDomainStr, NULL );
7938 	require_noerr( err, exit );
7939 
7940 	err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
7941 		"{"
7942 			"%kO="		// Domain array.
7943 			"["
7944 				"%s"	// Non-DNSSEC domain.
7945 				"%s"	// DNSSEC domain.
7946 				"%O"	// Reverse IPv4 domain.
7947 				"%O"	// Reverse IPv6 domain.
7948 			"]"
7949 			"%kO=[%@]"	// Server IP addresses.
7950 			"%kO=%i"	// Port number.
7951 			"%kO=%O"	// Interface name.
7952 			"%kO=%O"	// Service ID.
7953 		"}",
7954 		kSCPropNetDNSSupplementalMatchDomains,	inDomain,
7955 												dnssecDomainStr,
7956 												CFSTR( kDNSServerReverseIPv4DomainStr ),
7957 												CFSTR( kDNSServerReverseIPv6DomainStr ),
7958 		kSCPropNetDNSServerAddresses,			&addresses,
7959 		kSCPropNetDNSServerPort,				SockAddrGetPort( inAddrArray ),
7960 		kSCPropInterfaceName,					CFSTR( "lo0" ),
7961 		kSCPropNetDNSConfirmedServiceID,		kDNSServerServiceID );
7962 	require_noerr( err, exit );
7963 
7964 	for( i = 0; i < inAddrCount; ++i )
7965 	{
7966 		sockaddr_ip		sip;
7967 
7968 		SockAddrCopy( &inAddrArray[ i ], &sip );
7969 		SockAddrSetPort( &sip, 0 );
7970 		err = CFPropertyListAppendFormatted( kCFAllocatorDefault, addresses, "%##a", &sip );
7971 		require_noerr( err, exit );
7972 	}
7973 	store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
7974 	err = map_scerror( store );
7975 	require_noerr( err, exit );
7976 
7977 	key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, kDNSServerServiceID, kSCEntNetDNS );
7978 	require_action( key, exit, err = kUnknownErr );
7979 
7980 	ok = SCDynamicStoreSetValue( store, key, plist );
7981 	require_action( ok, exit, err = kUnknownErr );
7982 
7983 exit:
7984 	CFReleaseNullSafe( plist );
7985 	CFReleaseNullSafe( store );
7986 	CFReleaseNullSafe( key );
7987 	return( err );
7988 }
7989 
7990 //===========================================================================================================================
7991 
_DNSServerCmdLoopbackResolverRemove(void)7992 static OSStatus	_DNSServerCmdLoopbackResolverRemove( void )
7993 {
7994 	OSStatus				err;
7995 	SCDynamicStoreRef		store;
7996 	CFStringRef				key = NULL;
7997 	Boolean					success;
7998 
7999 	store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
8000 	err = map_scerror( store );
8001 	require_noerr( err, exit );
8002 
8003 	key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, kDNSServerServiceID, kSCEntNetDNS );
8004 	require_action( key, exit, err = kUnknownErr );
8005 
8006 	success = SCDynamicStoreRemoveValue( store, key );
8007 	require_action( success, exit, err = kUnknownErr );
8008 
8009 exit:
8010 	CFReleaseNullSafe( store );
8011 	CFReleaseNullSafe( key );
8012 	return( err );
8013 }
8014 #endif	// TARGET_OS_DARWIN
8015 
8016 //===========================================================================================================================
8017 
8018 typedef struct DNSServerConnectionPrivate *		DNSServerConnectionRef;
8019 
8020 typedef struct DNSServerDelayedResponse		DNSServerDelayedResponse;
8021 struct DNSServerDelayedResponse
8022 {
8023 	DNSServerDelayedResponse *		next;		// Next delayed response in list.
8024 	sockaddr_ip						client;		// Destination address.
8025 	uint64_t						dueTicks;	// Time, in ticks, when send is due.
8026 	uint8_t *						msgPtr;		// Response message pointer.
8027 	size_t							msgLen;		// Response message length.
8028 	size_t							index;		// Address index.
8029 	SocketRef						sock;		// Socket to use for send.
8030 };
8031 
8032 struct DNSServerPrivate
8033 {
8034 	CFRuntimeBase					base;				// CF object base.
8035 	uint8_t *						domain;				// Parent domain of server's resource records. (malloc'd)
8036 	dispatch_queue_t				queue;				// Queue for DNS server's events.
8037 	sockaddr_ip *					addrArray;			// Array of addresses to listen on.
8038 	size_t							addrCount;			// Number of addresses to listen on.
8039 	dispatch_source_t *				readSourceArrayUDP;	// Array of read sources for UDP sockets.
8040 	dispatch_source_t *				readSourceArrayTCP;	// Array of read sources for TCP listening sockets.
8041 	DNSServerConnectionRef			connectionList;		// List of TCP connections.
8042 	dispatch_source_t				connectionTimer;	// Timer for idle connections.
8043 	DNSServerStartHandler_f			startHandler;		// User's activation handler.
8044 	DNSServerStopHandler_f			stopHandler;		// User's invalidation handler.
8045     void *							userContext;		// User's handler context.
8046 	DNSServerDelayedResponse *		responseList;		// List of delayed UDP responses.
8047 	dispatch_source_t				responseTimer;		// Timer for when to send next delayed response.
8048 	int *							ignoredQTypes;		// Array of QTYPEs to ignore.
8049 	size_t							ignoredQTypeCount;	// Number of QTYPEs to ignore.
8050 	unsigned int					responseDelayMs;	// Response delay in milliseconds.
8051 	uint32_t						defaultTTL;			// Default TTL for resource records.
8052 	uint32_t						serial;				// Serial number for SOA record.
8053 	OSStatus						stopErr;			// The error, if any, that caused the server to stop.
8054 	Boolean							started;			// True if the server was started.
8055 	Boolean							stopped;			// True if the server was stopped.
8056 	Boolean							badUDPMode;			// True if the server runs in Bad UDP mode.
8057 };
8058 
8059 static void	_DNSServerUDPReadHandler( void *inContext );
8060 static OSStatus
8061 	_DNSServerScheduleDelayedResponse(
8062 		DNSServerRef			inServer,
8063 		SocketRef				inSock,
8064 		const struct sockaddr *	inDestAddr,
8065 		uint8_t *				inMsgPtr,
8066 		size_t					inMsgLen,
8067 		size_t					inIndex );
8068 static void	_DNSServerDelayedResponseFree( DNSServerDelayedResponse *inResponse );
8069 static void	_DNSServerDelayedResponseListFree( DNSServerDelayedResponse *inList );
8070 static void	_DNSServerTCPAcceptHandler( void *inContext );
8071 static void	_DNSServerConnectionTimerHandler( void *inContext );
8072 static void	_DNSServerResetConnectionTimerMs( DNSServerRef me, uint64_t inTimeoutMs );
8073 static OSStatus
8074 	_DNSServerAnswerQuery(
8075 		DNSServerRef	inServer,
8076 		const uint8_t *	inMsgPtr,
8077 		size_t			inMsgLen,
8078 		size_t			inIndex,
8079 		Boolean			inForTCP,
8080 		uint8_t **		outResponsePtr,
8081 		size_t *		outResponseLen );
8082 
8083 #define _DNSServerAnswerQueryForUDP( SERVER, QUERY_PTR, QUERY_LEN, INDEX, RESPONSE_PTR, RESPONSE_LEN ) \
8084 	_DNSServerAnswerQuery( SERVER, QUERY_PTR, QUERY_LEN, INDEX, false, RESPONSE_PTR, RESPONSE_LEN )
8085 
8086 #define _DNSServerAnswerQueryForTCP( SERVER, QUERY_PTR, QUERY_LEN, INDEX, RESPONSE_PTR, RESPONSE_LEN ) \
8087 	_DNSServerAnswerQuery( SERVER, QUERY_PTR, QUERY_LEN, INDEX, true, RESPONSE_PTR, RESPONSE_LEN )
8088 
8089 CF_CLASS_DEFINE( DNSServer );
8090 
8091 struct DNSServerConnectionPrivate
8092 {
8093 	CFRuntimeBase				base;				// CF object base.
8094 	DNSServerConnectionRef		next;				// Next connection in list.
8095 	DNSServerRef				server;				// Back pointer to server object.
8096 	sockaddr_ip					local;				// TCP connection's local address.
8097 	sockaddr_ip					remote;				// TCP connection's remote address.
8098 	size_t						index;				// Sever address index.
8099 	uint64_t					expirationTicks;	// Expiration time in ticks. Renewed upon receiving a complete query.
8100 	dispatch_source_t			readSource;			// Dispatch read source for TCP connection.
8101 	dispatch_source_t			writeSource;		// Dispatch write source for TCP connection.
8102 	size_t						offset;				// Offset into receive buffer.
8103 	void *						msgPtr;				// Pointer to dynamically allocated message buffer.
8104 	size_t						msgLen;				// Length of message buffer.
8105 	iovec_t						iov[ 2 ];			// IO vector for writing response message.
8106 	iovec_t *					iovPtr;				// Vector pointer for SocketWriteData().
8107 	int							iovCount;			// Vector count for SocketWriteData().
8108 	Boolean						readSuspended;		// True if the read source is currently suspended.
8109 	Boolean						writeSuspended;		// True if the write source is currently suspended.
8110 	Boolean						haveLen;			// True if currently receiving message instead of message length.
8111 	uint8_t						lenBuf[ 2 ];		// Buffer for two-octet message length field.
8112 };
8113 
8114 static CFTypeID	DNSServerConnectionGetTypeID( void );
8115 static OSStatus
8116 	_DNSServerConnectionCreate(
8117 		DNSServerRef				inServer,
8118 		const struct sockaddr *		inLocal,
8119 		const struct sockaddr *		inRemote,
8120 		size_t						inIndex,
8121 		DNSServerConnectionRef *	outCnx );
8122 static OSStatus	_DNSServerConnectionStart( DNSServerConnectionRef inCnx, SocketRef inSock );
8123 static void		_DNSServerConnectionStop( DNSServerConnectionRef inCnx, Boolean inRemoveFromList );
8124 static void		_DNSServerConnectionReadHandler( void *inContext );
8125 static void		_DNSServerConnectionWriteHandler( void *inContext );
8126 static void		_DNSServerConnectionRenewExpiration( DNSServerConnectionRef inCnx );
8127 
8128 CF_CLASS_DEFINE( DNSServerConnection );
8129 
8130 static OSStatus
_DNSServerCreate(dispatch_queue_t inQueue,DNSServerStartHandler_f inStartHandler,DNSServerStopHandler_f inStopHandler,void * inUserContext,unsigned int inResponseDelayMs,uint32_t inDefaultTTL,const sockaddr_ip * inAddrArray,size_t inAddrCount,const char * inDomain,Boolean inBadUDPMode,DNSServerRef * outServer)8131 	_DNSServerCreate(
8132 		dispatch_queue_t		inQueue,
8133 		DNSServerStartHandler_f	inStartHandler,
8134 		DNSServerStopHandler_f	inStopHandler,
8135 		void *					inUserContext,
8136 		unsigned int			inResponseDelayMs,
8137 		uint32_t				inDefaultTTL,
8138 		const sockaddr_ip *		inAddrArray,
8139 		size_t					inAddrCount,
8140 		const char *			inDomain,
8141 		Boolean					inBadUDPMode,
8142 		DNSServerRef *			outServer )
8143 {
8144 	OSStatus			err;
8145 	DNSServerRef		obj = NULL;
8146 
8147 	require_action_quiet( inDefaultTTL <= INT32_MAX, exit, err = kRangeErr );
8148 
8149 	CF_OBJECT_CREATE( DNSServer, obj, err, exit );
8150 
8151 	ReplaceDispatchQueue( &obj->queue, inQueue );
8152 	obj->startHandler		= inStartHandler;
8153 	obj->stopHandler		= inStopHandler;
8154 	obj->userContext		= inUserContext;
8155 	obj->responseDelayMs	= inResponseDelayMs;
8156 	obj->defaultTTL			= inDefaultTTL;
8157 	obj->badUDPMode			= inBadUDPMode;
8158 	obj->addrCount			= inAddrCount;
8159 
8160 	obj->addrArray = (sockaddr_ip *) _memdup( inAddrArray, obj->addrCount * sizeof( *obj->addrArray ) );
8161 	require_action( obj->addrArray, exit, err = kNoMemoryErr );
8162 
8163 	obj->readSourceArrayUDP = (dispatch_source_t *) calloc( obj->addrCount, sizeof( *obj->readSourceArrayUDP ) );
8164 	require_action( obj->readSourceArrayUDP, exit, err = kNoMemoryErr );
8165 
8166 	obj->readSourceArrayTCP = (dispatch_source_t *) calloc( obj->addrCount, sizeof( *obj->readSourceArrayTCP ) );
8167 	require_action( obj->readSourceArrayTCP, exit, err = kNoMemoryErr );
8168 
8169 	if( inDomain )
8170 	{
8171 		err = StringToDomainName( inDomain, &obj->domain, NULL );
8172 		require_noerr_quiet( err, exit );
8173 	}
8174 	else
8175 	{
8176 		err = DomainNameDup( kDNSServerDomain_Default, &obj->domain, NULL );
8177 		require_noerr_quiet( err, exit );
8178 	}
8179 	*outServer = obj;
8180 	obj = NULL;
8181 	err = kNoErr;
8182 
8183 exit:
8184 	CFReleaseNullSafe( obj );
8185 	return( err );
8186 }
8187 
8188 //===========================================================================================================================
8189 
_DNSServerFinalize(CFTypeRef inObj)8190 static void	_DNSServerFinalize( CFTypeRef inObj )
8191 {
8192 	DNSServerRef const		me = (DNSServerRef) inObj;
8193 	size_t					i;
8194 
8195 	check( !me->responseTimer );
8196 	check( !me->connectionList );
8197 	check( !me->connectionTimer );
8198 	ForgetMem( &me->addrArray );
8199 	if( me->readSourceArrayUDP )
8200 	{
8201 		for( i = 0; i < me->addrCount; ++i ) check( !me->readSourceArrayUDP[ i ] );
8202 		ForgetMem( &me->readSourceArrayUDP );
8203 	}
8204 	if( me->readSourceArrayTCP )
8205 	{
8206 		for( i = 0; i < me->addrCount; ++i ) check( !me->readSourceArrayTCP[ i ] );
8207 		ForgetMem( &me->readSourceArrayTCP );
8208 	}
8209 	ForgetMem( &me->domain );
8210 	dispatch_forget( &me->queue );
8211 	ForgetMem( &me->ignoredQTypes );
8212 }
8213 
8214 //===========================================================================================================================
8215 
_DNSServerSetIgnoredQType(DNSServerRef me,int inQType)8216 static OSStatus	_DNSServerSetIgnoredQType( DNSServerRef me, int inQType )
8217 {
8218 	size_t		newCount;
8219 	int *		mem;
8220 
8221 	newCount = me->ignoredQTypeCount + 1;
8222 	require_return_value( newCount <= SIZE_MAX / sizeof( int ), kSizeErr );
8223 
8224 	mem = realloc( me->ignoredQTypes, newCount * sizeof( int ) );
8225 	require_return_value( mem, kNoMemoryErr );
8226 
8227 	me->ignoredQTypes = mem;
8228 	me->ignoredQTypes[ me->ignoredQTypeCount++ ] = inQType;
8229 	return( kNoErr );
8230 }
8231 
8232 //===========================================================================================================================
8233 
8234 static void		_DNSServerStartOnQueue( void *inContext );
8235 static void		_DNSServerStartInternal( DNSServerRef inServer );
8236 static OSStatus	_DNSServerSetUpSockets( DNSServerRef inServer );
8237 static void		_DNSServerStopInternal( void *inContext, OSStatus inError );
8238 static SocketContext *
8239 	_DNSServerSocketContextCreate(
8240 		SocketRef		inSock,
8241 		DNSServerRef	inServer,
8242 		size_t			inIndex,
8243 		OSStatus *		outError );
8244 
_DNSServerStart(DNSServerRef me)8245 static void	_DNSServerStart( DNSServerRef me )
8246 {
8247 	CFRetain( me );
8248 	dispatch_async_f( me->queue, me, _DNSServerStartOnQueue );
8249 }
8250 
_DNSServerStartOnQueue(void * inContext)8251 static void	_DNSServerStartOnQueue( void *inContext )
8252 {
8253 	const DNSServerRef		me = (DNSServerRef) inContext;
8254 
8255 	_DNSServerStartInternal( me );
8256 	CFRelease( me );
8257 }
8258 
_DNSServerStartInternal(DNSServerRef me)8259 static void _DNSServerStartInternal( DNSServerRef me )
8260 {
8261 	OSStatus				err;
8262 	struct timeval			now;
8263 	SocketRef				sock	= kInvalidSocketRef;
8264 	SocketContext *			sockCtx	= NULL;
8265 	int						year, month, day;
8266 
8267 	require_action_quiet( !me->started && !me->stopped, exit, err = kNoErr );
8268 	me->started = true;
8269 	CFRetain( me );
8270 
8271 	err = _DNSServerSetUpSockets( me );
8272 	require_noerr( err, exit );
8273 
8274 	if( me->startHandler ) me->startHandler( me->addrArray, me->addrCount, me->userContext );
8275 
8276 	// Create the serial number for the server's SOA record in the YYYMMDDnn convention recommended by
8277 	// <https://tools.ietf.org/html/rfc1912#section-2.2> using the current time.
8278 
8279 	gettimeofday( &now, NULL );
8280 	SecondsToYMD_HMS( ( INT64_C_safe( kDaysToUnixEpoch ) * kSecondsPerDay ) + now.tv_sec, &year, &month, &day,
8281 		NULL, NULL, NULL );
8282 	me->serial = (uint32_t)( ( year * 1000000 ) + ( month * 10000 ) + ( day * 100 ) + 1 );
8283 	err = kNoErr;
8284 
8285 exit:
8286 	ForgetSocket( &sock );
8287 	if( sockCtx ) SocketContextRelease( sockCtx );
8288 	if( err ) _DNSServerStopInternal( me, err );
8289 }
8290 
8291 typedef struct
8292 {
8293 	SocketRef		sockUDP;
8294 	SocketRef		sockTCP;
8295 
8296 }	_DNSServerSocketPair;
8297 
8298 #define kDNSServerMaxBindTryCount	10
8299 
_DNSServerSetUpSockets(DNSServerRef me)8300 static OSStatus	_DNSServerSetUpSockets( DNSServerRef me )
8301 {
8302 	OSStatus					err;
8303 	SocketContext *				sockCtx			= NULL;
8304 	_DNSServerSocketPair *		sockPairs		= NULL;
8305 	_DNSServerSocketPair *		sockPairsHeap	= NULL;
8306 	_DNSServerSocketPair		sockPairsStack[ 16 ];
8307 	size_t						i;
8308 	const size_t				addrCount		= me->addrCount; // Don't use me->addrCount to avoid false analyzer warning.
8309 	int							portWanted, tryCount, tryCountMax;
8310 
8311 	require_action_quiet( addrCount > 0, exit, err = kNoErr );
8312 
8313 	sockPairs = sockPairsStack;
8314 	if( me->addrCount > countof( sockPairsStack ) )
8315 	{
8316 		sockPairsHeap = (_DNSServerSocketPair *) calloc( me->addrCount, sizeof( *sockPairsHeap ) );
8317 		require_action( sockPairsHeap, exit, err = kNoMemoryErr );
8318 		sockPairs = sockPairsHeap;
8319 	}
8320 	for( i = 0; i < addrCount; ++i )
8321 	{
8322 		sockPairs[ i ].sockUDP = kInvalidSocketRef;
8323 		sockPairs[ i ].sockTCP = kInvalidSocketRef;
8324 	}
8325 	// Create server sockets.
8326 
8327 	err = kNoErr;
8328 	portWanted = SockAddrGetPort( &me->addrArray[ 0 ] );
8329 	tryCountMax = ( portWanted == 0 ) ? kDNSServerMaxBindTryCount : 1;
8330 	for( tryCount = 0; tryCount < tryCountMax; ++tryCount )
8331 	{
8332 		int		portDefault = 0;
8333 
8334 		for( i = 0; i < addrCount; ++i )
8335 		{
8336 			sockaddr_ip * const					sip		= &me->addrArray[ i ];
8337 			_DNSServerSocketPair * const		pair	= &sockPairs[ i ];
8338 			const void *						address;
8339 			SocketRef							sock;
8340 			int									port, portActual;
8341 			sockaddr_ip							tmpSA;
8342 
8343 			switch( sip->sa.sa_family )
8344 			{
8345 				case AF_INET:	address = &sip->v4.sin_addr.s_addr;  break;
8346 				case AF_INET6:	address = sip->v6.sin6_addr.s6_addr; break;
8347 				default:
8348 					ds_ulog( kLogLevelError, "Unhandled address family %d", sip->sa.sa_family );
8349 					err = kTypeErr;
8350 					goto exit;
8351 			}
8352 			// Create UDP socket.
8353 			// Initially, portWanted is the port requested by the user. If it's 0, then the user wants any available
8354 			// ephemeral port. If it's negative, then the user would like a port number equal to its absolute value, but
8355 			// will settle for any available ephemeral port, if it's not available. The actual port number that was used
8356 			// will be stored in portActual and used for the remaining addresses that don't specify a non-zero port.
8357 
8358 			port = ( portWanted == 0 ) ? portDefault : portWanted;
8359 			err = _ServerSocketOpenEx2( sip->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP, address, port, &portActual,
8360 				kSocketBufferSize_DontSet, true, &sock );
8361 			if( err == EADDRINUSE )
8362 			{
8363 				SockAddrCopy( sip, &tmpSA );
8364 				SockAddrSetPort( &tmpSA, port );
8365 				ds_ulog( kLogLevelError, "IP address %##a is already in use for UDP\n", &tmpSA );
8366 				break;
8367 			}
8368 			require_noerr( err, exit );
8369 			check( ( portWanted == 0 ) || ( portActual == portWanted ) );
8370 
8371 			ForgetSocket( &pair->sockUDP );
8372 			pair->sockUDP = sock;
8373 			sock = kInvalidSocketRef;
8374 			if( portDefault == 0 ) portDefault = portActual;
8375 
8376 			// Create TCP socket.
8377 
8378 			err = _ServerSocketOpenEx2( sip->sa.sa_family, SOCK_STREAM, IPPROTO_TCP, address, portActual, NULL,
8379 				kSocketBufferSize_DontSet, false, &sock );
8380 			if( err == EADDRINUSE )
8381 			{
8382 				SockAddrCopy( sip, &tmpSA );
8383 				SockAddrSetPort( &tmpSA, portActual );
8384 				ds_ulog( kLogLevelError, "IP address %##a is already in use for TCP\n", &tmpSA );
8385 				break;
8386 			}
8387 			require_noerr( err, exit );
8388 
8389 			ForgetSocket( &pair->sockTCP );
8390 			pair->sockTCP = sock;
8391 			sock = kInvalidSocketRef;
8392 
8393 			SockAddrSetPort( sip, portActual );
8394 		}
8395 		if( !err ) break;
8396 	}
8397 	require_noerr( err, exit );
8398 
8399 	// Create read sources for server sockets.
8400 
8401 	for( i = 0; i < addrCount; ++i )
8402 	{
8403 		const sockaddr_ip * const			sip					= &me->addrArray[ i ];
8404 		dispatch_source_t * const			readSourceUDPPtr	= &me->readSourceArrayUDP[ i ];
8405 		dispatch_source_t * const			readSourceTCPPtr	= &me->readSourceArrayTCP[ i ];
8406 		_DNSServerSocketPair * const		pair				= &sockPairs[ i ];
8407 
8408 		// Create read source for UDP socket.
8409 
8410 		check( IsValidSocket( pair->sockUDP ) );
8411 		sockCtx = _DNSServerSocketContextCreate( pair->sockUDP, me, i, &err );
8412 		require_noerr( err, exit );
8413 		pair->sockUDP = kInvalidSocketRef;
8414 
8415 		err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler,
8416 			sockCtx, readSourceUDPPtr );
8417 		require_noerr( err, exit );
8418 		dispatch_resume( *readSourceUDPPtr );
8419 		sockCtx = NULL;
8420 
8421 		// Create read source for TCP socket.
8422 
8423 		check( IsValidSocket( pair->sockTCP ) );
8424 		sockCtx = _DNSServerSocketContextCreate( pair->sockTCP, me, i, &err );
8425 		require_noerr( err, exit );
8426 		pair->sockTCP = kInvalidSocketRef;
8427 
8428 		err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPAcceptHandler, SocketContextCancelHandler,
8429 			sockCtx, readSourceTCPPtr );
8430 		require_noerr( err, exit );
8431 		dispatch_resume( *readSourceTCPPtr );
8432 		sockCtx = NULL;
8433 
8434 		ds_ulog( kLogLevelInfo, "Server is listening on %##a\n", sip );
8435 	}
8436 
8437 exit:
8438 	if( sockPairs )
8439 	{
8440 		for( i = 0; i < addrCount; ++i )
8441 		{
8442 			ForgetSocket( &sockPairs[ i ].sockUDP );
8443 			ForgetSocket( &sockPairs[ i ].sockTCP );
8444 		}
8445 	}
8446 	FreeNullSafe( sockPairsHeap );
8447 	if( sockCtx ) SocketContextRelease( sockCtx );
8448 	return( err );
8449 }
8450 
8451 //===========================================================================================================================
8452 
8453 typedef struct
8454 {
8455 	DNSServerRef	server;
8456 	size_t			index;
8457 
8458 }	DNSServerContext;
8459 
8460 static void	_DNSServerContextFree( DNSServerContext *inCtx );
8461 static void	_DNSServerSocketContextFinalizer( void *inCtx );
8462 
8463 static SocketContext *
_DNSServerSocketContextCreate(SocketRef inSock,DNSServerRef inServer,size_t inIndex,OSStatus * outError)8464 	_DNSServerSocketContextCreate(
8465 		SocketRef		inSock,
8466 		DNSServerRef	inServer,
8467 		size_t			inIndex,
8468 		OSStatus *		outError )
8469 {
8470 	OSStatus				err;
8471 	SocketContext *			sockCtx = NULL;
8472 	DNSServerContext *		ctx;
8473 
8474 	ctx = (DNSServerContext *) calloc( 1, sizeof( *ctx ) );
8475 	require_action( ctx, exit, err = kNoMemoryErr );
8476 
8477 	ctx->index	= inIndex;
8478 	ctx->server	= inServer;
8479 	CFRetain( ctx->server );
8480 
8481 	sockCtx = SocketContextCreateEx( inSock, ctx, _DNSServerSocketContextFinalizer, &err );
8482 	require_noerr( err, exit );
8483 	ctx	= NULL;
8484 
8485 exit:
8486 	if( outError ) *outError = err;
8487 	if( ctx ) _DNSServerContextFree( ctx );
8488 	return( sockCtx );
8489 }
8490 
_DNSServerSocketContextFinalizer(void * inCtx)8491 static void	_DNSServerSocketContextFinalizer( void *inCtx )
8492 {
8493 	_DNSServerContextFree( (DNSServerContext *) inCtx );
8494 }
8495 
_DNSServerContextFree(DNSServerContext * inCtx)8496 static void	_DNSServerContextFree( DNSServerContext *inCtx )
8497 {
8498 	ForgetCF( &inCtx->server );
8499 	free( inCtx );
8500 }
8501 
8502 //===========================================================================================================================
8503 
8504 static void	_DNSServerStopOnQueue( void *inContext );
8505 static void	_DNSServerStop2( void *inContext );
8506 
_DNSServerStop(DNSServerRef me)8507 static void	_DNSServerStop( DNSServerRef me )
8508 {
8509 	CFRetain( me );
8510 	dispatch_async_f( me->queue, me, _DNSServerStopOnQueue );
8511 }
8512 
_DNSServerStopOnQueue(void * inContext)8513 static void	_DNSServerStopOnQueue( void *inContext )
8514 {
8515 	DNSServerRef const		me = (DNSServerRef) inContext;
8516 
8517 	_DNSServerStopInternal( me, kNoErr );
8518 	CFRelease( me );
8519 }
8520 
_DNSServerStopInternal(void * inContext,OSStatus inError)8521 static void	_DNSServerStopInternal( void *inContext, OSStatus inError )
8522 {
8523 	DNSServerRef const			me = (DNSServerRef) inContext;
8524 	DNSServerConnectionRef		cnx;
8525 	size_t						i;
8526 
8527 	require_quiet( !me->stopped, exit );
8528 	me->stopped = true;
8529 
8530 	me->stopErr = inError;
8531 	if( me->responseList )
8532 	{
8533 		_DNSServerDelayedResponseListFree( me->responseList );
8534 		me->responseList = NULL;
8535 	}
8536 	dispatch_source_forget( &me->responseTimer );
8537 	for( i = 0; i < me->addrCount; ++i )
8538 	{
8539 		dispatch_source_forget( &me->readSourceArrayUDP[ i ] );
8540 		dispatch_source_forget( &me->readSourceArrayTCP[ i ] );
8541 	}
8542 	while( ( cnx = me->connectionList ) != NULL )
8543 	{
8544 		me->connectionList = cnx->next;
8545 		_DNSServerConnectionStop( cnx, false );
8546 		cnx->next = NULL;
8547 		CFRelease( cnx );
8548 	}
8549 	dispatch_source_forget( &me->connectionTimer );
8550 
8551 	CFRetain( me );
8552 	dispatch_async_f( me->queue, me, _DNSServerStop2 );
8553 	if( me->started ) CFRelease( me );
8554 
8555 exit:
8556 	return;
8557 }
8558 
_DNSServerStop2(void * inContext)8559 static void	_DNSServerStop2( void *inContext )
8560 {
8561 	DNSServerRef const		me = (DNSServerRef) inContext;
8562 
8563 	if( me->stopHandler ) me->stopHandler( me->stopErr, me->userContext );
8564 	CFRelease( me );
8565 }
8566 
8567 //===========================================================================================================================
8568 
_DNSServerUDPReadHandler(void * inContext)8569 static void	_DNSServerUDPReadHandler( void *inContext )
8570 {
8571 	OSStatus							err;
8572 	SocketContext * const				sockCtx	= (SocketContext *) inContext;
8573 	const DNSServerContext * const		ctx		= (DNSServerContext *) sockCtx->userContext;
8574 	const DNSServerRef					me		= ctx->server;
8575 	ssize_t								n;
8576 	sockaddr_ip							client;
8577 	socklen_t							clientLen;
8578 	uint8_t *							respPtr	= NULL;	// malloc'd
8579 	size_t								respLen;
8580 	uint8_t								msg[ 512 ];
8581 
8582 	// Receive message.
8583 
8584 	clientLen = (socklen_t) sizeof( client );
8585 	n = recvfrom( sockCtx->sock, (char *) msg, sizeof( msg ), 0, &client.sa, &clientLen );
8586 	err = map_socket_value_errno( sockCtx->sock, n >= 0, n );
8587 	require_noerr( err, exit );
8588 
8589 	if( n < kDNSHeaderLength )
8590 	{
8591 		ds_ulog( kLogLevelInfo, "UDP: Received %zd bytes from %##a to %##a: Message is too small (< %d bytes)\n",
8592 			n, &client, &me->addrArray[ ctx->index ], kDNSHeaderLength );
8593 		goto exit;
8594 	}
8595 	ds_ulog( kLogLevelInfo, "UDP: Received %zd bytes from %##a to %##a -- %.1{du:dnsmsg}\n",
8596 		n, &client, &me->addrArray[ ctx->index ], msg, (size_t) n );
8597 
8598 	// Create response.
8599 
8600 	err = _DNSServerAnswerQueryForUDP( me, msg, (size_t) n, ctx->index + 1, &respPtr, &respLen );
8601 	if( err == kSkipErr ) ds_ulog( kLogLevelInfo, "UDP: Ignoring query\n" );
8602 	require_noerr_quiet( err, exit );
8603 
8604 	if( me->responseDelayMs > 0 )	// Defer response.
8605 	{
8606 		err = _DNSServerScheduleDelayedResponse( me, sockCtx->sock, &client.sa, respPtr, respLen, ctx->index );
8607 		require_noerr( err, exit );
8608 		respPtr = NULL;
8609 	}
8610 	else							// Send response.
8611 	{
8612 		ds_ulog( kLogLevelInfo, "UDP: Sending %zu byte response from %##a to %##a -- %.1{du:dnsmsg}\n",
8613 			respLen, &me->addrArray[ ctx->index ], &client, respPtr, respLen );
8614 
8615 		n = sendto( sockCtx->sock, (char *) respPtr, respLen, 0, &client.sa, clientLen );
8616 		err = map_socket_value_errno( sockCtx->sock, n == (ssize_t) respLen, n );
8617 		require_noerr( err, exit );
8618 	}
8619 
8620 exit:
8621 	FreeNullSafe( respPtr );
8622 }
8623 
8624 //===========================================================================================================================
8625 
8626 static void	_DNSServerSendDelayedResponses( void *inContext );
8627 
8628 static OSStatus
_DNSServerScheduleDelayedResponse(DNSServerRef me,SocketRef inSock,const struct sockaddr * inDestAddr,uint8_t * inMsgPtr,size_t inMsgLen,size_t inIndex)8629 	_DNSServerScheduleDelayedResponse(
8630 		DNSServerRef			me,
8631 		SocketRef				inSock,
8632 		const struct sockaddr *	inDestAddr,
8633 		uint8_t *				inMsgPtr,
8634 		size_t					inMsgLen,
8635 		size_t					inIndex )
8636 {
8637 	OSStatus						err;
8638 	DNSServerDelayedResponse *		resp;
8639 	DNSServerDelayedResponse *		newResp;
8640 	DNSServerDelayedResponse **		ptr;
8641 	uint64_t						dueTicks;
8642 
8643 	dueTicks = UpTicks() + MillisecondsToUpTicks( me->responseDelayMs );
8644 	newResp = (DNSServerDelayedResponse *) calloc( 1, sizeof( *newResp ) );
8645 	require_action( newResp, exit, err = kNoMemoryErr );
8646 
8647 	newResp->dueTicks	= dueTicks;
8648 	newResp->msgPtr		= inMsgPtr;
8649 	newResp->msgLen		= inMsgLen;
8650 	newResp->index		= inIndex;
8651 	newResp->sock		= inSock;
8652 	SockAddrCopy( inDestAddr, &newResp->client );
8653 
8654 	if( !me->responseList || ( _TicksDiff( dueTicks, me->responseList->dueTicks ) < 0 ) )
8655 	{
8656 		dispatch_source_forget( &me->responseTimer );
8657 		err = DispatchTimerOneShotCreate( dispatch_time_milliseconds( me->responseDelayMs ), 0, me->queue,
8658 			_DNSServerSendDelayedResponses, me, &me->responseTimer );
8659 		require_noerr( err, exit );
8660 		dispatch_resume( me->responseTimer );
8661 	}
8662 	for( ptr = &me->responseList; ( resp = *ptr ) != NULL; ptr = &resp->next )
8663 	{
8664 		if( _TicksDiff( newResp->dueTicks, resp->dueTicks ) < 0 ) break;
8665 	}
8666 	newResp->next = resp;
8667 	*ptr = newResp;
8668 	newResp = NULL;
8669 	err = kNoErr;
8670 
8671 exit:
8672 	if( newResp ) _DNSServerDelayedResponseFree( newResp );
8673 	return( err );
8674 }
8675 
_DNSServerSendDelayedResponses(void * inContext)8676 static void	_DNSServerSendDelayedResponses( void *inContext )
8677 {
8678 	OSStatus						err;
8679 	const DNSServerRef				me = (DNSServerRef) inContext;
8680 	DNSServerDelayedResponse *		resp;
8681 	DNSServerDelayedResponse *		freeList;
8682 	int64_t							deltaTicks;
8683 
8684 	dispatch_source_forget( &me->responseTimer );
8685 
8686 	deltaTicks = -1;
8687 	freeList = NULL;
8688 	while( ( resp = me->responseList ) != NULL )
8689 	{
8690 		ssize_t			n;
8691 		uint64_t		nowTicks = UpTicks();
8692 
8693 		deltaTicks = _TicksDiff( resp->dueTicks, nowTicks );
8694 		if( deltaTicks > 0 ) break;
8695 		me->responseList = resp->next;
8696 
8697 		ds_ulog( kLogLevelInfo, "UDP: Sending %zu byte delayed response from %##a to %##a -- %.1{du:dnsmsg}\n",
8698 			resp->msgLen, &me->addrArray[ resp->index ], &resp->client, resp->msgPtr, resp->msgLen );
8699 
8700 		n = sendto( resp->sock, (char *) resp->msgPtr, resp->msgLen, 0, &resp->client.sa, SockAddrGetSize( &resp->client ) );
8701 		err = map_socket_value_errno( resp->sock, n == (ssize_t) resp->msgLen, n );
8702 		check_noerr( err );
8703 
8704 		resp->next = freeList;
8705 		freeList = resp;
8706 	}
8707 	if( deltaTicks > 0 )
8708 	{
8709 		uint64_t		deltaNs;
8710 
8711 		deltaNs = UpTicksToNanoseconds( (uint64_t) deltaTicks );
8712 		if( deltaNs > INT64_MAX ) deltaNs = INT64_MAX;
8713 
8714 		err = DispatchTimerOneShotCreate( dispatch_time( DISPATCH_TIME_NOW, (int64_t) deltaNs ), 0, me->queue,
8715 			_DNSServerSendDelayedResponses, me, &me->responseTimer );
8716 		require_noerr( err, exit );
8717 		dispatch_resume( me->responseTimer );
8718 	}
8719 
8720 exit:
8721 	if( freeList ) _DNSServerDelayedResponseListFree( freeList );
8722 }
8723 
8724 //===========================================================================================================================
8725 
_DNSServerDelayedResponseFree(DNSServerDelayedResponse * inResp)8726 static void	_DNSServerDelayedResponseFree( DNSServerDelayedResponse *inResp )
8727 {
8728 	ForgetMem( &inResp->msgPtr );
8729 	inResp->sock = kInvalidSocketRef;
8730 	free( inResp );
8731 }
8732 
8733 //===========================================================================================================================
8734 
_DNSServerDelayedResponseListFree(DNSServerDelayedResponse * inList)8735 static void	_DNSServerDelayedResponseListFree( DNSServerDelayedResponse *inList )
8736 {
8737 	DNSServerDelayedResponse *		resp;
8738 
8739 	while( ( resp = inList ) != NULL )
8740 	{
8741 		inList = resp->next;
8742 		_DNSServerDelayedResponseFree( resp );
8743 	}
8744 }
8745 
8746 //===========================================================================================================================
8747 
8748 #define kDNSServerConnectionExpirationTimeSecs		5
8749 #define kDNSServerConnectionExpirationTimeMs		( kDNSServerConnectionExpirationTimeSecs * kMillisecondsPerSecond )
8750 
_DNSServerTCPAcceptHandler(void * inContext)8751 static void	_DNSServerTCPAcceptHandler( void *inContext )
8752 {
8753 	OSStatus							err;
8754 	SocketContext * const				sockCtx	= (SocketContext *) inContext;
8755 	const DNSServerContext * const		ctx		= (DNSServerContext *) sockCtx->userContext;
8756 	const DNSServerRef 					me		= ctx->server;
8757 	DNSServerConnectionRef				cnx		= NULL;
8758 	sockaddr_ip							remote, local;
8759 	socklen_t							len;
8760 	SocketRef							sock;
8761 
8762 	len = (socklen_t) sizeof( remote );
8763 	sock = accept( sockCtx->sock, &remote.sa, &len );
8764 	err = map_socket_creation_errno( sock );
8765 	require_noerr( err, exit );
8766 
8767 	len = (socklen_t) sizeof( local );
8768 	err = getsockname( sock, &local.sa, &len );
8769 	if( unlikely( err ) ) SockAddrCopy( &me->addrArray[ ctx->index ], &local );
8770 
8771 	err = _DNSServerConnectionCreate( me, &local.sa, &remote.sa, ctx->index, &cnx );
8772 	require_noerr_quiet( err, exit );
8773 
8774 	err = _DNSServerConnectionStart( cnx, sock );
8775 	require_noerr( err, exit );
8776 	sock = kInvalidSocketRef;
8777 
8778 	if( !me->connectionList ) _DNSServerResetConnectionTimerMs( me, kDNSServerConnectionExpirationTimeMs );
8779 	cnx->next = me->connectionList;
8780 	me->connectionList = cnx;
8781 	cnx = NULL;
8782 
8783 exit:
8784 	ForgetSocket( &sock );
8785 	if( cnx )
8786 	{
8787 		_DNSServerConnectionStop( cnx, true );
8788 		CFRelease( cnx );
8789 	}
8790 }
8791 
8792 //===========================================================================================================================
8793 
_DNSServerConnectionTimerHandler(void * inContext)8794 static void	_DNSServerConnectionTimerHandler( void *inContext )
8795 {
8796 	const DNSServerRef				me			= (DNSServerRef) inContext;
8797 	DNSServerConnectionRef			cnx;
8798 	DNSServerConnectionRef *		ptr;
8799 	uint64_t						nowTicks;
8800 	int64_t							delta, deltaMin;
8801 
8802 	nowTicks = UpTicks();
8803 	deltaMin = INT64_MAX;
8804 	ptr = &me->connectionList;
8805 	while( ( cnx = *ptr ) != NULL )
8806 	{
8807 		delta = _TicksDiff( cnx->expirationTicks, nowTicks );
8808 		if( delta <= 0 )
8809 		{
8810 			ds_ulog( kLogLevelInfo, "Timing out TCP connection: %##a <-> %##a\n", &cnx->local, &cnx->remote );
8811 			*ptr = cnx->next;
8812 			cnx->next = NULL;
8813 			_DNSServerConnectionStop( cnx, false );
8814 			CFRelease( cnx );
8815 		}
8816 		else
8817 		{
8818 			if( delta < deltaMin ) deltaMin = delta;
8819 			ptr = &cnx->next;
8820 		}
8821 	}
8822 	if( me->connectionList )
8823 	{
8824 		const uint64_t		timeMs = UpTicksToMilliseconds( (uint64_t) deltaMin );
8825 
8826 		check( timeMs <= kDNSServerConnectionExpirationTimeMs );
8827 		_DNSServerResetConnectionTimerMs( me, timeMs + 1 );
8828 	}
8829 }
8830 
8831 //===========================================================================================================================
8832 
_DNSServerResetConnectionTimerMs(DNSServerRef me,uint64_t inTimeoutMs)8833 static void	_DNSServerResetConnectionTimerMs( DNSServerRef me, uint64_t inTimeoutMs )
8834 {
8835 	OSStatus		err;
8836 
8837 	dispatch_source_forget( &me->connectionTimer );
8838 	if( inTimeoutMs == 0 ) inTimeoutMs = 1;
8839 	err = DispatchTimerOneShotCreate( dispatch_time_milliseconds( inTimeoutMs ),
8840 		UINT64_C( 10 ) * kNanosecondsPerMillisecond, me->queue, _DNSServerConnectionTimerHandler, me, &me->connectionTimer );
8841 	if( likely( !err ) )	dispatch_resume( me->connectionTimer );
8842 	else					ds_ulog( kLogLevelError, "Failed to create connection timer: %#m\n", err );
8843 }
8844 
8845 //===========================================================================================================================
8846 
8847 static OSStatus
8848 	_DNSServerInitializeResponseMessage(
8849 		DataBuffer *	inDB,
8850 		uint16_t		inID,
8851 		uint16_t		inFlags,
8852 		const uint8_t *	inQName,
8853 		uint16_t		inQType,
8854 		uint16_t		inQClass );
8855 static OSStatus
8856 	_DNSServerAnswerQueryDynamically(
8857 		DNSServerRef	inServer,
8858 		const uint8_t *	inQName,
8859 		int				inQType,
8860 		int				inQClass,
8861 		size_t          inIndex,
8862 		Boolean			inForTCP,
8863 		Boolean			inDNSSEC,
8864 		DataBuffer *	inDB );
8865 
8866 static OSStatus
_DNSServerAnswerQuery(DNSServerRef me,const uint8_t * const inMsgPtr,const size_t inMsgLen,const size_t inIndex,const Boolean inForTCP,uint8_t ** const outResponsePtr,size_t * const outResponseLen)8867 	_DNSServerAnswerQuery(
8868 		DNSServerRef			me,
8869 		const uint8_t * const	inMsgPtr,
8870 		const size_t			inMsgLen,
8871 		const size_t			inIndex,
8872 		const Boolean			inForTCP,
8873 		uint8_t ** const		outResponsePtr,
8874 		size_t * const			outResponseLen )
8875 {
8876 	OSStatus				err;
8877 	DataBuffer				db;
8878 	const uint8_t *			ptr;
8879 	const DNSHeader *		hdr;
8880 	const uint8_t *			optPtr;
8881 	size_t					optLen;
8882 	unsigned int			qflags, rcode;
8883 	uint16_t				msgID, qtype, qclass, rflags;
8884 	uint8_t					qname[ kDomainNameLengthMax ];
8885 	uint8_t					dbBuf[ 512 ];
8886 	Boolean					dnssecOK;
8887 
8888 	DataBuffer_Init( &db, dbBuf, sizeof( dbBuf ), kDNSMaxTCPMessageSize );
8889 
8890 	require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kUnderrunErr );
8891 
8892 	hdr		= (const DNSHeader *) inMsgPtr;
8893 	qflags	= DNSHeaderGetFlags( hdr );
8894 
8895 	// Minimal checking of the query message's header.
8896 
8897 	require_action_quiet( !( qflags & kDNSHeaderFlag_Response ), exit, err = kRequestErr );
8898 	require_action_quiet( DNSFlagsGetOpCode( qflags ) == kDNSOpCode_Query, exit, err = kRequestErr );
8899 	require_action_quiet( DNSHeaderGetQuestionCount( hdr ) == 1, exit, err = kRequestErr );
8900 
8901 	ptr = (const uint8_t *) &hdr[ 1 ];
8902 	err = DNSMessageExtractQuestion( inMsgPtr, inMsgLen, ptr, qname, &qtype, &qclass, &ptr );
8903 	require_noerr( err, exit );
8904 
8905 	// Check if this query should be ignored because of its QTYPE.
8906 
8907 	if( qclass == kDNSClassType_IN )
8908 	{
8909 		size_t		i;
8910 
8911 		for( i = 0; i < me->ignoredQTypeCount; ++i )
8912 		{
8913 			if( qtype == me->ignoredQTypes[ i ] )
8914 			{
8915 				err = kSkipErr;
8916 				goto exit;
8917 			}
8918 		}
8919 	}
8920 	// Set up response flags.
8921 
8922 	rflags = kDNSHeaderFlag_Response;
8923 	if( qflags & kDNSHeaderFlag_RecursionDesired ) rflags |= kDNSHeaderFlag_RecursionDesired;
8924 	DNSFlagsSetOpCode( rflags, kDNSOpCode_Query );
8925 
8926 	// Get OPT record, if any.
8927 
8928 	err = DNSMessageGetOptRecord( inMsgPtr, inMsgLen, &optPtr, &optLen );
8929 	require_noerr_action_quiet( err, done, rcode = kDNSRCode_FormErr );
8930 
8931 	// Create a tentative response message.
8932 
8933 	msgID = DNSHeaderGetID( hdr );
8934 	if( me->badUDPMode && !inForTCP ) ++msgID;
8935 	err = _DNSServerInitializeResponseMessage( &db, msgID, rflags, qname, qtype, qclass );
8936 	require_noerr_action( err, done, rcode = kDNSRCode_ServFail );
8937 
8938 	// Complete the response message.
8939 
8940 	dnssecOK = false;
8941 	if( optPtr )
8942 	{
8943 		const dns_fixed_fields_opt *		opt = (const dns_fixed_fields_opt *) optPtr;
8944 
8945 		if( dns_fixed_fields_opt_get_extended_flags( opt ) & kDNSExtendedFlag_DNSSECOK ) dnssecOK = true;
8946 	}
8947 	err = _DNSServerAnswerQueryDynamically( me, qname, qtype, qclass, inIndex, inForTCP, dnssecOK, &db );
8948 	if( err == kSkipErr ) goto exit;
8949 	rcode = err ? kDNSRCode_ServFail : 0;
8950 
8951 	// Create an error response if there was a format error or a server failure.
8952 
8953 done:
8954 	if( rcode != 0 )
8955 	{
8956 		DNSFlagsSetRCode( rflags, rcode );
8957 		err = _DNSServerInitializeResponseMessage( &db, DNSHeaderGetID( hdr ), rflags, qname, qtype, qclass );
8958 		require_noerr( err, exit );
8959 	}
8960 	err = DataBuffer_Detach( &db, outResponsePtr, outResponseLen );
8961 	require_noerr( err, exit );
8962 
8963 exit:
8964 	DataBuffer_Free( &db );
8965 	return( err );
8966 }
8967 
8968 //===========================================================================================================================
8969 
8970 static OSStatus
_DNSServerInitializeResponseMessage(DataBuffer * inDB,uint16_t inID,uint16_t inFlags,const uint8_t * inQName,uint16_t inQType,uint16_t inQClass)8971 	_DNSServerInitializeResponseMessage(
8972 		DataBuffer *	inDB,
8973 		uint16_t		inID,
8974 		uint16_t		inFlags,
8975 		const uint8_t *	inQName,
8976 		uint16_t		inQType,
8977 		uint16_t		inQClass )
8978 {
8979 	OSStatus		err;
8980 	DNSHeader		header;
8981 
8982 	DataBuffer_Reset( inDB );
8983 
8984 	memset( &header, 0, sizeof( header ) );
8985 	DNSHeaderSetID( &header, inID );
8986 	DNSHeaderSetFlags( &header, inFlags );
8987 	DNSHeaderSetQuestionCount( &header, 1 );
8988 
8989 	err = DataBuffer_Append( inDB, &header, sizeof( header ) );
8990 	require_noerr( err, exit );
8991 
8992 	err = _DataBuffer_AppendDNSQuestion( inDB, inQName, DomainNameLength( inQName ), inQType, inQClass );
8993 	require_noerr( err, exit );
8994 
8995 exit:
8996 	return( err );
8997 }
8998 
8999 //===========================================================================================================================
9000 
9001 // DNS Server QNAME Labels
9002 
9003 #define kLabel_IPv4					"ipv4"
9004 #define kLabel_IPv6					"ipv6"
9005 #define kLabelPrefix_Alias			"alias"
9006 #define kLabelPrefix_AliasTTL		"alias-ttl"
9007 #define kLabelPrefix_Count			"count-"
9008 #define kLabelPrefix_Index			"index-"
9009 #define kLabelPrefix_RCode			"rcode-"
9010 #define kLabelPrefix_SRV			"srv-"
9011 #define kLabelPrefix_Tag			"tag-"
9012 #define kLabelPrefix_TTL			"ttl-"
9013 
9014 // Experimental Labels
9015 
9016 #define kLabelPrefix_PDelay			"pdelay-"		// Specifies an additional simulated processing delay in milliseconds.
9017 #define kLabelPrefix_Zone			"z-"			// format: z-<algorithm mnemonic exclude '-'>-<zone index>
9018 
9019 typedef struct
9020 {
9021 	uint16_t			priority;	// Priority from SRV label.
9022 	uint16_t			weight;		// Weight from SRV label.
9023 	uint16_t			port;		// Port number from SRV label.
9024 	uint16_t			targetLen;	// Total length of the target hostname labels that follow an SRV label.
9025 	const uint8_t *		targetPtr;	// Pointer to the target hostname embedded in a domain name.
9026 
9027 }	ParsedSRV;
9028 
9029 typedef uint32_t		DNSNameFlags;
9030 #define kDNSNameFlag_HasA			( 1U << 0 )
9031 #define kDNSNameFlag_HasAAAA		( 1U << 1 )
9032 #define kDNSNameFlag_HasSOA			( 1U << 2 )
9033 #define kDNSNameFlag_HasSRV			( 1U << 3 )
9034 #define kDNSNameFlag_HasPTRv4		( 1U << 4 )
9035 #define kDNSNameFlag_HasPTRv6		( 1U << 5 )
9036 #define kDNSNameFlag_HasRRSIG		( 1U << 6 )
9037 #define kDNSNameFlag_HasDNSKEY		( 1U << 7 )
9038 #define kDNSNameFlag_HasDS			( 1U << 8 )
9039 
9040 #define kAliasTTLCountMax		( ( kDomainLabelLengthMax - sizeof_string( kLabelPrefix_AliasTTL ) ) / 2 )
9041 #define kParsedSRVCountMax		( kDomainNameLengthMax / ( 1 + sizeof_string( kLabelPrefix_SRV ) + 5 ) )
9042 
9043 static Boolean
9044 	_DNSServerParseHostName(
9045 		DNSServerRef		inServer,
9046 		const uint8_t *		inQName,
9047 		uint32_t *			outAliasCount,
9048 		uint32_t			outAliasTTLs[ kAliasTTLCountMax ],
9049 		uint32_t *			outAliasTTLCount,
9050 		uint32_t *			outCount,
9051 		uint32_t *			outRandCount,
9052 		uint32_t *			outIndex,
9053 		int *				outRCode,
9054 		uint32_t *			outTTL,
9055 		uint32_t *			outProcDelayMs,
9056 		DNSNameFlags *		outFlags,
9057 		const uint8_t **	outZone,
9058 		const uint8_t **	outZoneParent,
9059 		DNSKeyInfoRef *		outZSK,
9060 		DNSKeyInfoRef *		outKSK,
9061 		DNSKeyInfoRef *		outParentZSK );
9062 static Boolean
9063 	_DNSServerParseSRVName(
9064 		DNSServerRef		inServer,
9065 		const uint8_t *		inName,
9066 		const uint8_t **	outDomainPtr,
9067 		size_t *			outDomainLen,
9068 		ParsedSRV			outSRVArray[ kParsedSRVCountMax ],
9069 		size_t *			outSRVCount );
9070 static Boolean	_DNSServerParseReverseIPv4Name( DNSServerRef me, const uint8_t *inQName, unsigned int *outHostID );
9071 static Boolean	_DNSServerParseReverseIPv6Name( DNSServerRef me, const uint8_t *inQName, unsigned int *outHostID );
9072 #if( DEBUG )
9073 static void
9074 	_DNSServerSigCheck(
9075 		const uint8_t *		inOwner,
9076 		int					inTypeCovered,
9077 		const void *		inMsgPtr,
9078 		size_t				inMsgLen,
9079 		const uint8_t *		inSignaturePtr,
9080 		const size_t		inSignatureLen,
9081 		DNSKeyInfoRef		inKeyInfo );
9082 #endif
9083 
9084 typedef enum
9085 {
9086 	kQueryStatus_Null			= 0,
9087 	kQueryStatus_OK				= 1,
9088 	kQueryStatus_Truncated		= 2,
9089 	kQueryStatus_NotImplemented	= 3,
9090 	kQueryStatus_Refused		= 4
9091 
9092 }	QueryStatus;
9093 
9094 static OSStatus
_DNSServerAnswerQueryDynamically(DNSServerRef me,const uint8_t * const inQName,const int inQType,const int inQClass,const size_t inIndex,const Boolean inForTCP,const Boolean inDNSSEC,DataBuffer * const inDB)9095 	_DNSServerAnswerQueryDynamically(
9096 		DNSServerRef			me,
9097 		const uint8_t * const	inQName,
9098 		const int				inQType,
9099 		const int				inQClass,
9100 		const size_t			inIndex,
9101 		const Boolean			inForTCP,
9102 		const Boolean			inDNSSEC,
9103 		DataBuffer * const		inDB )
9104 {
9105 	OSStatus			err;
9106 	uint32_t			aliasCount		= 0;
9107 	uint32_t			aliasTTLs[ kAliasTTLCountMax ];
9108 	uint32_t			aliasTTLCount	= 0;
9109 	uint32_t			addrCount		= 0;
9110 	uint32_t			randCount		= 0;
9111 	uint32_t			index			= 0;
9112 	int					rcodeOverride	= -1;
9113 	uint32_t			ttl				= 0;
9114 	uint32_t			procDelayMs		= 0;
9115 	DNSNameFlags		nameFlags		= 0;
9116 	const uint8_t *		zone			= NULL;
9117 	const uint8_t *		zoneParent		= NULL;
9118 	DNSKeyInfoRef		zsk				= NULL;
9119 	DNSKeyInfoRef		ksk				= NULL;
9120 	DNSKeyInfoRef		zskParent		= NULL;
9121 	const uint8_t *		srvDomainPtr	= NULL;
9122 	size_t				srvDomainLen	= 0;
9123 	ParsedSRV			srvArray[ kParsedSRVCountMax ];
9124 	size_t				srvCount		= 0;
9125 	unsigned int		hostID			= 0;
9126 	struct timeval		now;
9127 	DNSHeader *			hdr;
9128 	unsigned int		flags;
9129 	int					rcode;
9130 	QueryStatus			status;
9131 	unsigned int		answerCount		= 0;
9132 	unsigned int		additionalCount	= 0;
9133 	Boolean				nameExists		= false;
9134 	uint8_t *			qnameLower		= NULL;
9135 	size_t				qnameLowerLen;
9136 	const uint8_t *		ownerLower;
9137 	size_t				ownerLowerLen;
9138 	DataBuffer *		sigMsg			= NULL;
9139 	DataBuffer			sigDB;
9140 	uint8_t				sigBuf[ 256 ];
9141 	uint8_t				nameCPtr[ 2 ];
9142 	uint64_t			startTicks;
9143 
9144 	startTicks = UpTicks();
9145 	require_action_quiet( inQClass == kDNSServiceClass_IN, done, status = kQueryStatus_NotImplemented );
9146 
9147 	nameExists = _DNSServerParseHostName( me, inQName, &aliasCount, aliasTTLs, &aliasTTLCount, &addrCount, &randCount,
9148 		&index, &rcodeOverride, &ttl, &procDelayMs, &nameFlags, &zone, &zoneParent, &zsk, &ksk, &zskParent );
9149 	if( nameExists )
9150 	{
9151 		check( !( ( aliasCount > 0 ) && ( aliasTTLCount > 0 ) ) );
9152 		check( ( randCount == 0 ) || ( ( randCount >= addrCount ) && ( randCount <= 255 ) ) );
9153 		check( rcodeOverride <= 15 );
9154 		check( !( nameFlags & kDNSNameFlag_HasRRSIG ) || ( zsk && ksk && zskParent ) );
9155 
9156 		if( aliasTTLCount > 0 ) aliasCount = (uint32_t) aliasTTLCount;
9157 		if( index != 0 )
9158 		{
9159 			if( index == inIndex )
9160 			{
9161 				rcodeOverride = -1;
9162 			}
9163 			else
9164 			{
9165 				if ( rcodeOverride < 0 )
9166 				{
9167 					err = kSkipErr;
9168 					goto exit;
9169 				}
9170 				else
9171 				{
9172 					addrCount = 0;
9173 				}
9174 			}
9175 		}
9176 	}
9177 	else if( ( nameExists = _DNSServerParseSRVName( me, inQName, &srvDomainPtr, &srvDomainLen, srvArray, &srvCount ) ) )
9178 	{
9179 		nameFlags = kDNSNameFlag_HasSRV;
9180 	}
9181 	else if( ( nameExists = _DNSServerParseReverseIPv4Name( me, inQName, &hostID ) ) )
9182 	{
9183 		check( ( hostID >= 1 ) && ( hostID <= 255 ) );
9184 
9185 		nameFlags = kDNSNameFlag_HasPTRv4;
9186 	}
9187 	else if( ( nameExists = _DNSServerParseReverseIPv6Name( me, inQName, &hostID ) ) )
9188 	{
9189 		check( ( hostID >= 1 ) && ( hostID <= 255 ) );
9190 
9191 		nameFlags = kDNSNameFlag_HasPTRv6;
9192 	}
9193 	require_action_quiet( nameExists, done, status = kQueryStatus_OK );
9194 
9195 	err = DomainNameDupLower( inQName, &qnameLower, &qnameLowerLen );
9196 	require_noerr( err, exit );
9197 
9198 	gettimeofday( &now, NULL );
9199 	if( aliasCount > 0 )
9200 	{
9201 		size_t						nameOffset, rdataLabelLen;
9202 		const uint8_t *				parentLower;
9203 		size_t						parentLowerLen = 0;
9204 		uint32_t					i;
9205 		dns_fixed_fields_record		recFields;
9206 		uint8_t						rdataLabel[ 1 + kDomainLabelLengthMax ];
9207 		uint8_t						parentCPtr[ 2 ];
9208 		Boolean						needSig;
9209 
9210 		// If aliasCount is non-zero, then the first label of QNAME is either "alias" or "alias-<N>". parentCPtr is a
9211 		// name compression pointer to the second label of QNAME, i.e., the parent domain name of QNAME. It's used for
9212 		// the RDATA of CNAME records whose canonical name ends with the superdomain name. It may also be used to
9213 		// construct CNAME record names when the offset to the previous CNAME's RDATA doesn't fit in a compression
9214 		// pointer.
9215 
9216 		DNSMessageWriteLabelPointer( parentCPtr, kDNSHeaderLength + ( 1 + inQName[ 0 ] ) );
9217 
9218 		rdataLabel[ 0 ]	= 0;
9219 		rdataLabelLen	= 1;
9220 		nameOffset		= kDNSHeaderLength; // The name of the first CNAME record is equal to QNAME.
9221 
9222 		needSig = ( inDNSSEC && ( nameFlags & kDNSNameFlag_HasRRSIG ) ) ? true : false;
9223 		if( needSig )
9224 		{
9225 			parentLower		= DomainNameGetNextLabel( qnameLower );
9226 			parentLowerLen	= DomainNameLength( parentLower );
9227 		}
9228 		for( i = aliasCount; i >= 1; --i )
9229 		{
9230 			size_t				nameLabelLen, nameLen, rdataLen, recordLen;
9231 			uint32_t			aliasTTL;
9232 			uint8_t				nameLabel[ 1 + kDomainLabelLengthMax ];
9233 			Boolean				useNamePtr;
9234 			const Boolean		useAliasTTLs = ( aliasTTLCount > 0 ) ? true : false;
9235 
9236 			memcpy( nameLabel, rdataLabel, rdataLabelLen );
9237 			nameLabelLen = rdataLabelLen;
9238 			if( nameOffset <= kDNSCompressionOffsetMax )
9239 			{
9240 				DNSMessageWriteLabelPointer( nameCPtr, nameOffset );
9241 				nameLen		= sizeof( nameCPtr );
9242 				useNamePtr	= true;
9243 			}
9244 			else
9245 			{
9246 				nameLen		= nameLabelLen + sizeof( parentCPtr );
9247 				useNamePtr	= false;
9248 			}
9249 
9250 			if( i > 1 )
9251 			{
9252 				// There's at least one alias/CNAME left.
9253 
9254 				uint8_t *			dst = &rdataLabel[ 1 ];
9255 				const uint8_t *		lim = &rdataLabel[ countof( rdataLabel ) ];
9256 				size_t				maxLen;
9257 				int					n;
9258 
9259 				maxLen = (size_t)( lim - dst );
9260 				if( useAliasTTLs )
9261 				{
9262 					uint32_t		j;
9263 
9264 					n = MemPrintF( dst, maxLen, "%s", kLabelPrefix_AliasTTL );
9265 					require_fatal( ( n > 0 ) && ( ( (size_t) n ) <= maxLen ), "Failed to print AliasTTL label" );
9266 					dst += n;
9267 
9268 					for( j = aliasCount - ( i - 1 ); j < aliasCount; ++j )
9269 					{
9270 						maxLen = (size_t)( lim - dst );
9271 						n = MemPrintF( dst, maxLen, "-%u", aliasTTLs[ j ] );
9272 						require_fatal( ( n > 0 ) && ( ( (size_t) n ) <= maxLen ), "Failed to print AliasTTL label" );
9273 						dst += n;
9274 					}
9275 				}
9276 				else if( i == 2 )
9277 				{
9278 					n = MemPrintF( dst, maxLen, "%s", kLabelPrefix_Alias );
9279 					require_fatal( ( n > 0 ) && ( ( (size_t) n ) <= maxLen ), "Failed to print Alias label" );
9280 					dst += n;
9281 				}
9282 				else
9283 				{
9284 					n = MemPrintF( dst, maxLen, "%s-%u", kLabelPrefix_Alias, i - 1 );
9285 					require_fatal( ( n > 0 ) && ( ( (size_t) n ) <= maxLen ), "Failed to print Alias label" );
9286 					dst += n;
9287 				}
9288 				rdataLabel[ 0 ]	= (uint8_t)( dst - &rdataLabel[ 1 ] );
9289 				rdataLabelLen	= 1 + rdataLabel[ 0 ];
9290 				rdataLen		= rdataLabelLen + sizeof( parentCPtr );
9291 			}
9292 			else
9293 			{
9294 				// This is the final CNAME.
9295 
9296 				rdataLen = sizeof( parentCPtr );
9297 			}
9298 
9299 			// If the transport is UDP, make sure the message is within the UDP size limit.
9300 
9301 			if( !inForTCP )
9302 			{
9303 				recordLen = nameLen + sizeof( recFields ) + rdataLen;
9304 				if( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize )
9305 				{
9306 					status = kQueryStatus_Truncated;
9307 					goto done;
9308 				}
9309 			}
9310 			// Append CNAME record's NAME to response.
9311 
9312 			if( useNamePtr )
9313 			{
9314 				err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
9315 				require_noerr( err, exit );
9316 			}
9317 			else
9318 			{
9319 				err = DataBuffer_Append( inDB, nameLabel, nameLabelLen );
9320 				require_noerr( err, exit );
9321 
9322 				err = DataBuffer_Append( inDB, parentCPtr, sizeof( parentCPtr ) );
9323 				require_noerr( err, exit );
9324 			}
9325 			// Append CNAME record's TYPE, CLASS, TTL, and RDLENGTH to response.
9326 
9327 			aliasTTL = useAliasTTLs ? aliasTTLs[ aliasCount - i ] : me->defaultTTL;
9328 			dns_fixed_fields_record_init( &recFields, kDNSServiceType_CNAME, kDNSServiceClass_IN, aliasTTL,
9329 				(uint16_t) rdataLen );
9330 			err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
9331 			require_noerr( err, exit );
9332 
9333 			// Save offset of CNAME record's RDATA, which may be used for the name of the next CNAME record.
9334 
9335 			nameOffset = DataBuffer_GetLen( inDB );
9336 
9337 			// Append CNAME record's RDATA to response.
9338 
9339 			if( i > 1 )
9340 			{
9341 				// There's at least one CNAME left.
9342 
9343 				err = DataBuffer_Append( inDB, rdataLabel, rdataLabelLen );
9344 				require_noerr( err, exit );
9345 			}
9346 			err = DataBuffer_Append( inDB, parentCPtr, sizeof( parentCPtr ) );
9347 			require_noerr( err, exit );
9348 
9349 			++answerCount;
9350 
9351 			if( needSig )
9352 			{
9353 				dns_fixed_fields_rrsig		sigFields;
9354 				const size_t				signerLen = DomainNameLength( zone );
9355 				uint32_t					inceptionSecs;
9356 				uint8_t						signature[ kDNSServerSignatureLengthMax ];
9357 				size_t						signatureLen;
9358 				int							labelCount;
9359 				Boolean						didSign;
9360 
9361 				// Initialize signing buffer.
9362 
9363 				check( !sigMsg );
9364 				sigMsg = &sigDB;
9365 				DataBuffer_Init( sigMsg, sigBuf, sizeof( sigBuf ), SIZE_MAX );
9366 
9367 				// Append RRSIG record RDATA fixed fields to signing buffer.
9368 
9369 				memset( &sigFields, 0, sizeof( sigFields ) );
9370 				dns_fixed_fields_rrsig_set_type_covered( &sigFields, kDNSServiceType_CNAME );
9371 				dns_fixed_fields_rrsig_set_algorithm( &sigFields, DNSKeyInfoGetAlgorithm( zsk ) );
9372 				labelCount = DomainNameLabelCount( inQName );
9373 				check( labelCount >= 0 );
9374 				dns_fixed_fields_rrsig_set_labels( &sigFields, (uint8_t) labelCount );
9375 				dns_fixed_fields_rrsig_set_original_ttl( &sigFields, aliasTTL );
9376 				inceptionSecs = (uint32_t) now.tv_sec;
9377 				dns_fixed_fields_rrsig_set_signature_expiration( &sigFields, inceptionSecs + kSecondsPerDay );
9378 				dns_fixed_fields_rrsig_set_signature_inception( &sigFields, inceptionSecs );
9379 				dns_fixed_fields_rrsig_set_key_tag( &sigFields, DNSKeyInfoGetKeyTag( zsk ) );
9380 
9381 				err = DataBuffer_Append( sigMsg, &sigFields, sizeof( sigFields ) );
9382 				require_noerr( err, exit );
9383 
9384 				// Append RRSIG record RDATA signer to signing buffer.
9385 
9386 				err = DataBuffer_Append( sigMsg, zone, signerLen );
9387 				require_noerr( err, exit );
9388 
9389 				// Append expanded CNAME record owner to signing buffer.
9390 
9391 				if( i == aliasCount )
9392 				{
9393 					err = DataBuffer_Append( sigMsg, qnameLower, qnameLowerLen );
9394 					require_noerr( err, exit );
9395 				}
9396 				else
9397 				{
9398 					err = DataBuffer_Append( sigMsg, nameLabel, nameLabelLen );
9399 					require_noerr( err, exit );
9400 
9401 					err = DataBuffer_Append( sigMsg, parentLower, parentLowerLen );
9402 					require_noerr( err, exit );
9403 				}
9404 				// Append CNAME record fixed fields to signing buffer.
9405 
9406 				err = DataBuffer_Append( sigMsg, &recFields, sizeof( recFields ) );
9407 				require_noerr( err, exit );
9408 
9409 				// Append expanded CNAME record RDATA to signing buffer.
9410 
9411 				if( i > 1 )
9412 				{
9413 					// There's at least one CNAME left.
9414 
9415 					err = DataBuffer_Append( sigMsg, rdataLabel, rdataLabelLen );
9416 					require_noerr( err, exit );
9417 				}
9418 				err = DataBuffer_Append( sigMsg, parentLower, parentLowerLen );
9419 				require_noerr( err, exit );
9420 
9421 				// Compute signature with ZSK.
9422 
9423 				memset( signature, 0, sizeof( signature ) );
9424 				didSign = DNSKeyInfoSign( zsk, DataBuffer_GetPtr( sigMsg ), DataBuffer_GetLen( sigMsg ),
9425 					signature, &signatureLen );
9426 				require_quiet( didSign, exit );
9427 
9428 				#if( DEBUG )
9429 				{
9430 					const uint8_t *		tmpPtr;
9431 					uint8_t				tmpBuf[ kDomainNameLengthMax ];
9432 
9433 					if( i == aliasCount )
9434 					{
9435 						tmpPtr = inQName;
9436 					}
9437 					else
9438 					{
9439 						memcpy( tmpBuf, nameLabel, nameLabelLen );
9440 						memcpy( &tmpBuf[ nameLabelLen ], parentLower, parentLowerLen );
9441 						tmpPtr = tmpBuf;
9442 					}
9443 					_DNSServerSigCheck( tmpPtr, kDNSServiceType_CNAME, DataBuffer_GetPtr( sigMsg ),
9444 						DataBuffer_GetLen( sigMsg ), signature, signatureLen, zsk );
9445 				}
9446 				#endif
9447 				// If the transport is UDP, make sure the message is within the UDP size limit.
9448 
9449 				rdataLen	= sizeof( sigFields ) + signerLen + signatureLen;
9450 				recordLen	= nameLen + sizeof( recFields ) + rdataLen;
9451 				if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
9452 				{
9453 					status = kQueryStatus_Truncated;
9454 					goto done;
9455 				}
9456 				// Append RRSIG record NAME to response.
9457 
9458 				if( useNamePtr )
9459 				{
9460 					err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
9461 					require_noerr( err, exit );
9462 				}
9463 				else
9464 				{
9465 					err = DataBuffer_Append( inDB, nameLabel, nameLabelLen );
9466 					require_noerr( err, exit );
9467 
9468 					err = DataBuffer_Append( inDB, parentCPtr, sizeof( parentCPtr ) );
9469 					require_noerr( err, exit );
9470 				}
9471 				// Append RRSIG record TYPE, CLASS, TTL, and RDLENGTH to response.
9472 
9473 				dns_fixed_fields_record_init( &recFields, kDNSServiceType_RRSIG, kDNSServiceClass_IN, aliasTTL,
9474 					(uint16_t) rdataLen );
9475 				err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
9476 				require_noerr( err, exit );
9477 
9478 				// Append RRSIG record RDATA fixed fields and signer to response.
9479 
9480 				err = DataBuffer_Append( inDB, &sigFields, sizeof( sigFields ) );
9481 				require_noerr( err, exit );
9482 
9483 				err = DataBuffer_Append( inDB, zone, signerLen );
9484 				require_noerr( err, exit );
9485 
9486 				// Append RRSIG record RDATA signature to response.
9487 
9488 				err = DataBuffer_Append( inDB, signature, signatureLen );
9489 				require_noerr( err, exit );
9490 
9491 				++answerCount;
9492 				DataBuffer_Free( sigMsg );
9493 				sigMsg = NULL;
9494 			}
9495 		}
9496 		check_compile_time_code( sizeof( nameCPtr ) == sizeof( parentCPtr ) );
9497 		memcpy( nameCPtr, parentCPtr, sizeof( nameCPtr ) );
9498 
9499 		ownerLower		= DomainNameGetNextLabel( qnameLower );
9500 		ownerLowerLen	= DomainNameLength( ownerLower );
9501 	}
9502 	else
9503 	{
9504 		// There are no aliases, so initialize the name compression pointer to point to QNAME.
9505 
9506 		DNSMessageWriteLabelPointer( nameCPtr, kDNSHeaderLength );
9507 
9508 		ownerLower		= qnameLower;
9509 		ownerLowerLen	= qnameLowerLen;
9510 	}
9511 
9512 	if( ( inQType == kDNSServiceType_A ) || ( inQType == kDNSServiceType_AAAA ) )
9513 	{
9514 		dns_fixed_fields_record		recFields;
9515 		dns_fixed_fields_rrsig		sigFields;
9516 		uint8_t *					idPtr;					// Pointer to the host identifier portion of an IP address.
9517 		size_t						recordLen;				// Length of the entire record.
9518 		size_t						rdataLen;				// Length of record's RDATA.
9519 		size_t						signerLen = 0;
9520 		unsigned int				i;						// For-loop counter.
9521 		uint8_t						rdata[ 16 ];			// A buffer that's big enough for either A or AAAA RDATA.
9522 		uint8_t						randIntegers[ 255 ];	// Array for random integers in [1, 255].
9523 		Boolean						needSig;
9524 
9525 		if( inQType == kDNSServiceType_A )
9526 		{
9527 			uint32_t		baseAddr;
9528 
9529 			require_action_quiet( nameFlags & kDNSNameFlag_HasA, done, status = kQueryStatus_OK );
9530 
9531 			rdataLen = 4;
9532 			baseAddr = ( me->badUDPMode && !inForTCP ) ? kDNSServerBadBaseAddrV4 : kDNSServerBaseAddrV4;
9533 			WriteBig32( rdata, baseAddr );
9534 			idPtr = &rdata[ 3 ]; // The last octet is the host identifier since the IPv4 address block is /24.
9535 		}
9536 		else
9537 		{
9538 			const uint8_t		( *baseAddr )[ 16 ];
9539 
9540 			require_action_quiet( nameFlags & kDNSNameFlag_HasAAAA, done, status = kQueryStatus_OK );
9541 
9542 			rdataLen = 16;
9543 			baseAddr = ( me->badUDPMode && !inForTCP ) ? &kDNSServerBadBaseAddrV6 : &kDNSServerBaseAddrV6;
9544 			memcpy( rdata, baseAddr, rdataLen );
9545 			idPtr = &rdata[ 14 ]; // The last two octets are the host identifier since we allow up to 511 IPv6 addresses.
9546 		}
9547 
9548 		if( randCount > 0 )
9549 		{
9550 			// Populate the array with all integers between 1 and <randCount>, inclusive.
9551 
9552 			for( i = 0; i < randCount; ++i ) randIntegers[ i ] = (uint8_t)( i + 1 );
9553 
9554 			// Prevent dubious static analyzer warning.
9555 			// Note: _DNSServerParseHostName() already enforces randCount >= addrCount. Also, this require_fatal() check
9556 			// needs to be placed right before the next for-loop. Any earlier, and the static analyzer warning will persist
9557 			// for some reason.
9558 
9559 			require_fatal( addrCount <= randCount, "Invalid Count label values: addrCount %u > randCount %u",
9560 				addrCount, randCount );
9561 
9562 			// Create a contiguous subarray starting at index 0 that contains <addrCount> randomly chosen integers between
9563 			// 1 and <randCount>, inclusive.
9564 			// Loop invariant 1: Array elements with indexes in [0, i - 1] have been randomly chosen.
9565 			// Loop invariant 2: Array elements with indexes in [i, randCount - 1] are candidates for being chosen.
9566 
9567 			for( i = 0; i < addrCount; ++i )
9568 			{
9569 				uint8_t			tmp;
9570 				uint32_t		j;
9571 
9572 				j = RandomRange( i, randCount - 1 );
9573 				if( i != j )
9574 				{
9575 					tmp = randIntegers[ i ];
9576 					randIntegers[ i ] = randIntegers[ j ];
9577 					randIntegers[ j ] = tmp;
9578 				}
9579 			}
9580 		}
9581 		needSig = ( inDNSSEC && ( nameFlags & kDNSNameFlag_HasRRSIG ) ) ? true : false;
9582 		if( needSig )
9583 		{
9584 			uint32_t		inceptionSecs;
9585 			int				labelCount;
9586 
9587 			// Initialize signing buffer.
9588 
9589 			check( !sigMsg );
9590 			sigMsg = &sigDB;
9591 			DataBuffer_Init( sigMsg, sigBuf, sizeof( sigBuf ), SIZE_MAX );
9592 
9593 			// Append RRSIG record RDATA fixed fields to signing buffer.
9594 
9595 			memset( &sigFields, 0, sizeof( sigFields ) );
9596 			dns_fixed_fields_rrsig_set_type_covered( &sigFields, (uint16_t) inQType );
9597 			dns_fixed_fields_rrsig_set_algorithm( &sigFields, DNSKeyInfoGetAlgorithm( zsk ) );
9598 			labelCount = DomainNameLabelCount( ownerLower );
9599 			check( labelCount >= 0 );
9600 			dns_fixed_fields_rrsig_set_labels( &sigFields, (uint8_t) labelCount );
9601 			dns_fixed_fields_rrsig_set_original_ttl( &sigFields, ttl );
9602 			inceptionSecs = (uint32_t) now.tv_sec;
9603 			dns_fixed_fields_rrsig_set_signature_expiration( &sigFields, inceptionSecs + kSecondsPerDay );
9604 			dns_fixed_fields_rrsig_set_signature_inception( &sigFields, inceptionSecs );
9605 			dns_fixed_fields_rrsig_set_key_tag( &sigFields, DNSKeyInfoGetKeyTag( zsk ) );
9606 
9607 			err = DataBuffer_Append( sigMsg, &sigFields, sizeof( sigFields ) );
9608 			require_noerr( err, exit );
9609 
9610 			// Append RRSIG record RDATA signer to signing buffer.
9611 
9612 			signerLen = DomainNameLength( zone );
9613 			err = DataBuffer_Append( sigMsg, zone, signerLen );
9614 			require_noerr( err, exit );
9615 		}
9616 		recordLen = sizeof( nameCPtr ) + sizeof( recFields ) + rdataLen;
9617 		dns_fixed_fields_record_init( &recFields, (uint16_t) inQType, kDNSServiceClass_IN, ttl, (uint16_t) rdataLen );
9618 		for( i = 0; i < addrCount; ++i )
9619 		{
9620 			// If the transport is UDP, make sure the message is within the UDP size limit.
9621 
9622 			if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
9623 			{
9624 				status = kQueryStatus_Truncated;
9625 				goto done;
9626 			}
9627 			// Append A/AAAA record NAME to response.
9628 
9629 			err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
9630 			require_noerr( err, exit );
9631 
9632 			// Append A/AAAA record TYPE, CLASS, TTL, and RDLENGTH to response.
9633 
9634 			err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
9635 			require_noerr( err, exit );
9636 
9637 			// Append A/AAAA record RDATA to response.
9638 
9639 			hostID = ( randCount > 0 ) ? randIntegers[ i ] : ( i + 1 );
9640 			if( inQType == kDNSServiceType_A )
9641 			{
9642 				*idPtr = (uint8_t) hostID;
9643 			}
9644 			else
9645 			{
9646 				WriteBig16( idPtr, hostID );
9647 			}
9648 			err = DataBuffer_Append( inDB, rdata, rdataLen );
9649 			require_noerr( err, exit );
9650 
9651 			++answerCount;
9652 			if( needSig )
9653 			{
9654 				// Append A/AAAA record to signing buffer.
9655 
9656 				err = DataBuffer_Append( sigMsg, ownerLower, ownerLowerLen );
9657 				require_noerr( err, exit );
9658 
9659 				err = DataBuffer_Append( sigMsg, &recFields, sizeof( recFields ) );
9660 				require_noerr( err, exit );
9661 
9662 				err = DataBuffer_Append( sigMsg, rdata, rdataLen );
9663 				require_noerr( err, exit );
9664 			}
9665 		}
9666 		if( needSig )
9667 		{
9668 			uint8_t		signature[ kDNSServerSignatureLengthMax ];
9669 			size_t		signatureLen;
9670 			Boolean		didSign;
9671 
9672 			// Compute signature with ZSK.
9673 
9674 			memset( signature, 0, sizeof( signature ) );
9675 			didSign = DNSKeyInfoSign( zsk, DataBuffer_GetPtr( sigMsg ), DataBuffer_GetLen( sigMsg ),
9676 				signature, &signatureLen );
9677 			require_quiet( didSign, exit );
9678 
9679 		#if( DEBUG )
9680 			_DNSServerSigCheck( ownerLower, inQType, DataBuffer_GetPtr( sigMsg ), DataBuffer_GetLen( sigMsg ), signature,
9681 				signatureLen, zsk );
9682 		#endif
9683 			// If the transport is UDP, make sure the message is within the UDP size limit.
9684 
9685 			rdataLen	= sizeof( sigFields ) + signerLen + signatureLen;
9686 			recordLen	= sizeof( nameCPtr ) + sizeof( recFields ) + rdataLen;
9687 			if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
9688 			{
9689 				status = kQueryStatus_Truncated;
9690 				goto done;
9691 			}
9692 			// Append RRSIG record NAME to response.
9693 
9694 			err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
9695 			require_noerr( err, exit );
9696 
9697 			// Append RRSIG record TYPE, CLASS, TTL, and RDLENGTH to response.
9698 
9699 			dns_fixed_fields_record_init( &recFields, kDNSServiceType_RRSIG, kDNSServiceClass_IN, ttl, (uint16_t) rdataLen );
9700 			err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
9701 			require_noerr( err, exit );
9702 
9703 			// Append RRSIG record RDATA fixed fields and signer to response.
9704 
9705 			err = DataBuffer_Append( inDB, &sigFields, sizeof( sigFields ) );
9706 			require_noerr( err, exit );
9707 
9708 			err = DataBuffer_Append( inDB, zone, signerLen );
9709 			require_noerr( err, exit );
9710 
9711 			// Append RRSIG record RDATA signature to response.
9712 
9713 			err = DataBuffer_Append( inDB, signature, signatureLen );
9714 			require_noerr( err, exit );
9715 
9716 			++answerCount;
9717 			DataBuffer_Free( sigMsg );
9718 			sigMsg = NULL;
9719 		}
9720 	}
9721 	else if( inQType == kDNSServiceType_SRV )
9722 	{
9723 		dns_fixed_fields_record		recFields;
9724 		size_t						i;
9725 
9726 		require_action_quiet( nameFlags & kDNSNameFlag_HasSRV, done, status = kQueryStatus_OK );
9727 
9728 		dns_fixed_fields_record_init( &recFields, kDNSServiceType_SRV, kDNSServiceClass_IN, me->defaultTTL, 0 );
9729 		for( i = 0; i < srvCount; ++i )
9730 		{
9731 			dns_fixed_fields_srv		srvFields;
9732 			size_t						rdataLen;
9733 			size_t						recordLen;
9734 			const ParsedSRV * const		srv = &srvArray[ i ];
9735 
9736 			// If the transport is UDP, make sure the message is within the UDP size limit.
9737 
9738 			rdataLen  = sizeof( srvFields ) + srvDomainLen + srv->targetLen + 1;
9739 			recordLen = sizeof( nameCPtr ) + sizeof( recFields ) + rdataLen;
9740 			if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
9741 			{
9742 				status = kQueryStatus_Truncated;
9743 				goto done;
9744 			}
9745 			// Append record NAME to response.
9746 
9747 			err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
9748 			require_noerr( err, exit );
9749 
9750 			// Append record TYPE, CLASS, TTL, and RDLENGTH to response.
9751 
9752 			dns_fixed_fields_record_set_rdlength( &recFields, (uint16_t) rdataLen );
9753 			err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
9754 			require_noerr( err, exit );
9755 
9756 			// Append SRV RDATA priority, weight, and port to response.
9757 
9758 			dns_fixed_fields_srv_init( &srvFields, srv->priority, srv->weight, srv->port );
9759 			err = DataBuffer_Append( inDB, &srvFields, sizeof( srvFields ) );
9760 			require_noerr( err, exit );
9761 
9762 			// Append SRV RDATA target non-root labels to response.
9763 
9764 			if( srv->targetLen > 0 )
9765 			{
9766 				err = DataBuffer_Append( inDB, srv->targetPtr, srv->targetLen );
9767 				require_noerr( err, exit );
9768 			}
9769 
9770 			if( srvDomainLen > 0 )
9771 			{
9772 				err = DataBuffer_Append( inDB, srvDomainPtr, srvDomainLen );
9773 				require_noerr( err, exit );
9774 			}
9775 
9776 			// Append SRV RDATA target root label to response.
9777 
9778 			err = DataBuffer_Append( inDB, "", 1 );
9779 			require_noerr( err, exit );
9780 
9781 			++answerCount;
9782 		}
9783 	}
9784 	else if( inQType == kDNSServiceType_SOA )
9785 	{
9786 		size_t		nameLen, recordLen;
9787 
9788 		require_action_quiet( nameFlags & kDNSNameFlag_HasSOA, done, status = kQueryStatus_OK );
9789 
9790 		nameLen	= DomainNameLength( me->domain );
9791 		if( !inForTCP )
9792 		{
9793 			err = AppendSOARecord( NULL, me->domain, nameLen, 0, 0, 0, kRootLabel, kRootLabel, 0, 0, 0, 0, 0, &recordLen );
9794 			require_noerr( err, exit );
9795 
9796 			// If the transport is UDP, make sure the message is within the UDP size limit.
9797 
9798 			if( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize )
9799 			{
9800 				status = kQueryStatus_Truncated;
9801 				goto done;
9802 			}
9803 		}
9804 
9805 		err = AppendSOARecord( inDB, me->domain, nameLen, kDNSServiceType_SOA, kDNSServiceClass_IN, me->defaultTTL,
9806 			kRootLabel, kRootLabel, me->serial, 1 * kSecondsPerDay, 2 * kSecondsPerHour, 1000 * kSecondsPerHour,
9807 			me->defaultTTL, NULL );
9808 		require_noerr( err, exit );
9809 
9810 		++answerCount;
9811 	}
9812 	else if( inQType == kDNSServiceType_PTR )
9813 	{
9814 		dns_fixed_fields_record		recFields;
9815 		size_t						domainLen, rdataLen, recordLen;
9816 		uint8_t						label[ 1 + kDomainLabelLengthMax ];
9817 		uint8_t *					dst = &label[ 1 ];
9818 		const uint8_t *				lim = &label[ countof( label ) ];
9819 
9820 		if( nameFlags & kDNSNameFlag_HasPTRv4 )
9821 		{
9822 			size_t				maxLen;
9823 			int					n;
9824 			const uint32_t		ipv4Addr = kDNSServerBaseAddrV4 + hostID;
9825 
9826 			maxLen = (size_t)( lim - dst );
9827 			n = MemPrintF( dst, maxLen, "ipv4-%u-%u-%u-%u",
9828 				( ipv4Addr >> 24 ) & 0xFFU,
9829 				( ipv4Addr >> 16 ) & 0xFFU,
9830 				( ipv4Addr >>  8 ) & 0xFFU,
9831 				  ipv4Addr         & 0xFFU );
9832 			require_fatal( ( n > 0 ) && ( ( (size_t) n ) <= maxLen ), "Failed to print reverse IPv4 hostname label" );
9833 			dst += n;
9834 		}
9835 		else if( nameFlags & kDNSNameFlag_HasPTRv6 )
9836 		{
9837 			size_t		maxLen;
9838 			int			n, i;
9839 			uint8_t		ipv6Addr[ 16 ];
9840 
9841 			maxLen = (size_t)( lim - dst );
9842 			n = MemPrintF( dst, maxLen, "ipv6" );
9843 			require_fatal( ( n > 0 ) && ( ( (size_t) n ) <= maxLen ), "Failed to print reverse IPv6 hostname label" );
9844 			dst += n;
9845 
9846 			memcpy( ipv6Addr, kDNSServerBaseAddrV6, 16 );
9847 			ipv6Addr[ 15 ] = (uint8_t) hostID;
9848 			for( i = 0; i < 8; ++i )
9849 			{
9850 				maxLen = (size_t)( lim - dst );
9851 				n = MemPrintF( dst, maxLen, "-%04x", ReadBig16( &ipv6Addr[ i * 2 ] ) );
9852 				require_fatal( ( n > 0 ) && ( ( (size_t) n ) <= maxLen ), "Failed to print reverse IPv6 hostname label" );
9853 				dst += n;
9854 			}
9855 		}
9856 		else
9857 		{
9858 			status = kQueryStatus_OK;
9859 			goto done;
9860 		}
9861 		label[ 0 ] = (uint8_t)( dst - &label[ 1 ] );
9862 
9863 		// If the transport is UDP, make sure the message is within the UDP size limit.
9864 
9865 		domainLen	= DomainNameLength( me->domain );
9866 		rdataLen	= 1 + label[ 0 ] + domainLen;
9867 		recordLen	= sizeof( nameCPtr ) + sizeof( recFields ) + rdataLen;
9868 		if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
9869 		{
9870 			status = kQueryStatus_Truncated;
9871 			goto done;
9872 		}
9873 
9874 		// Append PTR record NAME to response.
9875 
9876 		err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
9877 		require_noerr( err, exit );
9878 
9879 		// Append PTR record TYPE, CLASS, TTL, and RDLENGTH to response.
9880 
9881 		dns_fixed_fields_record_init( &recFields, kDNSServiceType_PTR, kDNSServiceClass_IN, me->defaultTTL,
9882 			(uint16_t) rdataLen );
9883 		err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
9884 		require_noerr( err, exit );
9885 
9886 		// Append PTR record RDATA to response.
9887 
9888 		err = DataBuffer_Append( inDB, label, 1 + label[ 0 ] );
9889 		require_noerr( err, exit );
9890 
9891 		err = DataBuffer_Append( inDB, me->domain, domainLen );
9892 		require_noerr( err, exit );
9893 
9894 		++answerCount;
9895 	}
9896 	else if( inQType == kDNSServiceType_DNSKEY )
9897 	{
9898 		size_t						recordLen;
9899 		size_t						signerLen = 0;
9900 		dns_fixed_fields_record		recFields;
9901 		dns_fixed_fields_rrsig		sigFields;
9902 		Boolean						needSig;
9903 
9904 		require_action_quiet( nameFlags & kDNSNameFlag_HasDNSKEY, done, status = kQueryStatus_OK );
9905 
9906 		// If the transport is UDP, make sure the message is within the UDP size limit.
9907 
9908 		recordLen = sizeof( nameCPtr ) + sizeof( recFields ) + DNSKeyInfoGetRDataLen( zsk );
9909 		if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
9910 		{
9911 			status = kQueryStatus_Truncated;
9912 			goto done;
9913 		}
9914 		// Append ZSK DNSKEY record NAME to response.
9915 
9916 		err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
9917 		require_noerr( err, exit );
9918 
9919 		// Append ZSK DNSKEY record TYPE, CLASS, TTL, and RDLENGTH to response.
9920 
9921 		dns_fixed_fields_record_init( &recFields, kDNSServiceType_DNSKEY, kDNSServiceClass_IN, ttl,
9922 			DNSKeyInfoGetRDataLen( zsk ) );
9923 		err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
9924 		require_noerr( err, exit );
9925 
9926 		// Append ZSK DNSKEY record RDATA to response.
9927 
9928 		err = DataBuffer_Append( inDB, DNSKeyInfoGetRDataPtr( zsk ), DNSKeyInfoGetRDataLen( zsk ) );
9929 		require_noerr( err, exit );
9930 
9931 		++answerCount;
9932 
9933 		needSig = ( inDNSSEC && ( nameFlags & kDNSNameFlag_HasRRSIG ) ) ? true : false;
9934 		if( needSig )
9935 		{
9936 			uint32_t		inceptionSecs;
9937 			int				labelCount;
9938 
9939 			// Initialize signing buffer.
9940 
9941 			check( !sigMsg );
9942 			sigMsg = &sigDB;
9943 			DataBuffer_Init( sigMsg, sigBuf, sizeof( sigBuf ), SIZE_MAX );
9944 
9945 			// Append RRSIG record RDATA fixed fields to signing buffer.
9946 
9947 			memset( &sigFields, 0, sizeof( sigFields ) );
9948 			dns_fixed_fields_rrsig_set_type_covered( &sigFields, kDNSServiceType_DNSKEY );
9949 			dns_fixed_fields_rrsig_set_algorithm( &sigFields, DNSKeyInfoGetAlgorithm( ksk ) );
9950 			labelCount = DomainNameLabelCount( ownerLower );
9951 			check( labelCount >= 0 );
9952 			dns_fixed_fields_rrsig_set_labels( &sigFields, (uint8_t) labelCount );
9953 			dns_fixed_fields_rrsig_set_original_ttl( &sigFields, ttl );
9954 			inceptionSecs = (uint32_t) now.tv_sec;
9955 			dns_fixed_fields_rrsig_set_signature_expiration( &sigFields, inceptionSecs + kSecondsPerDay );
9956 			dns_fixed_fields_rrsig_set_signature_inception( &sigFields, inceptionSecs );
9957 			dns_fixed_fields_rrsig_set_key_tag( &sigFields, DNSKeyInfoGetKeyTag( ksk ) );
9958 
9959 			err = DataBuffer_Append( sigMsg, &sigFields, sizeof( sigFields ) );
9960 			require_noerr( err, exit );
9961 
9962 			// Append RRSIG record RDATA signer to signing buffer.
9963 
9964 			signerLen = DomainNameLength( zone );
9965 			err = DataBuffer_Append( sigMsg, zone, signerLen );
9966 			require_noerr( err, exit );
9967 
9968 			// Append ZSK DNSKEY record to signing buffer.
9969 
9970 			err = DataBuffer_Append( sigMsg, ownerLower, ownerLowerLen );
9971 			require_noerr( err, exit );
9972 
9973 			err = DataBuffer_Append( sigMsg, &recFields, sizeof( recFields ) );
9974 			require_noerr( err, exit );
9975 
9976 			err = DataBuffer_Append( sigMsg, DNSKeyInfoGetRDataPtr( zsk ), DNSKeyInfoGetRDataLen( zsk ) );
9977 			require_noerr( err, exit );
9978 		}
9979 		// If the transport is UDP, make sure the message is within the UDP size limit.
9980 
9981 		recordLen = sizeof( nameCPtr ) + sizeof( recFields ) + DNSKeyInfoGetRDataLen( ksk );
9982 		if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
9983 		{
9984 			status = kQueryStatus_Truncated;
9985 			goto done;
9986 		}
9987 		// Append KSK DNSKEY record NAME to response.
9988 
9989 		err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
9990 		require_noerr( err, exit );
9991 
9992 		// Append KSK DNSKEY record TYPE, CLASS, TTL, and RDLENGTH to response.
9993 
9994 		err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
9995 		require_noerr( err, exit );
9996 
9997 		// Append KSK DNSKEY record RDATA to response.
9998 
9999 		err = DataBuffer_Append( inDB, DNSKeyInfoGetRDataPtr( ksk ), DNSKeyInfoGetRDataLen( ksk ) );
10000 		require_noerr( err, exit );
10001 
10002 		++answerCount;
10003 		if( needSig )
10004 		{
10005 			size_t		rdataLen;
10006 			uint8_t		signature[ kDNSServerSignatureLengthMax ];
10007 			size_t		signatureLen;
10008 			Boolean		didSign;
10009 
10010 			// Append KSK DNSKEY record to signing buffer.
10011 
10012 			err = DataBuffer_Append( sigMsg, ownerLower, ownerLowerLen );
10013 			require_noerr( err, exit );
10014 
10015 			err = DataBuffer_Append( sigMsg, &recFields, sizeof( recFields ) );
10016 			require_noerr( err, exit );
10017 
10018 			err = DataBuffer_Append( sigMsg, DNSKeyInfoGetRDataPtr( ksk ), DNSKeyInfoGetRDataLen( ksk ) );
10019 			require_noerr( err, exit );
10020 
10021 			// Compute signature with KSK.
10022 
10023 			memset( signature, 0, sizeof( signature ) );
10024 			didSign = DNSKeyInfoSign( ksk, DataBuffer_GetPtr( sigMsg ), DataBuffer_GetLen( sigMsg ),
10025 				signature, &signatureLen );
10026 			require_quiet( didSign, exit );
10027 
10028 		#if( DEBUG )
10029 			_DNSServerSigCheck( ownerLower, inQType, DataBuffer_GetPtr( sigMsg ), DataBuffer_GetLen( sigMsg ), signature,
10030 				signatureLen, ksk );
10031 		#endif
10032 			// If the transport is UDP, make sure the message is within the UDP size limit.
10033 
10034 			rdataLen	= sizeof( sigFields ) + signerLen + signatureLen;
10035 			recordLen	= sizeof( nameCPtr ) + sizeof( recFields ) + rdataLen;
10036 			if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
10037 			{
10038 				status = kQueryStatus_Truncated;
10039 				goto done;
10040 			}
10041 			// Append RRSIG record NAME to response.
10042 
10043 			err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
10044 			require_noerr( err, exit );
10045 
10046 			// Append RRSIG record TYPE, CLASS, TTL, and RDLENGTH to response.
10047 
10048 			dns_fixed_fields_record_init( &recFields, kDNSServiceType_RRSIG, kDNSServiceClass_IN, ttl, (uint16_t) rdataLen );
10049 			err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
10050 			require_noerr( err, exit );
10051 
10052 			// Append RRSIG record RDATA fixed fields and signer to response.
10053 
10054 			err = DataBuffer_Append( inDB, &sigFields, sizeof( sigFields ) );
10055 			require_noerr( err, exit );
10056 
10057 			err = DataBuffer_Append( inDB, zone, signerLen );
10058 			require_noerr( err, exit );
10059 
10060 			// Append RRSIG record RDATA signature to response.
10061 
10062 			err = DataBuffer_Append( inDB, signature, signatureLen );
10063 			require_noerr( err, exit );
10064 
10065 			++answerCount;
10066 			DataBuffer_Free( sigMsg );
10067 			sigMsg = NULL;
10068 		}
10069 	}
10070 	else if( inQType == kDNSServiceType_DS )
10071 	{
10072 		SHA256_CTX					ctx;
10073 		size_t						rdataLen, i, recordLen;
10074 		size_t						signerLen = 0;
10075 		dns_ds_sha256 *				dsPtr;
10076 		dns_ds_sha256 *				dsPtrs[ 2 ];
10077 		int							cmp;
10078 		dns_ds_sha256				dsZSK, dsKSK;
10079 		dns_fixed_fields_rrsig		sigFields;
10080 		dns_fixed_fields_record		recFields;
10081 		Boolean						needSig;
10082 
10083 		check_compile_time_code( sizeof( dsPtr->digest ) == SHA256_DIGEST_LENGTH );
10084 
10085 		require_action_quiet( nameFlags & kDNSNameFlag_HasDS, done, status = kQueryStatus_OK );
10086 
10087 		// Set up ZSK DS RDATA.
10088 
10089 		dsPtr = &dsZSK;
10090 		memset( dsPtr, 0, sizeof( *dsPtr ) );
10091 		dns_ds_sha256_set_key_tag( dsPtr, DNSKeyInfoGetKeyTag( zsk ) );
10092 		dns_ds_sha256_set_algorithm( dsPtr, DNSKeyInfoGetAlgorithm( zsk ) );
10093 		dns_ds_sha256_set_digest_type( dsPtr, kDSDigestType_SHA256 );
10094 
10095 		SHA256_Init( &ctx );
10096 		SHA256_Update( &ctx, ownerLower, ownerLowerLen );
10097 		SHA256_Update( &ctx, DNSKeyInfoGetRDataPtr( zsk ), DNSKeyInfoGetRDataLen( zsk ) );
10098 		SHA256_Final( dsPtr->digest, &ctx );
10099 
10100 		// Set up KSK DS RDATA.
10101 
10102 		dsPtr = &dsKSK;
10103 		memset( dsPtr, 0, sizeof( *dsPtr ) );
10104 		dns_ds_sha256_set_key_tag( dsPtr, DNSKeyInfoGetKeyTag( ksk ) );
10105 		dns_ds_sha256_set_algorithm( dsPtr, DNSKeyInfoGetAlgorithm( ksk ) );
10106 		dns_ds_sha256_set_digest_type( dsPtr, kDSDigestType_SHA256 );
10107 
10108 		SHA256_Init( &ctx );
10109 		SHA256_Update( &ctx, ownerLower, ownerLowerLen );
10110 		SHA256_Update( &ctx, DNSKeyInfoGetRDataPtr( ksk ), DNSKeyInfoGetRDataLen( ksk ) );
10111 		SHA256_Final( dsPtr->digest, &ctx );
10112 
10113 		// Order the DS RDATAs
10114 
10115 		cmp = memcmp( &dsZSK, &dsKSK, sizeof( dns_ds_sha256 ) );
10116 		if( cmp <= 0 )
10117 		{
10118 			dsPtrs[ 0 ] = &dsZSK;
10119 			dsPtrs[ 1 ] = ( cmp == 0 ) ? NULL : &dsKSK;
10120 		}
10121 		else
10122 		{
10123 			dsPtrs[ 0 ] = &dsKSK;
10124 			dsPtrs[ 1 ] = &dsZSK;
10125 		}
10126 		needSig = ( inDNSSEC && ( nameFlags & kDNSNameFlag_HasRRSIG ) ) ? true : false;
10127 		if( needSig )
10128 		{
10129 			uint32_t		inceptionSecs;
10130 			int				labelCount;
10131 
10132 			// Initialize signing buffer.
10133 
10134 			check( !sigMsg );
10135 			sigMsg = &sigDB;
10136 			DataBuffer_Init( sigMsg, sigBuf, sizeof( sigBuf ), SIZE_MAX );
10137 
10138 			// Append RRSIG record RDATA fixed fields to signing buffer.
10139 
10140 			memset( &sigFields, 0, sizeof( sigFields ) );
10141 			dns_fixed_fields_rrsig_set_type_covered( &sigFields, kDNSServiceType_DS );
10142 			dns_fixed_fields_rrsig_set_algorithm( &sigFields, DNSKeyInfoGetAlgorithm( zskParent ) );
10143 			labelCount = DomainNameLabelCount( ownerLower );
10144 			check( labelCount >= 0 );
10145 			dns_fixed_fields_rrsig_set_labels( &sigFields, (uint8_t) labelCount );
10146 			dns_fixed_fields_rrsig_set_original_ttl( &sigFields, ttl );
10147 			inceptionSecs = (uint32_t) now.tv_sec;
10148 			dns_fixed_fields_rrsig_set_signature_expiration( &sigFields, inceptionSecs + kSecondsPerDay );
10149 			dns_fixed_fields_rrsig_set_signature_inception( &sigFields, inceptionSecs );
10150 			dns_fixed_fields_rrsig_set_key_tag( &sigFields, DNSKeyInfoGetKeyTag( zskParent ) );
10151 
10152 			err = DataBuffer_Append( sigMsg, &sigFields, sizeof( sigFields ) );
10153 			require_noerr( err, exit );
10154 
10155 			// Append RRSIG record RDATA signer to signing buffer.
10156 
10157 			signerLen = DomainNameLength( zoneParent );
10158 			err = DataBuffer_Append( sigMsg, zoneParent, signerLen );
10159 			require_noerr( err, exit );
10160 		}
10161 		rdataLen = sizeof( dns_ds_sha256 );
10162 		dns_fixed_fields_record_init( &recFields, kDNSServiceType_DS, kDNSServiceClass_IN, ttl, (uint16_t) rdataLen );
10163 		for( i = 0; i < countof( dsPtrs ); ++i )
10164 		{
10165 			dsPtr = dsPtrs[ i ];
10166 			if( !dsPtr ) continue;
10167 
10168 			// If the transport is UDP, make sure the message is within the UDP size limit.
10169 
10170 			recordLen = sizeof( nameCPtr ) + sizeof( recFields ) + rdataLen;
10171 			if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
10172 			{
10173 				status = kQueryStatus_Truncated;
10174 				goto done;
10175 			}
10176 			// Append DS record NAME to response.
10177 
10178 			err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
10179 			require_noerr( err, exit );
10180 
10181 			// Append DS record TYPE, CLASS, TTL, and RDLENGTH to response.
10182 			err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
10183 			require_noerr( err, exit );
10184 
10185 			// Append DS record RDATA to response.
10186 
10187 			err = DataBuffer_Append( inDB, dsPtr, sizeof( *dsPtr ) );
10188 			require_noerr( err, exit );
10189 
10190 			++answerCount;
10191 			if( needSig )
10192 			{
10193 				// Append DS record to signing buffer.
10194 
10195 				err = DataBuffer_Append( sigMsg, ownerLower, ownerLowerLen );
10196 				require_noerr( err, exit );
10197 
10198 				err = DataBuffer_Append( sigMsg, &recFields, sizeof( recFields ) );
10199 				require_noerr( err, exit );
10200 
10201 				err = DataBuffer_Append( sigMsg, dsPtr, sizeof( *dsPtr ) );
10202 				require_noerr( err, exit );
10203 			}
10204 		}
10205 		if( needSig )
10206 		{
10207 			uint8_t		signature[ kDNSServerSignatureLengthMax ];
10208 			size_t		signatureLen;
10209 			Boolean		didSign;
10210 
10211 			// Compute signature with parent ZSK.
10212 
10213 			memset( signature, 0, sizeof( signature ) );
10214 			didSign = DNSKeyInfoSign( zskParent, DataBuffer_GetPtr( sigMsg ), DataBuffer_GetLen( sigMsg ),
10215 				signature, &signatureLen );
10216 			require_quiet( didSign, exit );
10217 
10218 		#if( DEBUG )
10219 			_DNSServerSigCheck( ownerLower, inQType, DataBuffer_GetPtr( sigMsg ), DataBuffer_GetLen( sigMsg ), signature,
10220 				signatureLen, zskParent );
10221 		#endif
10222 			// If the transport is UDP, make sure the message is within the UDP size limit.
10223 
10224 			rdataLen	= sizeof( sigFields ) + signerLen + signatureLen;
10225 			recordLen	= sizeof( nameCPtr ) + sizeof( recFields ) + rdataLen;
10226 			if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) )
10227 			{
10228 				status = kQueryStatus_Truncated;
10229 				goto done;
10230 			}
10231 			// Append RRSIG record NAME to response.
10232 
10233 			err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
10234 			require_noerr( err, exit );
10235 
10236 			// Append RRSIG record TYPE, CLASS, TTL, and RDLENGTH to response.
10237 
10238 			dns_fixed_fields_record_init( &recFields, kDNSServiceType_RRSIG, kDNSServiceClass_IN, ttl, (uint16_t) rdataLen );
10239 			err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
10240 			require_noerr( err, exit );
10241 
10242 			// Append RRSIG record RDATA fixed fields and signer to response.
10243 
10244 			err = DataBuffer_Append( inDB, &sigFields, sizeof( sigFields ) );
10245 			require_noerr( err, exit );
10246 
10247 			err = DataBuffer_Append( inDB, zoneParent, signerLen );
10248 			require_noerr( err, exit );
10249 
10250 			// Append RRSIG record RDATA signature to response.
10251 
10252 			err = DataBuffer_Append( inDB, signature, signatureLen );
10253 			require_noerr( err, exit );
10254 
10255 			++answerCount;
10256 
10257 			DataBuffer_Free( sigMsg );
10258 			sigMsg = NULL;
10259 		}
10260 	}
10261 	status = kQueryStatus_OK;
10262 
10263 done:
10264 	hdr = (DNSHeader *) DataBuffer_GetPtr( inDB );
10265 	flags = DNSHeaderGetFlags( hdr );
10266 	switch( status )
10267 	{
10268 		case kQueryStatus_OK:
10269 		case kQueryStatus_Truncated:
10270 			flags |= kDNSHeaderFlag_AuthAnswer;
10271 			if( status == kQueryStatus_Truncated ) flags |= kDNSHeaderFlag_Truncation;
10272 			rcode = nameExists ? kDNSRCode_NoError : kDNSRCode_NXDomain;
10273 			break;
10274 
10275 		case kQueryStatus_NotImplemented:
10276 			rcode = kDNSRCode_NotImp;
10277 			break;
10278 
10279 		case kQueryStatus_Refused:
10280 			rcode = kDNSRCode_Refused;
10281 			break;
10282 
10283 		case kQueryStatus_Null:
10284 		default:
10285 			err = kInternalErr;
10286 			goto exit;
10287 	}
10288 	if( rcodeOverride >= 0 )
10289 	{
10290 		dns_fixed_fields_record		recFields;
10291 		const char *				rcodeStr;
10292 		size_t						maxLen, txtStrLen, rdataLen, recordLen;
10293 		int							n;
10294 		uint8_t						rdataBuf[ 1 + 255 ]; // Enough space for one TXT record string.
10295 
10296 		// Create the RDATA for an informational TXT record that contains the original rcode to put in the Additional
10297 		// section.
10298 
10299 		maxLen = sizeof( rdataBuf ) - 1;
10300 		rcodeStr = DNSRCodeToString( rcode );
10301 		if( rcodeStr )	n = MemPrintF( &rdataBuf[ 1 ], maxLen, "original-rcode=%s", rcodeStr );
10302 		else			n = MemPrintF( &rdataBuf[ 1 ], maxLen, "original-rcode=%d", rcode );
10303 		txtStrLen = ( n > 0 ) ? Min( (size_t) n, maxLen ) : 0;
10304 		rdataBuf[ 0 ] = (uint8_t) txtStrLen;
10305 		rdataLen = 1 + txtStrLen;
10306 
10307 		// The TXT record isn't strictly necessary, so only include it if it fits.
10308 
10309 		recordLen = sizeof( nameCPtr ) + sizeof( recFields ) + rdataLen;
10310 		maxLen = inForTCP ? kDNSMaxTCPMessageSize : kDNSMaxUDPMessageSize;
10311 		if( ( DataBuffer_GetLen( inDB ) < maxLen ) && ( ( maxLen - DataBuffer_GetLen( inDB ) ) >= recordLen ) )
10312 		{
10313 			// Append TXT record NAME to response.
10314 
10315 			err = DataBuffer_Append( inDB, nameCPtr, sizeof( nameCPtr ) );
10316 			require_noerr( err, exit );
10317 
10318 			// Append TXT record TYPE, CLASS, TTL, and RDLENGTH to response.
10319 
10320 			dns_fixed_fields_record_init( &recFields, kDNSRecordType_TXT, kDNSClassType_IN, 0, (uint16_t) rdataLen );
10321 			err = DataBuffer_Append( inDB, &recFields, sizeof( recFields ) );
10322 			require_noerr( err, exit );
10323 
10324 			// Append TXT record RDATA to response.
10325 
10326 			err = DataBuffer_Append( inDB, rdataBuf, rdataLen );
10327 			require_noerr( err, exit );
10328 
10329 			++additionalCount;
10330 		}
10331 		rcode = rcodeOverride;
10332 	}
10333 	DNSFlagsSetRCode( flags, rcode );
10334 	DNSHeaderSetFlags( hdr, flags );
10335 	DNSHeaderSetAnswerCount( hdr, answerCount );
10336 	DNSHeaderSetAdditionalCount( hdr, additionalCount );
10337 	if( procDelayMs > 0 )
10338 	{
10339 		const uint64_t		delayTicks		= MillisecondsToUpTicks( procDelayMs );
10340 		const uint64_t		elapsedTicks	= UpTicks() - startTicks;
10341 
10342 		if( delayTicks > elapsedTicks ) SleepForUpTicks( delayTicks - elapsedTicks );
10343 	}
10344 	err = kNoErr;
10345 
10346 exit:
10347 	FreeNullSafe( qnameLower );
10348 	if( sigMsg ) DataBuffer_Free( sigMsg );
10349 	return( err );
10350 }
10351 
10352 static Boolean
10353 	_DNSServerNameIsDNSSECZone(
10354 		const uint8_t *		inName,
10355 		const uint8_t **	outZoneParent,
10356 		DNSKeyInfoRef *		outZSK,
10357 		DNSKeyInfoRef *		outKSK,
10358 		DNSKeyInfoRef *		outParentZSK );
10359 
10360 static Boolean
_DNSServerParseHostName(DNSServerRef me,const uint8_t * inQName,uint32_t * outAliasCount,uint32_t outAliasTTLs[kAliasTTLCountMax],uint32_t * outAliasTTLCount,uint32_t * outCount,uint32_t * outRandCount,uint32_t * outIndex,int * outRCode,uint32_t * outTTL,uint32_t * outProcDelayMs,DNSNameFlags * outFlags,const uint8_t ** outZone,const uint8_t ** outZoneParent,DNSKeyInfoRef * outZSK,DNSKeyInfoRef * outKSK,DNSKeyInfoRef * outParentZSK)10361 	_DNSServerParseHostName(
10362 		DNSServerRef		me,
10363 		const uint8_t *		inQName,
10364 		uint32_t *			outAliasCount,
10365 		uint32_t			outAliasTTLs[ kAliasTTLCountMax ],
10366 		uint32_t *			outAliasTTLCount,
10367 		uint32_t *			outCount,
10368 		uint32_t *			outRandCount,
10369 		uint32_t *			outIndex,
10370 		int *				outRCode,
10371 		uint32_t *			outTTL,
10372 		uint32_t *			outProcDelayMs,
10373 		DNSNameFlags *		outFlags,
10374 		const uint8_t **	outZone,
10375 		const uint8_t **	outZoneParent,
10376 		DNSKeyInfoRef *		outZSK,
10377 		DNSKeyInfoRef *		outKSK,
10378 		DNSKeyInfoRef *		outParentZSK )
10379 {
10380 	OSStatus			err;
10381 	const uint8_t *		label;
10382 	size_t				labelLen;
10383     const uint8_t *		labelNext;
10384 	uint32_t			aliasTTLCount	= 0;	// Count of TTL args from Alias-TTL label.
10385 	uint32_t			aliasCount		= 0;	// Arg from Alias label. Valid values are in [2, 2^31 - 1].
10386 	int32_t				count			= -1;	// First arg from Count label. Valid values are in [0, 255].
10387 	uint32_t			randCount		= 0;	// Second arg from Count label. Valid values are in [count, 255].
10388 	uint32_t			index			= 0;	// Arg from Index label. Valid values are in [1, 2^32 - 1].
10389 	int					rcode			= -1;	// Arg from RCode label. Valid values are in [0, 15].
10390 	int32_t				ttl				= -1;	// Arg from TTL label. Valid values are in [0, 2^31 - 1].
10391 	uint32_t			procDelayMs		= 0;	// Arg from PDelay label. Valid values are in [1, 2000]. Units are in ms.
10392 	DNSNameFlags		flags			= 0;
10393 	int32_t				maxCount;
10394 	const uint8_t *		zone			= NULL;
10395 	const uint8_t *		zoneParent		= NULL;
10396 	DNSKeyInfoRef		zsk				= NULL;
10397 	DNSKeyInfoRef		ksk				= NULL;
10398 	DNSKeyInfoRef		parentZSK		= NULL;
10399 	Boolean				isAlias			= false;
10400 
10401 	for( label = inQName; ( labelLen = *label ) != 0; label = labelNext )
10402 	{
10403 		const uint8_t *		labelData;
10404 		uint32_t			arg;
10405 
10406 		if( labelLen > kDomainLabelLengthMax ) break;
10407 		labelData = &label[ 1 ];
10408 		labelNext = &labelData[ labelLen ];
10409 
10410 		if( label == inQName )
10411 		{
10412 			// Check if the first label is a valid alias TTL sequence label.
10413 			// Note: Since "alias" is a prefix of "alias-ttl", check for "alias-ttl" first.
10414 
10415 			if( strnicmp_prefix( labelData, labelLen, kLabelPrefix_AliasTTL ) == 0 )
10416 			{
10417 				const char *			ptr = (const char *) &labelData[ sizeof_string( kLabelPrefix_AliasTTL ) ];
10418 				const char * const		end = (const char *) labelNext;
10419 
10420 				while( ptr < end )
10421 				{
10422 					if( *ptr++ != '-' ) break;
10423 					err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10424 					if( err || ( arg > INT32_MAX ) ) break; // TTL must be in [0, 2^31 - 1].
10425 					if( outAliasTTLs ) outAliasTTLs[ aliasTTLCount ] = arg;
10426 					++aliasTTLCount;
10427 				}
10428 				if( ( aliasTTLCount == 0 ) || ( ptr != end ) ) break;
10429 				isAlias = true;
10430 				continue;
10431 			}
10432 
10433 			// Check if the first label is a valid alias label.
10434 
10435 			if( ( strnicmp_prefix( labelData, labelLen, kLabelPrefix_Alias ) == 0 ) )
10436 			{
10437 				const char *			ptr = (const char *) &labelData[ sizeof_string( kLabelPrefix_Alias ) ];
10438 				const char * const		end = (const char *) labelNext;
10439 
10440 				if( ptr < end )
10441 				{
10442 					if( *ptr++ != '-' ) break;
10443 					err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10444 					if( err || ( arg < 2 ) || ( arg > INT32_MAX ) ) break; // Alias count must be in [2, 2^31 - 1].
10445 					aliasCount = arg;
10446 					if( ptr != end ) break;
10447 				}
10448 				else
10449 				{
10450 					aliasCount = 1;
10451 				}
10452 				isAlias = true;
10453 				continue;
10454 			}
10455 		}
10456 
10457 		// Check if this label is a valid count label.
10458 
10459 		if( strnicmp_prefix( labelData, labelLen, kLabelPrefix_Count ) == 0  )
10460 		{
10461 			const char *			ptr = (const char *) &labelData[ sizeof_string( kLabelPrefix_Count ) ];
10462 			const char * const		end = (const char *) labelNext;
10463 
10464 			if( count >= 0 ) break; // Count cannot be specified more than once.
10465 
10466 			err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10467 			if( err || ( arg > INT32_MAX ) ) break; // The actual upper bound for Count will be verified below.
10468 			count = (int32_t) arg;
10469 
10470 			if( ptr < end )
10471 			{
10472 				if( *ptr++ != '-' ) break;
10473 				err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10474 				if( err || ( arg < ( (uint32_t) count ) ) || ( arg > 255 ) ) break; // Rand count must be in [count, 255].
10475 				randCount = arg;
10476 				if( ptr != end ) break;
10477 			}
10478 			continue;
10479 		}
10480 
10481 		// Check if this label is a valid Index label.
10482 
10483 		if( strnicmp_prefix( labelData, labelLen, kLabelPrefix_Index ) == 0  )
10484 		{
10485 			const char *			ptr = (const char *) &labelData[ sizeof_string( kLabelPrefix_Index ) ];
10486 			const char * const		end = (const char *) labelNext;
10487 
10488 			if( index > 0 ) break; // Index cannot be specified more than once.
10489 
10490 			err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10491 			if( err || ( arg < 1 ) || ( arg > UINT32_MAX ) ) break; // Index must be in [1, 2^32 - 1].
10492 			index = arg;
10493 			if( ptr != end ) break;
10494 			continue;
10495 		}
10496 
10497 		// Check if this label is a valid IPv4 label.
10498 
10499 		if( strnicmpx( labelData, labelLen, kLabel_IPv4 ) == 0 )
10500 		{
10501 			// Valid names have at most one IPv4 or IPv6 label.
10502 
10503 			if( flags & ( kDNSNameFlag_HasA | kDNSNameFlag_HasAAAA ) ) break;
10504 			flags |= kDNSNameFlag_HasA;
10505 			continue;
10506 		}
10507 
10508 		// Check if this label is a valid IPv6 label.
10509 
10510 		if( strnicmpx( labelData, labelLen, kLabel_IPv6 ) == 0 )
10511 		{
10512 			// Valid names have at most one IPv4 or IPv6 label.
10513 
10514 			if( flags & ( kDNSNameFlag_HasA | kDNSNameFlag_HasAAAA ) ) break;
10515 			flags |= kDNSNameFlag_HasAAAA;
10516 			continue;
10517 		}
10518 
10519 		// Check if this label is a valid tag label.
10520 
10521 		if( strnicmp_prefix( labelData, labelLen, kLabelPrefix_Tag ) == 0  )
10522 		{
10523 			continue;
10524 		}
10525 
10526 		// Check if this label is a valid RCode label.
10527 
10528 		if( strnicmp_prefix( labelData, labelLen, kLabelPrefix_RCode ) == 0  )
10529 		{
10530 			const char *			ptr = (const char *) &labelData[ sizeof_string( kLabelPrefix_RCode ) ];
10531 			const char * const		end = (const char *) labelNext;
10532 			const char *			src;
10533 			char *					dst;
10534 			const char *			lim;
10535 			char					argStr[ kDomainLabelLengthMax + 1 ];
10536 
10537 			if( rcode >= 0 ) break; // RCode cannot be specified more than once.
10538 
10539 			// First check if the RCode label's argument is an RCODE mnemonic, e.g., ServFail, Refused, etc.
10540 			// The argument part of a label consists of all of the characters up to the start of the next label.
10541 			// In order to treat the argument as a C string, the argument must not contain any NUL characters.
10542 			// For example, a malformed label such as "rcode-refused\x00garbage" has the argument "refused\x00garbage",
10543 			// but as a C string, the NUL character makes it "refused".
10544 
10545 			src = ptr;
10546 			dst = argStr;
10547 			lim = &argStr[ countof( argStr ) - 1 ];
10548 			while( ( src < end ) && ( *src != '\0' ) && ( dst < lim ) ) *dst++ = *src++;
10549 			if( src == end )
10550 			{
10551 				*dst = '\0';
10552 				rcode = DNSRCodeFromString( argStr );
10553 			}
10554 
10555 			// If we don't have a valid rcode yet, try to parse the argument as a decimal integer.
10556 
10557 			if( rcode < 0 )
10558 			{
10559 				err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10560 				if( err || ( arg > 15 ) ) break; // RCode must be in [0, 15].
10561 				rcode = (int) arg;
10562 				if( ptr != end ) break;
10563 			}
10564 			continue;
10565 		}
10566 
10567 		// Check if this label is a valid TTL label.
10568 
10569 		if( strnicmp_prefix( labelData, labelLen, kLabelPrefix_TTL ) == 0  )
10570 		{
10571 			const char *			ptr = (const char *) &labelData[ sizeof_string( kLabelPrefix_TTL ) ];
10572 			const char * const		end = (const char *) labelNext;
10573 
10574 			if( ttl >= 0 ) break; // TTL cannot be specified more than once.
10575 
10576 			err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10577 			if( err || ( arg > INT32_MAX ) ) break; // TTL must be in [0, 2^31 - 1].
10578 			ttl = (int32_t) arg;
10579 			if( ptr != end ) break;
10580 			continue;
10581 		}
10582 
10583 		// Check if this label is a valid PDelay label.
10584 
10585 		if( strnicmp_prefix( labelData, labelLen, kLabelPrefix_PDelay ) == 0 )
10586 		{
10587 			const char *			ptr = (const char *) &labelData[ sizeof_string( kLabelPrefix_PDelay ) ];
10588 			const char * const		end = (const char *) labelNext;
10589 
10590 			if( procDelayMs > 0 ) break; // PDelay cannot be specified more than once.
10591 
10592 			err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10593 			if( err || ( arg < 1 ) || ( arg > 2000 ) ) break; // PDelay must be in [1, 2000].
10594 			procDelayMs = arg;
10595 			if( ptr != end ) break;
10596 			continue;
10597 		}
10598 
10599 		// If this and the remaining labels are equal to "d.test.", then the name exists.
10600 		// Otherwise, this label is invalid. In both cases, there are no more labels to check.
10601 
10602 		if( _DNSServerNameIsDNSSECZone( label, &zoneParent, &zsk, &ksk, &parentZSK ) )
10603 		{
10604 			zone = label;
10605 			flags |= kDNSNameFlag_HasRRSIG;
10606 			if( ( label == inQName ) || ( isAlias && ( label == DomainNameGetNextLabel( inQName ) ) ) )
10607 			{
10608 				flags |= kDNSNameFlag_HasSOA;
10609 				flags |= kDNSNameFlag_HasDNSKEY;
10610 				flags |= kDNSNameFlag_HasDS;
10611 			}
10612 		}
10613 		else if( DomainNameEqual( label, me->domain ) )
10614 		{
10615 			zone = label;
10616 			if( ( label == inQName ) || ( isAlias && ( label == DomainNameGetNextLabel( inQName ) ) ) )
10617 			{
10618 				flags |= kDNSNameFlag_HasSOA;
10619 			}
10620 		}
10621 		break;
10622 	}
10623 	require_quiet( zone, exit );
10624 
10625 	// If a Count value of 0 was specified, then the hostname has no A or AAAA records.
10626 	// Otherwise, if the hostname has no IPv4 or IPv6 labels, then it has both A and AAAA records.
10627 
10628 	if( count == 0 )
10629 	{
10630 		flags &= ~( kDNSNameFlag_HasA | kDNSNameFlag_HasAAAA );
10631 	}
10632 	else if( !( flags & ( kDNSNameFlag_HasA | kDNSNameFlag_HasAAAA ) ) )
10633 	{
10634 		flags |= ( kDNSNameFlag_HasA | kDNSNameFlag_HasAAAA );
10635 	}
10636 
10637 	// Allow IPv6-only hostnames to have a maximum address count of up to 511 instead of the normal 255.
10638 
10639 	maxCount = ( ( flags & ( kDNSNameFlag_HasA | kDNSNameFlag_HasAAAA ) ) == kDNSNameFlag_HasAAAA ) ? 511 : 255;
10640 	require_action_quiet( count <= maxCount, exit, zone = NULL );
10641 
10642 	if( outAliasCount )		*outAliasCount		= aliasCount;
10643 	if( outAliasTTLCount )	*outAliasTTLCount	= aliasTTLCount;
10644 	if( outCount )			*outCount			= ( count >= 0 ) ? ( (uint32_t) count ) : 1;
10645 	if( outRandCount )		*outRandCount		= randCount;
10646 	if( outIndex )			*outIndex			= index;
10647 	if( outRCode )			*outRCode			= rcode;
10648 	if( outTTL )			*outTTL				= ( ttl >= 0 ) ? ( (uint32_t) ttl ) : me->defaultTTL;
10649 	if( outProcDelayMs )	*outProcDelayMs		= procDelayMs;
10650 	if( outFlags )			*outFlags			= flags;
10651 	if( outZone )			*outZone			= zone;
10652 	if( outZoneParent )		*outZoneParent		= zoneParent;
10653 	if( outZSK )			*outZSK				= zsk;
10654 	if( outKSK )			*outKSK				= ksk;
10655 	if( outParentZSK )		*outParentZSK		= parentZSK;
10656 
10657 exit:
10658 	return( zone ? true : false );
10659 }
10660 
10661 //===========================================================================================================================
10662 
10663 static Boolean
_DNSServerParseSRVName(DNSServerRef me,const uint8_t * inName,const uint8_t ** outDomainPtr,size_t * outDomainLen,ParsedSRV outSRVArray[kParsedSRVCountMax],size_t * outSRVCount)10664 	_DNSServerParseSRVName(
10665 		DNSServerRef		me,
10666 		const uint8_t *		inName,
10667 		const uint8_t **	outDomainPtr,
10668 		size_t *			outDomainLen,
10669 		ParsedSRV			outSRVArray[ kParsedSRVCountMax ],
10670 		size_t *			outSRVCount )
10671 {
10672 	OSStatus			err;
10673 	const uint8_t *		label;
10674 	const uint8_t *		domainPtr;
10675 	size_t				domainLen;
10676 	size_t				srvCount;
10677 	uint32_t			arg;
10678 	int					isNameValid = false;
10679 
10680 	label = inName;
10681 
10682 	// Ensure that first label, i.e, the service label, begins with a '_' character.
10683 
10684 	require_quiet( ( label[ 0 ] > 0 ) && ( label[ 1 ] == '_' ), exit );
10685 	label = DomainNameGetNextLabel( label );
10686 
10687 	// Ensure that the second label, i.e., the proto label, begins with a '_' character (usually _tcp or _udp).
10688 
10689 	require_quiet( ( label[ 0 ] > 0 ) && ( label[ 1 ] == '_' ), exit );
10690 	label = DomainNameGetNextLabel( label );
10691 
10692 	// Parse the domain name, if any.
10693 
10694 	domainPtr = label;
10695 	while( *label )
10696 	{
10697 		if( DomainNameEqual( label, me->domain ) ||
10698 			( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 ) ) break;
10699 		label = DomainNameGetNextLabel( label );
10700 	}
10701 	require_quiet( *label, exit );
10702 
10703 	domainLen = (size_t)( label - domainPtr );
10704 
10705 	// Parse SRV labels, if any.
10706 
10707 	srvCount = 0;
10708 	while( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 )
10709 	{
10710 		const uint8_t * const	nextLabel	= DomainNameGetNextLabel( label );
10711 		const char *			ptr			= (const char *) &label[ 1 + sizeof_string( kLabelPrefix_SRV ) ];
10712 		const char * const		end			= (const char *) nextLabel;
10713 		const uint8_t *			target;
10714 		unsigned int			priority, weight, port;
10715 
10716 		err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10717 		require_quiet( !err && ( arg <= UINT16_MAX ), exit );
10718 		priority = (unsigned int) arg;
10719 
10720 		require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
10721 		++ptr;
10722 
10723 		err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10724 		require_quiet( !err && ( arg <= UINT16_MAX ), exit );
10725 		weight = (unsigned int) arg;
10726 
10727 		require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
10728 		++ptr;
10729 
10730 		err = DecimalTextToUInt32( ptr, end, &arg, &ptr );
10731 		require_quiet( !err && ( arg <= UINT16_MAX ), exit );
10732 		port = (unsigned int) arg;
10733 
10734 		require_quiet( ptr == end, exit );
10735 
10736 		target = nextLabel;
10737 		for( label = nextLabel; *label; label = DomainNameGetNextLabel( label ) )
10738 		{
10739 			if( DomainNameEqual( label, me->domain ) ||
10740 				( strnicmp_prefix( &label[ 1 ], label[ 0 ], kLabelPrefix_SRV ) == 0 ) ) break;
10741 		}
10742 		require_quiet( *label, exit );
10743 
10744 		if( outSRVArray )
10745 		{
10746 			outSRVArray[ srvCount ].priority	= (uint16_t) priority;
10747 			outSRVArray[ srvCount ].weight		= (uint16_t) weight;
10748 			outSRVArray[ srvCount ].port		= (uint16_t) port;
10749 			outSRVArray[ srvCount ].targetPtr	= target;
10750 			outSRVArray[ srvCount ].targetLen	= (uint16_t)( label - target );
10751 		}
10752 		++srvCount;
10753 	}
10754 	require_quiet( DomainNameEqual( label, me->domain ), exit );
10755 	isNameValid = true;
10756 
10757 	if( outDomainPtr )	*outDomainPtr	= domainPtr;
10758 	if( outDomainLen )	*outDomainLen	= domainLen;
10759 	if( outSRVCount )	*outSRVCount	= srvCount;
10760 
10761 exit:
10762 	return( isNameValid ? true : false );
10763 }
10764 
10765 //===========================================================================================================================
10766 
_DNSServerParseReverseIPv4Name(DNSServerRef me,const uint8_t * inQName,unsigned int * outHostID)10767 static Boolean	_DNSServerParseReverseIPv4Name( DNSServerRef me, const uint8_t *inQName, unsigned int *outHostID )
10768 {
10769 	OSStatus			err;
10770 	const uint8_t *		label;
10771 	size_t				labelLen;
10772 	const uint8_t *		labelData;
10773     const uint8_t *		labelNext;
10774 	const uint8_t *		ptr;
10775 	uint32_t			hostID;
10776 	int					isNameValid = false;
10777 
10778 	Unused( me );
10779 
10780 	label		= inQName;
10781 	labelLen	= *label;
10782 	require_quiet( labelLen > 0, exit );
10783 
10784 	labelData = &label[ 1 ];
10785 	labelNext = &labelData[ labelLen ];
10786 	err = DecimalTextToUInt32( (const char *) labelData, (const char *) labelNext, &hostID, (const char **) &ptr );
10787 	require_noerr_quiet( err, exit );
10788 	require_quiet( ( hostID >= 1 ) && ( hostID <= 255 ), exit );
10789 	require_quiet( ptr == labelNext, exit );
10790 
10791 	require_quiet( DomainNameEqual( labelNext, kDNSServerReverseIPv4DomainName ), exit );
10792 	isNameValid = true;
10793 
10794 	if( outHostID ) *outHostID = (unsigned int) hostID;
10795 
10796 exit:
10797 	return( isNameValid ? true : false );
10798 }
10799 
10800 //===========================================================================================================================
10801 
_DNSServerParseReverseIPv6Name(DNSServerRef me,const uint8_t * inQName,unsigned int * outHostID)10802 static Boolean	_DNSServerParseReverseIPv6Name( DNSServerRef me, const uint8_t *inQName, unsigned int *outHostID )
10803 {
10804 	const uint8_t *		label;
10805 	unsigned int		hostID;
10806 	int					i;
10807 	int					isNameValid = false;
10808 
10809 	Unused( me );
10810 
10811 	hostID	= 0;
10812 	label	= inQName;
10813 	for( i = 0; i < 2; ++i )
10814 	{
10815 		unsigned int		labelLen, c;
10816 
10817 		labelLen = label[ 0 ];
10818 		require_quiet( labelLen == 1, exit );
10819 
10820 		c = label[ 1 ];
10821 		require_quiet( isxdigit_safe( c ), exit );
10822 
10823 		hostID = hostID | ( HexCharToValue( c ) << ( 4 * i ) );
10824 		label = &label[ 1 + labelLen ];
10825 	}
10826 	require_quiet( ( hostID >= 1 ) && ( hostID <= 255 ), exit );
10827 	require_quiet( DomainNameEqual( label, kDNSServerReverseIPv6DomainName ), exit );
10828 	isNameValid = true;
10829 
10830 	if( outHostID ) *outHostID = hostID;
10831 
10832 exit:
10833 	return( isNameValid ? true : false );
10834 }
10835 
10836 #if( DEBUG )
10837 //===========================================================================================================================
10838 
10839 static void
_DNSServerSigCheck(const uint8_t * inOwner,int inTypeCovered,const void * inMsgPtr,size_t inMsgLen,const uint8_t * inSignaturePtr,const size_t inSignatureLen,DNSKeyInfoRef inKeyInfo)10840 	_DNSServerSigCheck(
10841 		const uint8_t *	inOwner,
10842 		int				inTypeCovered,
10843 		const void *	inMsgPtr,
10844 		size_t			inMsgLen,
10845 		const uint8_t *	inSignaturePtr,
10846 		const size_t	inSignatureLen,
10847 		DNSKeyInfoRef	inKeyInfo )
10848 {
10849 	if( !DNSKeyInfoVerify( inKeyInfo, inMsgPtr, inMsgLen, inSignaturePtr, inSignatureLen ) )
10850 	{
10851 		const char *		typeStr;
10852 		char				typeBuf[ 16 ];
10853 
10854 		typeStr = DNSRecordTypeValueToString( inTypeCovered );
10855 		if( !typeStr )
10856 		{
10857 			SNPrintF( typeBuf, sizeof( typeBuf ), "TYPE%d", inTypeCovered );
10858 			typeStr = typeBuf;
10859 		}
10860 		ds_ulog( kLogLevelError,
10861 			"Signature for %{du:dname} %s is invalid! -- algorithm: %s (%d), public key: '%H'\n",
10862 			inOwner, typeStr, DNSKeyInfoGetAlgorithmDescription( inKeyInfo ), DNSKeyInfoGetAlgorithm( inKeyInfo ),
10863 			DNSKeyInfoGetPubKeyPtr( inKeyInfo ), DNSKeyInfoGetPubKeyLen( inKeyInfo ), SIZE_MAX );
10864 	}
10865 }
10866 #endif
10867 
10868 //===========================================================================================================================
10869 
10870 #define kDNSServerDefaultDNSSECAlgorithm		14	// TODO: Think about adding an option for the default algorithm.
10871 
10872 static Boolean
_DNSServerNameIsDNSSECZone(const uint8_t * inName,const uint8_t ** outZoneParent,DNSKeyInfoRef * outZSK,DNSKeyInfoRef * outKSK,DNSKeyInfoRef * outParentZSK)10873 	_DNSServerNameIsDNSSECZone(
10874 		const uint8_t *		inName,
10875 		const uint8_t **	outZoneParent,
10876 		DNSKeyInfoRef *		outZSK,
10877 		DNSKeyInfoRef *		outKSK,
10878 		DNSKeyInfoRef *		outParentZSK )
10879 {
10880 	const uint8_t *		label;
10881 	size_t				labelLen;
10882 	const uint8_t *		labelNext;
10883 	const uint8_t *		zoneParent			= NULL;
10884 	DNSKeyInfoRef		zsk;
10885 	DNSKeyInfoRef		ksk;
10886 	DNSKeyInfoRef		parentZSK;
10887 	uint32_t			zoneAlgorithm		= 0;
10888 	uint32_t			zoneIndex			= 0;
10889 	uint32_t			zoneParentAlgorithm	= 0;
10890 	uint32_t			zoneParentIndex		= 0;
10891 	Boolean				parsedAllLabels		= false;
10892 	Boolean				nameIsValid			= false;
10893 
10894 	for( label = inName; ( labelLen = *label ) != 0; label = labelNext )
10895 	{
10896 		const uint8_t *		labelData;
10897 
10898 		if( labelLen > kDomainLabelLengthMax ) break;
10899 		labelData = &label[ 1 ];
10900 		labelNext = &labelData[ labelLen ];
10901 
10902 		if( strnicmp_prefix( labelData, labelLen, kLabelPrefix_Zone ) == 0  )
10903 		{
10904 			OSStatus				err;
10905 			const char *			ptr = (const char *) &labelData[ sizeof_string( kLabelPrefix_Zone ) ];
10906 			const char * const		end = (const char *) labelNext;
10907 			uint32_t				algorithm;
10908 			uint32_t				index;
10909 
10910 			err = DecimalTextToUInt32( ptr, end, &algorithm, &ptr );
10911 			if( err ) break;
10912 			if( ( ptr >= end ) || ( *ptr++ != '-' ) ) break;
10913 
10914 			err = DecimalTextToUInt32( ptr, end, &index, &ptr );
10915 			if( err || ( index < kZoneLabelIndexArgMin ) || ( index > kZoneLabelIndexArgMax ) ) break;
10916 			if( ptr != end ) break;
10917 			if( zoneIndex == 0 )
10918 			{
10919 				zoneAlgorithm	= algorithm;
10920 				zoneIndex		= index;
10921 			}
10922 			else if( zoneParentIndex == 0 )
10923 			{
10924 				zoneParentAlgorithm	= algorithm;
10925 				zoneParent			= label;
10926 				zoneParentIndex		= index;
10927 			}
10928 			continue;
10929 		}
10930 		if( DomainNameEqual( label, kDNSServerDomain_DNSSEC ) )
10931 		{
10932 			if( !zoneParent ) zoneParent = label;
10933 			parsedAllLabels = true;
10934 		}
10935 		break;
10936 	}
10937 	require_quiet( parsedAllLabels, exit );
10938 
10939 	if( zoneAlgorithm == 0 ) zoneAlgorithm = kDNSServerDefaultDNSSECAlgorithm;
10940 	zsk = GetDNSKeyInfoZSK( zoneAlgorithm, zoneIndex );
10941 	require_quiet( zsk, exit );
10942 
10943 	ksk = GetDNSKeyInfoKSK( zoneAlgorithm, zoneIndex );
10944 	require_quiet( ksk, exit );
10945 
10946 	if( zoneParentAlgorithm == 0 ) zoneParentAlgorithm = kDNSServerDefaultDNSSECAlgorithm;
10947 	parentZSK = GetDNSKeyInfoZSK( zoneParentAlgorithm, zoneParentIndex );
10948 	require_quiet( parentZSK, exit );
10949 
10950 	if( outZoneParent )	*outZoneParent	= zoneParent;
10951 	if( outZSK )		*outZSK			= zsk;
10952 	if( outKSK )		*outKSK			= ksk;
10953 	if( outParentZSK )	*outParentZSK	= parentZSK;
10954 	nameIsValid = true;
10955 
10956 exit:
10957 	return( nameIsValid );
10958 }
10959 
10960 //===========================================================================================================================
10961 
10962 static OSStatus
_DNSServerConnectionCreate(DNSServerRef inServer,const struct sockaddr * inLocal,const struct sockaddr * inRemote,size_t inIndex,DNSServerConnectionRef * outCnx)10963 	_DNSServerConnectionCreate(
10964 		DNSServerRef				inServer,
10965 		const struct sockaddr *		inLocal,
10966 		const struct sockaddr *		inRemote,
10967 		size_t						inIndex,
10968 		DNSServerConnectionRef *	outCnx )
10969 {
10970 	OSStatus					err;
10971 	DNSServerConnectionRef		obj;
10972 
10973 	CF_OBJECT_CREATE( DNSServerConnection, obj, err, exit );
10974 
10975 	obj->index	= inIndex;
10976 	obj->server	= inServer;
10977 	CFRetain( obj->server );
10978 	SockAddrCopy( inLocal, &obj->local );
10979 	SockAddrCopy( inRemote, &obj->remote );
10980 
10981 	*outCnx = obj;
10982 	err = kNoErr;
10983 
10984 exit:
10985 	return( err );
10986 }
10987 
10988 //===========================================================================================================================
10989 
_DNSServerConnectionFinalize(CFTypeRef inObj)10990 static void	_DNSServerConnectionFinalize( CFTypeRef inObj )
10991 {
10992 	const DNSServerConnectionRef		me = (DNSServerConnectionRef) inObj;
10993 
10994 	check( !me->readSource );
10995 	check( !me->writeSource );
10996 	ForgetCF( &me->server );
10997 	ForgetMem( &me->msgPtr );
10998 }
10999 
11000 //===========================================================================================================================
11001 
_DNSServerConnectionStart(DNSServerConnectionRef me,SocketRef inSock)11002 static OSStatus	_DNSServerConnectionStart( DNSServerConnectionRef me, SocketRef inSock )
11003 {
11004 	OSStatus			err;
11005 	SocketContext *		sockCtx = NULL;
11006 
11007 	err = SocketMakeNonBlocking( inSock );
11008 	require_noerr( err, exit );
11009 
11010 #if( defined( SO_NOSIGPIPE ) )
11011 	setsockopt( inSock, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, (socklen_t) sizeof( int ) );
11012 #endif
11013 	me->readSource = dispatch_source_create( DISPATCH_SOURCE_TYPE_READ, (uintptr_t) inSock, 0, me->server->queue );
11014 	require_action( me->readSource, exit, err = kNoResourcesErr );
11015 	me->readSuspended = true;
11016 
11017 	me->writeSource = dispatch_source_create( DISPATCH_SOURCE_TYPE_WRITE, (uintptr_t) inSock, 0, me->server->queue );
11018 	require_action( me->writeSource, exit, err = kNoResourcesErr );
11019 	me->writeSuspended = true;
11020 
11021 	sockCtx = SocketContextCreateEx( inSock, me, SocketContextFinalizerCF, &err );
11022 	require_noerr( err, exit );
11023 	CFRetain( me );
11024 
11025 	SocketContextRetain( sockCtx );
11026 	dispatch_set_context( me->readSource, sockCtx );
11027 	dispatch_source_set_event_handler_f( me->readSource, _DNSServerConnectionReadHandler );
11028 	dispatch_source_set_cancel_handler_f( me->readSource, SocketContextCancelHandler );
11029 	dispatch_resume_if_suspended( me->readSource, &me->readSuspended );
11030 
11031 	SocketContextRetain( sockCtx );
11032 	dispatch_set_context( me->writeSource, sockCtx );
11033 	dispatch_source_set_event_handler_f( me->writeSource, _DNSServerConnectionWriteHandler );
11034 	dispatch_source_set_cancel_handler_f( me->writeSource, SocketContextCancelHandler );
11035 
11036 	_DNSServerConnectionRenewExpiration( me );
11037 
11038 exit:
11039 	if( sockCtx ) SocketContextRelease( sockCtx );
11040 	if( err ) _DNSServerConnectionStop( me, true );
11041 	return( err );
11042 }
11043 
11044 //===========================================================================================================================
11045 
_DNSServerConnectionStop(DNSServerConnectionRef me,Boolean inRemoveFromList)11046 static void	_DNSServerConnectionStop( DNSServerConnectionRef me, Boolean inRemoveFromList )
11047 {
11048 	dispatch_source_forget_ex( &me->readSource, &me->readSuspended );
11049 	dispatch_source_forget_ex( &me->writeSource, &me->writeSuspended );
11050 	if( inRemoveFromList )
11051 	{
11052 		DNSServerConnectionRef *		ptr;
11053 
11054 		ptr = &me->server->connectionList;
11055 		while( *ptr && ( *ptr != me ) ) ptr = &( *ptr )->next;
11056 		if( *ptr )
11057 		{
11058 			*ptr = me->next;
11059 			me->next = NULL;
11060 			CFRelease( me );
11061 		}
11062 	}
11063 }
11064 
11065 //===========================================================================================================================
11066 
_DNSServerConnectionReadHandler(void * inContext)11067 static void	_DNSServerConnectionReadHandler( void *inContext )
11068 {
11069 	OSStatus							err;
11070 	const SocketContext * const			sockCtx	= (SocketContext *) inContext;
11071 	const DNSServerConnectionRef		me		= (DNSServerConnectionRef) sockCtx->userContext;
11072 	uint8_t *							respPtr	= NULL;	// malloc'd
11073 	size_t								respLen;
11074 
11075 	// Receive message length.
11076 
11077 	if( !me->haveLen )
11078 	{
11079 		err = SocketReadData( sockCtx->sock, me->lenBuf, sizeof( me->lenBuf ), &me->offset );
11080 		if( ( err == EWOULDBLOCK ) || ( err == kConnectionErr ) ) goto exit;
11081 		require_noerr( err, exit );
11082 
11083 		me->haveLen	= true;
11084 		me->offset	= 0;
11085 		me->msgLen	= ReadBig16( me->lenBuf );
11086 		if( me->msgLen < kDNSHeaderLength )
11087 		{
11088 			ds_ulog( kLogLevelInfo, "TCP: Message length of %zu bytes from %##a to %##a is too small (< %d bytes)\n",
11089 				me->msgLen, &me->remote, &me->local, kDNSHeaderLength );
11090 			err = kSizeErr;
11091 			goto exit;
11092 		}
11093 		me->msgPtr = malloc( me->msgLen );
11094 		require_action( me->msgPtr, exit, err = kNoMemoryErr );
11095 	}
11096 
11097 	// Receive message.
11098 
11099 	err = SocketReadData( sockCtx->sock, me->msgPtr, me->msgLen, &me->offset );
11100 	if( ( err == EWOULDBLOCK ) || ( err == kConnectionErr ) ) goto exit;
11101 	require_noerr( err, exit );
11102 	dispatch_suspend_if_resumed( me->readSource, &me->readSuspended );
11103 	me->offset	= 0;
11104 	me->haveLen	= false;
11105 
11106 	ds_ulog( kLogLevelInfo, "TCP: Received %zu bytes from %##a to %##a -- %.1{du:dnsmsg}\n",
11107 		me->msgLen, &me->remote, &me->local, me->msgPtr, me->msgLen );
11108 
11109 	// Create response.
11110 
11111 	err = _DNSServerAnswerQueryForTCP( me->server, me->msgPtr, me->msgLen, me->index + 1, &respPtr, &respLen );
11112 	if( err == kSkipErr ) ds_ulog( kLogLevelInfo, "TCP: Ignoring query\n" );
11113 	require_noerr_quiet( err, exit );
11114 
11115 	_DNSServerConnectionRenewExpiration( me );
11116 
11117 	// Prepare to send response.
11118 
11119 	FreeNullSafe( me->msgPtr );
11120 	me->msgPtr = respPtr;
11121 	me->msgLen = respLen;
11122 	respPtr = NULL;
11123 
11124 	ds_ulog( kLogLevelInfo, "TCP: Sending %zu byte response from %##a to %##a -- %.1{du:dnsmsg}\n",
11125 		me->msgLen, &me->local, &me->remote, me->msgPtr, me->msgLen );
11126 
11127 	check( me->msgLen <= UINT16_MAX );
11128 	WriteBig16( me->lenBuf, me->msgLen );
11129 	me->iov[ 0 ].iov_base	= me->lenBuf;
11130 	me->iov[ 0 ].iov_len	= sizeof( me->lenBuf );
11131 	me->iov[ 1 ].iov_base	= me->msgPtr;
11132 	me->iov[ 1 ].iov_len	= me->msgLen;
11133 	me->iovPtr				= me->iov;
11134 	me->iovCount			= 2;
11135 	dispatch_resume_if_suspended( me->writeSource, &me->writeSuspended );
11136 
11137 exit:
11138 	FreeNullSafe( respPtr );
11139 	if( err && ( err != EWOULDBLOCK ) )
11140 	{
11141 		_DNSServerConnectionStop( me, true );
11142 	}
11143 }
11144 
11145 //===========================================================================================================================
11146 
_DNSServerConnectionWriteHandler(void * inContext)11147 static void	_DNSServerConnectionWriteHandler( void *inContext )
11148 {
11149 	OSStatus							err;
11150 	const SocketContext * const			sockCtx	= (SocketContext *) inContext;
11151 	const DNSServerConnectionRef		me		= (DNSServerConnectionRef) sockCtx->userContext;
11152 
11153 	err = SocketWriteData( sockCtx->sock, &me->iovPtr, &me->iovCount );
11154 	if( !err )
11155 	{
11156 		me->iovPtr		= NULL;
11157 		me->iovCount	= 0;
11158 		memset( me->iov, 0, sizeof( me->iov ) );
11159 		ForgetPtrLen( &me->msgPtr, &me->msgLen );
11160 		dispatch_suspend_if_resumed( me->writeSource, &me->writeSuspended );
11161 		dispatch_resume_if_suspended( me->readSource, &me->readSuspended );
11162 	}
11163 	else if( err != EWOULDBLOCK )
11164 	{
11165 		_DNSServerConnectionStop( me, true );
11166 	}
11167 }
11168 
11169 //===========================================================================================================================
11170 
_DNSServerConnectionRenewExpiration(DNSServerConnectionRef me)11171 static void	_DNSServerConnectionRenewExpiration( DNSServerConnectionRef me )
11172 {
11173 	me->expirationTicks = UpTicks() + SecondsToUpTicks( kDNSServerConnectionExpirationTimeSecs );
11174 }
11175 
11176 //===========================================================================================================================
11177 //	MDNSReplierCmd
11178 //===========================================================================================================================
11179 
11180 typedef struct
11181 {
11182 	uint8_t *				hostname;			// Used as the base name for hostnames and service names.
11183 	uint8_t *				serviceLabel;		// Label containing the base service name.
11184 	unsigned int			maxInstanceCount;	// Maximum number of service instances and hostnames.
11185 	uint64_t *				bitmaps;			// Array of 64-bit bitmaps for keeping track of needed responses.
11186 	size_t					bitmapCount;		// Number of 64-bit bitmaps.
11187 	dispatch_source_t		readSourceV4;		// Read dispatch source for IPv4 socket.
11188 	dispatch_source_t		readSourceV6;		// Read dispatch source for IPv6 socket.
11189 	uint32_t				ifIndex;			// Index of the interface to run on.
11190 	unsigned int			recordCountA;		// Number of A records per hostname.
11191 	unsigned int			recordCountAAAA;	// Number of AAAA records per hostname.
11192 	unsigned int			maxDropCount;		// If > 0, the drop rates apply to only the first <maxDropCount> responses.
11193 	double					ucastDropRate;		// Probability of dropping a unicast response.
11194 	double					mcastDropRate;		// Probability of dropping a multicast query or response.
11195 	uint8_t *				dropCounters;		// If maxDropCount > 0, array of <maxInstanceCount> response drop counters.
11196 	Boolean					noAdditionals;		// True if responses are to not include additional records.
11197 	Boolean					useIPv4;			// True if the replier is to use IPv4.
11198 	Boolean					useIPv6;			// True if the replier is to use IPv6.
11199 	uint8_t					msgBuf[ kMDNSMessageSizeMax ];	// Buffer for received mDNS message.
11200 #if( TARGET_OS_DARWIN )
11201 	dispatch_source_t		processMonitor;		// Process monitor source for process being followed, if any.
11202 	pid_t					followPID;			// PID of process being followed, if any. (If it exits, we exit).
11203 #endif
11204 
11205 }	MDNSReplierContext;
11206 
11207 typedef struct MRResourceRecord		MRResourceRecord;
11208 struct MRResourceRecord
11209 {
11210 	MRResourceRecord *		next;		// Next item in list.
11211 	uint8_t *				name;		// Resource record name.
11212 	uint16_t				type;		// Resource record type.
11213 	uint16_t				class;		// Resource record class.
11214 	uint32_t				ttl;		// Resource record TTL.
11215 	uint16_t				rdlength;	// Resource record data length.
11216 	uint8_t *				rdata;		// Resource record data.
11217 	const uint8_t *			target;		// For SRV records, pointer to target in RDATA.
11218 };
11219 
11220 typedef struct MRNameOffsetItem		MRNameOffsetItem;
11221 struct MRNameOffsetItem
11222 {
11223 	MRNameOffsetItem *	next;		// Next item in list.
11224 	uint16_t			offset;		// Offset of domain name in response message.
11225 	uint8_t				name[ 1 ];	// Variable-length array for domain name.
11226 };
11227 
11228 #if( TARGET_OS_DARWIN )
11229 static void		_MDNSReplierFollowedProcessHandler( void *inContext );
11230 #endif
11231 static void		_MDNSReplierReadHandler( void *inContext );
11232 static OSStatus
11233 	_MDNSReplierAnswerQuery(
11234 		MDNSReplierContext *	inContext,
11235 		const uint8_t *			inQueryPtr,
11236 		size_t					inQueryLen,
11237 		sockaddr_ip *			inSender,
11238 		SocketRef				inSock,
11239 		unsigned int			inIndex );
11240 static OSStatus
11241 	_MDNSReplierAnswerListAdd(
11242 		MDNSReplierContext *	inContext,
11243 		MRResourceRecord **		inAnswerList,
11244 		unsigned int			inIndex,
11245 		const uint8_t *			inName,
11246 		unsigned int			inType,
11247 		unsigned int			inClass );
11248 static void
11249 	_MDNSReplierAnswerListRemovePTR(
11250 		MRResourceRecord **	inAnswerListPtr,
11251 		const uint8_t *		inName,
11252 		const uint8_t *		inRData );
11253 static OSStatus
11254 	_MDNSReplierSendOrDropResponse(
11255 		MDNSReplierContext *	inContext,
11256 		MRResourceRecord *		inAnswerList,
11257 		sockaddr_ip *			inQuerier,
11258 		SocketRef				inSock,
11259 		unsigned int			inIndex,
11260 		Boolean					inUnicast );
11261 static OSStatus
11262 	_MDNSReplierCreateResponse(
11263 		MDNSReplierContext *	inContext,
11264 		MRResourceRecord *		inAnswerList,
11265 		unsigned int			inIndex,
11266 		uint8_t **				outResponsePtr,
11267 		size_t *				outResponseLen );
11268 static OSStatus
11269 	_MDNSReplierAppendNameToResponse(
11270 		DataBuffer *		inResponse,
11271 		const uint8_t *		inName,
11272 		MRNameOffsetItem **	inNameOffsetListPtr );
11273 static Boolean
11274 	_MDNSReplierServiceTypeMatch(
11275 		const MDNSReplierContext *	inContext,
11276 		const uint8_t *				inName,
11277 		unsigned int *				outTXTSize,
11278 		unsigned int *				outCount );
11279 static Boolean
11280 	_MDNSReplierServiceInstanceNameMatch(
11281 		const MDNSReplierContext *	inContext,
11282 		const uint8_t *				inName,
11283 		unsigned int *				outIndex,
11284 		unsigned int *				outTXTSize,
11285 		unsigned int *				outCount );
11286 static Boolean	_MDNSReplierAboutRecordNameMatch( const MDNSReplierContext *inContext, const uint8_t *inName );
11287 static Boolean
11288 	_MDNSReplierHostnameMatch(
11289 		const MDNSReplierContext *	inContext,
11290 		const uint8_t *				inName,
11291 		unsigned int *				outIndex );
11292 static OSStatus	_MDNSReplierCreateTXTRecord( const uint8_t *inRecordName, size_t inSize, uint8_t **outTXT );
11293 static OSStatus
11294 	_MRResourceRecordCreate(
11295 		uint8_t *			inName,
11296 		uint16_t			inType,
11297 		uint16_t			inClass,
11298 		uint32_t			inTTL,
11299 		uint16_t			inRDLength,
11300 		uint8_t *			inRData,
11301 		MRResourceRecord **	outRecord );
11302 static void		_MRResourceRecordFree( MRResourceRecord *inRecord );
11303 static void		_MRResourceRecordFreeList( MRResourceRecord *inList );
11304 static OSStatus	_MRNameOffsetItemCreate( const uint8_t *inName, uint16_t inOffset, MRNameOffsetItem **outItem );
11305 static void		_MRNameOffsetItemFree( MRNameOffsetItem *inItem );
11306 static void		_MRNameOffsetItemFreeList( MRNameOffsetItem *inList );
11307 
11308 ulog_define_ex( kDNSSDUtilIdentifier, MDNSReplier, kLogLevelInfo, kLogFlags_None, "MDNSReplier", NULL );
11309 #define mr_ulog( LEVEL, ... )		ulog( &log_category_from_name( MDNSReplier ), (LEVEL), __VA_ARGS__ )
11310 
MDNSReplierCmd(void)11311 static void	MDNSReplierCmd( void )
11312 {
11313 	OSStatus					err;
11314 	MDNSReplierContext *		context;
11315 	SocketRef					sockV4	= kInvalidSocketRef;
11316 	SocketRef					sockV6	= kInvalidSocketRef;
11317 	const char *				ifname;
11318 	size_t						len;
11319 	uint8_t						name[ 1 + kDomainLabelLengthMax + 1 ];
11320 	char						ifnameBuf[ IF_NAMESIZE + 1 ];
11321 
11322 	err = CheckIntegerArgument( gMDNSReplier_MaxInstanceCount, "max instance count", 1, UINT16_MAX );
11323 	require_noerr_quiet( err, exit );
11324 
11325 	err = CheckIntegerArgument( gMDNSReplier_RecordCountA, "A record count", 0, 255 );
11326 	require_noerr_quiet( err, exit );
11327 
11328 	err = CheckIntegerArgument( gMDNSReplier_RecordCountAAAA, "AAAA record count", 0, 255 );
11329 	require_noerr_quiet( err, exit );
11330 
11331 	err = CheckDoubleArgument( gMDNSReplier_UnicastDropRate, "unicast drop rate", 0.0, 1.0 );
11332 	require_noerr_quiet( err, exit );
11333 
11334 	err = CheckDoubleArgument( gMDNSReplier_MulticastDropRate, "multicast drop rate", 0.0, 1.0 );
11335 	require_noerr_quiet( err, exit );
11336 
11337 	err = CheckIntegerArgument( gMDNSReplier_MaxDropCount, "drop count", 0, 255 );
11338 	require_noerr_quiet( err, exit );
11339 
11340 	if( gMDNSReplier_Foreground )
11341 	{
11342 		LogControl( "MDNSReplier:output=file;stdout,MDNSReplier:flags=time;prefix" );
11343 	}
11344 
11345 	context = (MDNSReplierContext *) calloc( 1, sizeof( *context ) );
11346 	require_action( context, exit, err = kNoMemoryErr );
11347 
11348 	context->maxInstanceCount	= (unsigned int) gMDNSReplier_MaxInstanceCount;
11349 	context->recordCountA		= (unsigned int) gMDNSReplier_RecordCountA;
11350 	context->recordCountAAAA	= (unsigned int) gMDNSReplier_RecordCountAAAA;
11351 	context->maxDropCount		= (unsigned int) gMDNSReplier_MaxDropCount;
11352 	context->ucastDropRate		= gMDNSReplier_UnicastDropRate;
11353 	context->mcastDropRate		= gMDNSReplier_MulticastDropRate;
11354 	context->noAdditionals		= gMDNSReplier_NoAdditionals ? true : false;
11355 	context->useIPv4			= ( gMDNSReplier_UseIPv4 || !gMDNSReplier_UseIPv6 ) ? true : false;
11356 	context->useIPv6			= ( gMDNSReplier_UseIPv6 || !gMDNSReplier_UseIPv4 ) ? true : false;
11357 	context->bitmapCount		= ( context->maxInstanceCount + 63 ) / 64;
11358 
11359 #if( TARGET_OS_DARWIN )
11360 	if( gMDNSReplier_FollowPID )
11361 	{
11362 		context->followPID = _StringToPID( gMDNSReplier_FollowPID, &err );
11363 		if( err || ( context->followPID < 0 ) )
11364 		{
11365 			FPrintF( stderr, "error: Invalid follow PID: %s\n", gMDNSReplier_FollowPID );
11366 			goto exit;
11367 		}
11368 
11369 		err = DispatchProcessMonitorCreate( context->followPID, DISPATCH_PROC_EXIT, dispatch_get_main_queue(),
11370 			_MDNSReplierFollowedProcessHandler, NULL, context, &context->processMonitor );
11371 		require_noerr( err, exit );
11372 		dispatch_resume( context->processMonitor );
11373 	}
11374 	else
11375 	{
11376 		context->followPID = -1;
11377 	}
11378 #endif
11379 
11380 	if( context->maxDropCount > 0 )
11381 	{
11382 		context->dropCounters = (uint8_t *) calloc( context->maxInstanceCount, sizeof( *context->dropCounters ) );
11383 		require_action( context->dropCounters, exit, err = kNoMemoryErr );
11384 	}
11385 
11386 	context->bitmaps = (uint64_t *) calloc( context->bitmapCount, sizeof( *context->bitmaps ) );
11387 	require_action( context->bitmaps, exit, err = kNoMemoryErr );
11388 
11389 	// Create the base hostname label.
11390 
11391 	len = strlen( gMDNSReplier_Hostname );
11392 	if( context->maxInstanceCount > 1 )
11393 	{
11394 		unsigned int		maxInstanceCount, digitCount;
11395 
11396 		// When there's more than one instance, extra bytes are needed to append " (<instance index>)" or
11397 		// "-<instance index>" to the base hostname.
11398 
11399 		maxInstanceCount = context->maxInstanceCount;
11400 		for( digitCount = 0; maxInstanceCount > 0; ++digitCount ) maxInstanceCount /= 10;
11401 		len += ( 3 + digitCount );
11402 	}
11403 
11404 	if( len <= kDomainLabelLengthMax )
11405 	{
11406 		uint8_t *		dst = &name[ 1 ];
11407 		uint8_t *		lim = &name[ countof( name ) ];
11408 
11409 		SNPrintF_Add( (char **) &dst, (char *) lim, "%s", gMDNSReplier_Hostname );
11410 		name[ 0 ] = (uint8_t)( dst - &name[ 1 ] );
11411 
11412 		err = DomainNameDupLower( name, &context->hostname, NULL );
11413 		require_noerr( err, exit );
11414 	}
11415 	else
11416 	{
11417 		FPrintF( stderr, "error: Base name \"%s\" is too long for max instance count of %u.\n",
11418 			gMDNSReplier_Hostname, context->maxInstanceCount );
11419 		goto exit;
11420 	}
11421 
11422 	// Create the service label.
11423 
11424 	len = strlen( gMDNSReplier_ServiceTypeTag ) + 3;	// We need three extra bytes for the service type prefix "_t-".
11425 	if( len <= kDomainLabelLengthMax )
11426 	{
11427 		uint8_t *		dst = &name[ 1 ];
11428 		uint8_t *		lim = &name[ countof( name ) ];
11429 
11430 		SNPrintF_Add( (char **) &dst, (char *) lim, "_t-%s", gMDNSReplier_ServiceTypeTag );
11431 		name[ 0 ] = (uint8_t)( dst - &name[ 1 ] );
11432 
11433 		err = DomainNameDupLower( name, &context->serviceLabel, NULL );
11434 		require_noerr( err, exit );
11435 	}
11436 	else
11437 	{
11438 		FPrintF( stderr, "error: Service type tag is too long.\n" );
11439 		goto exit;
11440 	}
11441 
11442 	err = InterfaceIndexFromArgString( gInterface, &context->ifIndex );
11443 	require_noerr_quiet( err, exit );
11444 
11445 	ifname = if_indextoname( context->ifIndex, ifnameBuf );
11446 	require_action( ifname, exit, err = kNameErr );
11447 
11448 	// Set up IPv4 socket.
11449 
11450 	if( context->useIPv4 )
11451 	{
11452 		err = CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort, ifname, context->ifIndex, true, NULL, &sockV4 );
11453 		require_noerr( err, exit );
11454 	}
11455 
11456 	// Set up IPv6 socket.
11457 
11458 	if( context->useIPv6 )
11459 	{
11460 		err = CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort, ifname, context->ifIndex, true, NULL, &sockV6 );
11461 		require_noerr( err, exit );
11462 	}
11463 
11464 	// Create dispatch read sources for socket(s).
11465 
11466 	if( IsValidSocket( sockV4 ) )
11467 	{
11468 		SocketContext *		sockCtx;
11469 
11470 		sockCtx = SocketContextCreate( sockV4, context, &err );
11471 		require_noerr( err, exit );
11472 		sockV4 = kInvalidSocketRef;
11473 
11474 		err = DispatchReadSourceCreate( sockCtx->sock, NULL, _MDNSReplierReadHandler, SocketContextCancelHandler, sockCtx,
11475 			&context->readSourceV4 );
11476 		if( err ) ForgetSocketContext( &sockCtx );
11477 		require_noerr( err, exit );
11478 
11479 		dispatch_resume( context->readSourceV4 );
11480 	}
11481 
11482 	if( IsValidSocket( sockV6 ) )
11483 	{
11484 		SocketContext *		sockCtx;
11485 
11486 		sockCtx = SocketContextCreate( sockV6, context, &err );
11487 		require_noerr( err, exit );
11488 		sockV6 = kInvalidSocketRef;
11489 
11490 		err = DispatchReadSourceCreate( sockCtx->sock, NULL, _MDNSReplierReadHandler, SocketContextCancelHandler, sockCtx,
11491 			&context->readSourceV6 );
11492 		if( err ) ForgetSocketContext( &sockCtx );
11493 		require_noerr( err, exit );
11494 
11495 		dispatch_resume( context->readSourceV6 );
11496 	}
11497 
11498 	dispatch_main();
11499 
11500 exit:
11501 	ForgetSocket( &sockV4 );
11502 	ForgetSocket( &sockV6 );
11503 	exit( 1 );
11504 }
11505 
11506 #if( TARGET_OS_DARWIN )
11507 //===========================================================================================================================
11508 //	_MDNSReplierFollowedProcessHandler
11509 //===========================================================================================================================
11510 
_MDNSReplierFollowedProcessHandler(void * inContext)11511 static void	_MDNSReplierFollowedProcessHandler( void *inContext )
11512 {
11513 	MDNSReplierContext * const		context = (MDNSReplierContext *) inContext;
11514 
11515 	if( dispatch_source_get_data( context->processMonitor ) & DISPATCH_PROC_EXIT )
11516 	{
11517 		mr_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited.\n", (int64_t) context->followPID );
11518 		exit( 0 );
11519 	}
11520 }
11521 #endif
11522 
11523 //===========================================================================================================================
11524 //	_MDNSReplierReadHandler
11525 //===========================================================================================================================
11526 
11527 #define ShouldDrop( P )		( ( (P) > 0.0 ) && ( ( (P) >= 1.0 ) || RandomlyTrue( P ) ) )
11528 
_MDNSReplierReadHandler(void * inContext)11529 static void	_MDNSReplierReadHandler( void *inContext )
11530 {
11531 	OSStatus						err;
11532 	SocketContext * const			sockCtx = (SocketContext *) inContext;
11533 	MDNSReplierContext * const		context = (MDNSReplierContext *) sockCtx->userContext;
11534 	size_t							msgLen;
11535 	sockaddr_ip						sender;
11536 	const DNSHeader *				hdr;
11537 	unsigned int					flags, questionCount, i, j;
11538 	const uint8_t *					ptr;
11539 	int								drop, isMetaQuery;
11540 
11541 	err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &sender, sizeof( sender ),
11542 		NULL, NULL, NULL, NULL );
11543 	require_noerr( err, exit );
11544 
11545 	if( msgLen < kDNSHeaderLength )
11546 	{
11547 		mr_ulog( kLogLevelInfo, "Message is too small (%zu < %d).\n", msgLen, kDNSHeaderLength );
11548 		goto exit;
11549 	}
11550 
11551 	// Perform header field checks.
11552 	// The message ID and most flag bits are silently ignored (see <https://tools.ietf.org/html/rfc6762#section-18>).
11553 
11554 	hdr = (DNSHeader *) context->msgBuf;
11555 	flags = DNSHeaderGetFlags( hdr );
11556 	require_quiet( ( flags & kDNSHeaderFlag_Response ) == 0, exit );		// Reject responses.
11557 	require_quiet( DNSFlagsGetOpCode( flags ) == kDNSOpCode_Query, exit );	// Reject opcodes other than standard query.
11558 	require_quiet( DNSFlagsGetRCode( flags )  == kDNSRCode_NoError, exit );	// Reject non-zero rcodes.
11559 
11560 	drop = ( !context->maxDropCount && ShouldDrop( context->mcastDropRate ) ) ? true : false;
11561 
11562 	mr_ulog( kLogLevelInfo, "Received %zu byte message from %##a%?s -- %#.1{du:dnsmsg}\n",
11563 		msgLen, &sender, drop, " (dropping)", context->msgBuf, msgLen );
11564 
11565 	// Based on the QNAMEs in the query message, determine from which sets of records we may possibly need answers.
11566 
11567 	questionCount = DNSHeaderGetQuestionCount( hdr );
11568 	require_quiet( questionCount > 0, exit );
11569 
11570 	memset( context->bitmaps, 0, context->bitmapCount * sizeof_element( context->bitmaps ) );
11571 
11572 	isMetaQuery = false;
11573 	ptr = (const uint8_t *) &hdr[ 1 ];
11574 	for( i = 0; i < questionCount; ++i )
11575 	{
11576 		unsigned int		count, index;
11577 		uint16_t			qtype, qclass;
11578 		uint8_t				qname[ kDomainNameLengthMax ];
11579 
11580 		err = DNSMessageExtractQuestion( context->msgBuf, msgLen, ptr, qname, &qtype, &qclass, &ptr );
11581 		require_noerr_quiet( err, exit );
11582 
11583 		if( ( qclass & ~kMDNSClassUnicastResponseBit ) != kDNSServiceClass_IN ) continue;
11584 
11585 		if( _MDNSReplierHostnameMatch( context, qname, &index ) ||
11586 			_MDNSReplierServiceInstanceNameMatch( context, qname, &index, NULL, NULL ) )
11587 		{
11588 			if( ( index >= 1 ) && ( index <= context->maxInstanceCount ) )
11589 			{
11590 				context->bitmaps[ ( index - 1 ) / 64 ] |= ( UINT64_C( 1 ) << ( ( index - 1 ) % 64 ) );
11591 			}
11592 		}
11593 		else if( _MDNSReplierServiceTypeMatch( context, qname, NULL, &count ) )
11594 		{
11595 			if( ( count >= 1 ) && ( count <= context->maxInstanceCount ) )
11596 			{
11597 				for( j = 0; j < (unsigned int) context->bitmapCount; ++j )
11598 				{
11599 					if( count < 64 )
11600 					{
11601 						context->bitmaps[ j ] |= ( ( UINT64_C( 1 ) << count ) - 1 );
11602 						break;
11603 					}
11604 					else
11605 					{
11606 						context->bitmaps[ j ] = ~UINT64_C( 0 );
11607 						count -= 64;
11608 					}
11609 				}
11610 			}
11611 		}
11612 		else if( _MDNSReplierAboutRecordNameMatch( context, qname ) )
11613 		{
11614 			isMetaQuery = true;
11615 		}
11616 	}
11617 
11618 	// Attempt to answer the query message using selected record sets.
11619 
11620 	if( isMetaQuery )
11621 	{
11622 		err = _MDNSReplierAnswerQuery( context, context->msgBuf, msgLen, &sender, sockCtx->sock, 0 );
11623 		check_noerr( err );
11624 	}
11625 	if( drop ) goto exit;
11626 
11627 	for( i = 0; i < context->bitmapCount; ++i )
11628 	{
11629 		for( j = 0; ( context->bitmaps[ i ] != 0 ) && ( j < 64 ); ++j )
11630 		{
11631 			const uint64_t		bitmask = UINT64_C( 1 ) << j;
11632 
11633 			if( context->bitmaps[ i ] & bitmask )
11634 			{
11635 				context->bitmaps[ i ] &= ~bitmask;
11636 
11637 				err = _MDNSReplierAnswerQuery( context, context->msgBuf, msgLen, &sender, sockCtx->sock,
11638 					( i * 64 ) + j + 1 );
11639 				check_noerr( err );
11640 			}
11641 		}
11642 	}
11643 
11644 exit:
11645 	return;
11646 }
11647 
11648 //===========================================================================================================================
11649 //	_MDNSReplierAnswerQuery
11650 //===========================================================================================================================
11651 
11652 static OSStatus
_MDNSReplierAnswerQuery(MDNSReplierContext * inContext,const uint8_t * inQueryPtr,size_t inQueryLen,sockaddr_ip * inSender,SocketRef inSock,unsigned int inIndex)11653 	_MDNSReplierAnswerQuery(
11654 		MDNSReplierContext *	inContext,
11655 		const uint8_t *			inQueryPtr,
11656 		size_t					inQueryLen,
11657 		sockaddr_ip *			inSender,
11658 		SocketRef				inSock,
11659 		unsigned int			inIndex )
11660 {
11661 	OSStatus				err;
11662 	const DNSHeader *		hdr;
11663 	const uint8_t *			ptr;
11664 	unsigned int			questionCount, answerCount, i;
11665 	MRResourceRecord *		ucastAnswerList = NULL;
11666 	MRResourceRecord *		mcastAnswerList = NULL;
11667 
11668 	require_action( inIndex <= inContext->maxInstanceCount, exit, err = kRangeErr );
11669 
11670 	// Get answers for questions.
11671 
11672 	check( inQueryLen >= kDNSHeaderLength );
11673 	hdr = (const DNSHeader *) inQueryPtr;
11674 	questionCount = DNSHeaderGetQuestionCount( hdr );
11675 
11676 	ptr = (const uint8_t *) &hdr[ 1 ];
11677 	for( i = 0; i < questionCount; ++i )
11678 	{
11679 		MRResourceRecord **		answerListPtr;
11680 		uint16_t				qtype, qclass;
11681 		uint8_t					qname[ kDomainNameLengthMax ];
11682 
11683 		err = DNSMessageExtractQuestion( inQueryPtr, inQueryLen, ptr, qname, &qtype, &qclass, &ptr );
11684 		require_noerr_quiet( err, exit );
11685 
11686 		if( qclass & kMDNSClassUnicastResponseBit )
11687 		{
11688 			qclass &= ~kMDNSClassUnicastResponseBit;
11689 			answerListPtr = &ucastAnswerList;
11690 		}
11691 		else
11692 		{
11693 			answerListPtr = &mcastAnswerList;
11694 		}
11695 
11696 		err = _MDNSReplierAnswerListAdd( inContext, answerListPtr, inIndex, qname, qtype, qclass );
11697 		require_noerr( err, exit );
11698 	}
11699 	require_action_quiet( mcastAnswerList || ucastAnswerList, exit, err = kNoErr );
11700 
11701 	// Suppress known answers.
11702 	// Records in the Answer section of the query message are known answers, so remove them from the answer lists.
11703 	// See <https://tools.ietf.org/html/rfc6762#section-7.1>.
11704 
11705 	answerCount = DNSHeaderGetAnswerCount( hdr );
11706 	for( i = 0; i < answerCount; ++i )
11707 	{
11708 		const uint8_t *		rdataPtr;
11709 		const uint8_t *		recordPtr;
11710 		uint16_t			type, class;
11711 		uint8_t				name[ kDomainNameLengthMax ];
11712 		uint8_t				instance[ kDomainNameLengthMax ];
11713 
11714 		recordPtr = ptr;
11715 		err = DNSMessageExtractRecord( inQueryPtr, inQueryLen, ptr, NULL, &type, &class, NULL, NULL, NULL, &ptr );
11716 		require_noerr_quiet( err, exit );
11717 
11718 		if( ( type != kDNSServiceType_PTR ) || ( class != kDNSServiceClass_IN ) ) continue;
11719 
11720 		err = DNSMessageExtractRecord( inQueryPtr, inQueryLen, recordPtr, name, NULL, NULL, NULL, &rdataPtr, NULL, NULL );
11721 		require_noerr( err, exit );
11722 
11723 		err = DNSMessageExtractDomainName( inQueryPtr, inQueryLen, rdataPtr, instance, NULL );
11724 		require_noerr_quiet( err, exit );
11725 
11726 		if( ucastAnswerList ) _MDNSReplierAnswerListRemovePTR( &ucastAnswerList, name, instance );
11727 		if( mcastAnswerList ) _MDNSReplierAnswerListRemovePTR( &mcastAnswerList, name, instance );
11728 	}
11729 	require_action_quiet( mcastAnswerList || ucastAnswerList, exit, err = kNoErr );
11730 
11731 	// Send or drop responses.
11732 
11733 	if( ucastAnswerList )
11734 	{
11735 		err = _MDNSReplierSendOrDropResponse( inContext, ucastAnswerList, inSender, inSock, inIndex, true );
11736 		require_noerr( err, exit );
11737 	}
11738 
11739 	if( mcastAnswerList )
11740 	{
11741 		err = _MDNSReplierSendOrDropResponse( inContext, mcastAnswerList, inSender, inSock, inIndex, false );
11742 		require_noerr( err, exit );
11743 	}
11744 	err = kNoErr;
11745 
11746 exit:
11747 	_MRResourceRecordFreeList( ucastAnswerList );
11748 	_MRResourceRecordFreeList( mcastAnswerList );
11749 	return( err );
11750 }
11751 
11752 //===========================================================================================================================
11753 //	_MDNSReplierAnswerListAdd
11754 //===========================================================================================================================
11755 
11756 static OSStatus
_MDNSReplierAnswerListAdd(MDNSReplierContext * inContext,MRResourceRecord ** inAnswerList,unsigned int inIndex,const uint8_t * inName,unsigned int inType,unsigned int inClass)11757 	_MDNSReplierAnswerListAdd(
11758 		MDNSReplierContext *	inContext,
11759 		MRResourceRecord **		inAnswerList,
11760 		unsigned int			inIndex,
11761 		const uint8_t *			inName,
11762 		unsigned int			inType,
11763 		unsigned int			inClass )
11764 {
11765 	OSStatus					err;
11766 	uint8_t *					recordName	= NULL;
11767 	uint8_t *					rdataPtr	= NULL;
11768 	size_t						rdataLen;
11769 	MRResourceRecord *			answer;
11770 	MRResourceRecord **			answerPtr;
11771 	const uint8_t * const		hostname	= inContext->hostname;
11772 	unsigned int				i;
11773 	uint32_t					index;
11774 	unsigned int				count, txtSize;
11775 
11776 	require_action( inIndex <= inContext->maxInstanceCount, exit, err = kRangeErr );
11777 	require_action_quiet( inClass == kDNSServiceClass_IN, exit, err = kNoErr );
11778 
11779 	for( answerPtr = inAnswerList; ( answer = *answerPtr ) != NULL; answerPtr = &answer->next )
11780 	{
11781 		if( ( answer->type == inType ) && DomainNameEqual( answer->name, inName ) )
11782 		{
11783 			err = kNoErr;
11784 			goto exit;
11785 		}
11786 	}
11787 
11788 	// Index 0 is reserved for answering queries about the mdnsreplier, while all other index values up to the maximum
11789 	// instance count are for answering queries about service instances.
11790 
11791 	if( inIndex == 0 )
11792 	{
11793 		if( _MDNSReplierAboutRecordNameMatch( inContext, inName ) )
11794 		{
11795 			int		listHasTXT = false;
11796 
11797 			if( inType == kDNSServiceType_ANY )
11798 			{
11799 				for( answer = *inAnswerList; answer; answer = answer->next )
11800 				{
11801 					if( ( answer->type == kDNSServiceType_TXT ) && DomainNameEqual( answer->name, inName ) )
11802 					{
11803 						listHasTXT = true;
11804 						break;
11805 					}
11806 				}
11807 			}
11808 
11809 			if( ( inType == kDNSServiceType_TXT ) || ( ( inType == kDNSServiceType_ANY ) && !listHasTXT ) )
11810 			{
11811 				err = DomainNameDupLower( inName, &recordName, NULL );
11812 				require_noerr( err, exit );
11813 
11814 				err = CreateTXTRecordDataFromString( "ready=yes", ',', &rdataPtr, &rdataLen );
11815 				require_noerr( err, exit );
11816 
11817 				err = _MRResourceRecordCreate( recordName, kDNSServiceType_TXT, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
11818 					(uint16_t) rdataLen, rdataPtr, &answer );
11819 				require_noerr( err, exit );
11820 				recordName	= NULL;
11821 				rdataPtr	= NULL;
11822 
11823 				*answerPtr = answer;
11824 			}
11825 			else if( inType == kDNSServiceType_NSEC )
11826 			{
11827 				err = DomainNameDupLower( inName, &recordName, NULL );
11828 				require_noerr( err, exit );
11829 
11830 				err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_TXT );
11831 				require_noerr( err, exit );
11832 
11833 				err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
11834 					(uint16_t) rdataLen, rdataPtr, &answer );
11835 				require_noerr( err, exit );
11836 				recordName	= NULL;
11837 				rdataPtr	= NULL;
11838 
11839 				*answerPtr = answer;
11840 			}
11841 		}
11842 	}
11843 	else if( _MDNSReplierHostnameMatch( inContext, inName, &index ) && ( index == inIndex ) )
11844 	{
11845 		int		listHasA	= false;
11846 		int		listHasAAAA	= false;
11847 
11848 		if( inType == kDNSServiceType_ANY )
11849 		{
11850 			for( answer = *inAnswerList; answer; answer = answer->next )
11851 			{
11852 				if( answer->type == kDNSServiceType_A )
11853 				{
11854 					if( !listHasA && DomainNameEqual( answer->name, inName ) ) listHasA = true;
11855 				}
11856 				else if( answer->type == kDNSServiceType_AAAA )
11857 				{
11858 					if( !listHasAAAA && DomainNameEqual( answer->name, inName ) ) listHasAAAA = true;
11859 				}
11860 				if( listHasA && listHasAAAA ) break;
11861 			}
11862 		}
11863 
11864 		if( ( inType == kDNSServiceType_A ) || ( ( inType == kDNSServiceType_ANY ) && !listHasA ) )
11865 		{
11866 			for( i = 1; i <= inContext->recordCountA; ++i )
11867 			{
11868 				err = DomainNameDupLower( inName, &recordName, NULL );
11869 				require_noerr( err, exit );
11870 
11871 				rdataLen = 4;
11872 				rdataPtr = (uint8_t *) malloc( rdataLen );
11873 				require_action( rdataPtr, exit, err = kNoMemoryErr );
11874 
11875 				rdataPtr[ 0 ] = 0;
11876 				WriteBig16( &rdataPtr[ 1 ], inIndex );
11877 				rdataPtr[ 3 ] = (uint8_t) i;
11878 
11879 				err = _MRResourceRecordCreate( recordName, kDNSServiceType_A, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
11880 					(uint16_t) rdataLen, rdataPtr, &answer );
11881 				require_noerr( err, exit );
11882 				recordName	= NULL;
11883 				rdataPtr	= NULL;
11884 
11885 				*answerPtr = answer;
11886 				 answerPtr = &answer->next;
11887 			}
11888 		}
11889 
11890 		if( ( inType == kDNSServiceType_AAAA ) || ( ( inType == kDNSServiceType_ANY ) && !listHasAAAA ) )
11891 		{
11892 			for( i = 1; i <= inContext->recordCountAAAA; ++i )
11893 			{
11894 				const uint8_t		( *baseAddr )[ 16 ];
11895 
11896 				err = DomainNameDupLower( inName, &recordName, NULL );
11897 				require_noerr( err, exit );
11898 
11899 				rdataLen = 16;
11900 				baseAddr = ( i == 1 ) ? &kMDNSReplierLinkLocalBaseAddrV6 : &kMDNSReplierBaseAddrV6;
11901 				rdataPtr = (uint8_t *) _memdup( baseAddr, rdataLen );
11902 				require_action( rdataPtr, exit, err = kNoMemoryErr );
11903 
11904 				WriteBig16( &rdataPtr[ 12 ], inIndex );
11905 				rdataPtr[ 15 ] = (uint8_t) i;
11906 
11907 				err = _MRResourceRecordCreate( recordName, kDNSServiceType_AAAA, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
11908 					(uint16_t) rdataLen, rdataPtr, &answer );
11909 				require_noerr( err, exit );
11910 				recordName	= NULL;
11911 				rdataPtr	= NULL;
11912 
11913 				*answerPtr = answer;
11914 				 answerPtr = &answer->next;
11915 			}
11916 		}
11917 		else if( inType == kDNSServiceType_NSEC )
11918 		{
11919 			err = DomainNameDupLower( inName, &recordName, NULL );
11920 			require_noerr( err, exit );
11921 
11922 			if( ( inContext->recordCountA > 0 ) && ( inContext->recordCountAAAA > 0 ) )
11923 			{
11924 				err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 2, kDNSServiceType_A, kDNSServiceType_AAAA );
11925 				require_noerr( err, exit );
11926 			}
11927 			else if( inContext->recordCountA > 0 )
11928 			{
11929 				err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_A );
11930 				require_noerr( err, exit );
11931 			}
11932 			else if( inContext->recordCountAAAA > 0 )
11933 			{
11934 				err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 1, kDNSServiceType_AAAA );
11935 				require_noerr( err, exit );
11936 			}
11937 			else
11938 			{
11939 				err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 0 );
11940 				require_noerr( err, exit );
11941 			}
11942 
11943 			err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
11944 				(uint16_t) rdataLen, rdataPtr, &answer );
11945 			require_noerr( err, exit );
11946 			recordName	= NULL;
11947 			rdataPtr	= NULL;
11948 
11949 			*answerPtr = answer;
11950 		}
11951 	}
11952 	else if( _MDNSReplierServiceTypeMatch( inContext, inName, NULL, &count ) && ( count >= inIndex ) )
11953 	{
11954 		int		listHasPTR = false;
11955 
11956 		if( inType == kDNSServiceType_ANY )
11957 		{
11958 			for( answer = *inAnswerList; answer; answer = answer->next )
11959 			{
11960 				if( ( answer->type == kDNSServiceType_PTR ) && DomainNameEqual( answer->name, inName ) )
11961 				{
11962 					listHasPTR = true;
11963 					break;
11964 				}
11965 			}
11966 		}
11967 
11968 		if( ( inType == kDNSServiceType_PTR ) || ( ( inType == kDNSServiceType_ANY ) && !listHasPTR ) )
11969 		{
11970 			size_t				recordNameLen;
11971 			uint8_t *			ptr;
11972 			uint8_t *			lim;
11973 
11974 			err = DomainNameDupLower( inName, &recordName, &recordNameLen );
11975 			require_noerr( err, exit );
11976 
11977 			rdataLen = 1 + hostname[ 0 ] + 10 + recordNameLen;
11978 			rdataPtr = (uint8_t *) malloc( rdataLen );
11979 			require_action( rdataPtr, exit, err = kNoMemoryErr );
11980 
11981 			lim = &rdataPtr[ rdataLen ];
11982 
11983 			ptr = &rdataPtr[ 1 ];
11984 			memcpy( ptr, &hostname[ 1 ], hostname[ 0 ] );
11985 			ptr += hostname[ 0 ];
11986 			if( inIndex != 1 ) SNPrintF_Add( (char **) &ptr, (char *) lim, " (%u)", inIndex );
11987 			rdataPtr[ 0 ] = (uint8_t)( ptr - &rdataPtr[ 1 ] );
11988 
11989 			check( (size_t)( lim - ptr ) >= recordNameLen );
11990 			memcpy( ptr, recordName, recordNameLen );
11991 			ptr += recordNameLen;
11992 
11993 			rdataLen = (size_t)( ptr - rdataPtr );
11994 
11995 			err = _MRResourceRecordCreate( recordName, kDNSServiceType_PTR, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
11996 				(uint16_t) rdataLen, rdataPtr, &answer );
11997 			require_noerr( err, exit );
11998 			recordName	= NULL;
11999 			rdataPtr	= NULL;
12000 
12001 			*answerPtr = answer;
12002 		}
12003 	}
12004 	else if( _MDNSReplierServiceInstanceNameMatch( inContext, inName, &index, &txtSize, &count ) &&
12005 		( index == inIndex ) && ( count >= inIndex ) )
12006 	{
12007 		int		listHasSRV = false;
12008 		int		listHasTXT = false;
12009 
12010 		if( inType == kDNSServiceType_ANY )
12011 		{
12012 			for( answer = *inAnswerList; answer; answer = answer->next )
12013 			{
12014 				if( answer->type == kDNSServiceType_SRV )
12015 				{
12016 					if( !listHasSRV && DomainNameEqual( answer->name, inName ) ) listHasSRV = true;
12017 				}
12018 				else if( answer->type == kDNSServiceType_TXT )
12019 				{
12020 					if( !listHasTXT && DomainNameEqual( answer->name, inName ) ) listHasTXT = true;
12021 				}
12022 				if( listHasSRV && listHasTXT ) break;
12023 			}
12024 		}
12025 
12026 		if( ( inType == kDNSServiceType_SRV ) || ( ( inType == kDNSServiceType_ANY ) && !listHasSRV ) )
12027 		{
12028 			dns_fixed_fields_srv *		fields;
12029 			uint8_t *					ptr;
12030 			uint8_t *					lim;
12031 			uint8_t *					targetPtr;
12032 
12033 			err = DomainNameDupLower( inName, &recordName, NULL );
12034 			require_noerr( err, exit );
12035 
12036 			rdataLen = sizeof( dns_fixed_fields_srv ) + 1 + hostname[ 0 ] + 10 + kLocalNameLen;
12037 			rdataPtr = (uint8_t *) malloc( rdataLen );
12038 			require_action( rdataPtr, exit, err = kNoMemoryErr );
12039 
12040 			lim = &rdataPtr[ rdataLen ];
12041 
12042 			fields = (dns_fixed_fields_srv *) rdataPtr;
12043 			dns_fixed_fields_srv_init( fields, 0, 0, (uint16_t)( kMDNSReplierPortBase + txtSize ) );
12044 
12045 			targetPtr = (uint8_t *) &fields[ 1 ];
12046 
12047 			ptr = &targetPtr[ 1 ];
12048 			memcpy( ptr, &hostname[ 1 ], hostname[ 0 ] );
12049 			ptr += hostname[ 0 ];
12050 			if( inIndex != 1 ) SNPrintF_Add( (char **) &ptr, (char *) lim, "-%u", inIndex );
12051 			targetPtr[ 0 ] = (uint8_t)( ptr - &targetPtr[ 1 ] );
12052 
12053 			check( (size_t)( lim - ptr ) >= kLocalNameLen );
12054 			memcpy( ptr, kLocalName, kLocalNameLen );
12055 			ptr += kLocalNameLen;
12056 
12057 			rdataLen = (size_t)( ptr - rdataPtr );
12058 
12059 			err = _MRResourceRecordCreate( recordName, kDNSServiceType_SRV, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
12060 				(uint16_t) rdataLen, rdataPtr, &answer );
12061 			require_noerr( err, exit );
12062 			recordName	= NULL;
12063 			rdataPtr	= NULL;
12064 
12065 			*answerPtr = answer;
12066 			 answerPtr = &answer->next;
12067 		}
12068 
12069 		if( ( inType == kDNSServiceType_TXT ) || ( ( inType == kDNSServiceType_ANY ) && !listHasTXT ) )
12070 		{
12071 			err = DomainNameDupLower( inName, &recordName, NULL );
12072 			require_noerr( err, exit );
12073 
12074 			rdataLen = txtSize;
12075 			err = _MDNSReplierCreateTXTRecord( inName, rdataLen, &rdataPtr );
12076 			require_noerr( err, exit );
12077 
12078 			err = _MRResourceRecordCreate( recordName, kDNSServiceType_TXT, kDNSServiceClass_IN, kMDNSRecordTTL_Other,
12079 				(uint16_t) rdataLen, rdataPtr, &answer );
12080 			require_noerr( err, exit );
12081 			recordName	= NULL;
12082 			rdataPtr	= NULL;
12083 
12084 			*answerPtr = answer;
12085 		}
12086 		else if( inType == kDNSServiceType_NSEC )
12087 		{
12088 			err = DomainNameDupLower( inName, &recordName, NULL );
12089 			require_noerr( err, exit );
12090 
12091 			err = CreateNSECRecordData( recordName, &rdataPtr, &rdataLen, 2, kDNSServiceType_TXT, kDNSServiceType_SRV );
12092 			require_noerr( err, exit );
12093 
12094 			err = _MRResourceRecordCreate( recordName, kDNSServiceType_NSEC, kDNSServiceClass_IN, kMDNSRecordTTL_Host,
12095 				(uint16_t) rdataLen, rdataPtr, &answer );
12096 			require_noerr( err, exit );
12097 			recordName	= NULL;
12098 			rdataPtr	= NULL;
12099 
12100 			*answerPtr = answer;
12101 		}
12102 	}
12103 	err = kNoErr;
12104 
12105 exit:
12106 	FreeNullSafe( recordName );
12107 	FreeNullSafe( rdataPtr );
12108 	return( err );
12109 }
12110 
12111 //===========================================================================================================================
12112 //	_MDNSReplierAnswerListRemovePTR
12113 //===========================================================================================================================
12114 
12115 static void
_MDNSReplierAnswerListRemovePTR(MRResourceRecord ** inAnswerListPtr,const uint8_t * inName,const uint8_t * inRData)12116 	_MDNSReplierAnswerListRemovePTR(
12117 		MRResourceRecord **	inAnswerListPtr,
12118 		const uint8_t *		inName,
12119 		const uint8_t *		inRData )
12120 {
12121 	MRResourceRecord *		answer;
12122 	MRResourceRecord **		answerPtr;
12123 
12124 	for( answerPtr = inAnswerListPtr; ( answer = *answerPtr ) != NULL; answerPtr = &answer->next )
12125 	{
12126 		if( ( answer->type == kDNSServiceType_PTR ) && ( answer->class == kDNSServiceClass_IN ) &&
12127 			DomainNameEqual( answer->name, inName ) && DomainNameEqual( answer->rdata, inRData ) ) break;
12128 	}
12129 	if( answer )
12130 	{
12131 		*answerPtr = answer->next;
12132 		_MRResourceRecordFree( answer );
12133 	}
12134 }
12135 
12136 //===========================================================================================================================
12137 //	_MDNSReplierSendOrDropResponse
12138 //===========================================================================================================================
12139 
12140 static OSStatus
_MDNSReplierSendOrDropResponse(MDNSReplierContext * inContext,MRResourceRecord * inAnswerList,sockaddr_ip * inQuerier,SocketRef inSock,unsigned int inIndex,Boolean inUnicast)12141 	_MDNSReplierSendOrDropResponse(
12142 		MDNSReplierContext *	inContext,
12143 		MRResourceRecord *		inAnswerList,
12144 		sockaddr_ip *			inQuerier,
12145 		SocketRef				inSock,
12146 		unsigned int			inIndex,
12147 		Boolean					inUnicast )
12148 {
12149 	OSStatus					err;
12150 	uint8_t *					responsePtr	= NULL;
12151 	size_t						responseLen;
12152 	const struct sockaddr *		destAddr;
12153 	ssize_t						n;
12154 	const double				dropRate	= inUnicast ? inContext->ucastDropRate : inContext->mcastDropRate;
12155 	int							drop;
12156 
12157 	check( inIndex <= inContext->maxInstanceCount );
12158 
12159 	// If maxDropCount > 0, then the drop rates apply only to the first maxDropCount responses. Otherwise, all messages are
12160 	// subject to their respective drop rate. Also, responses to queries about mDNS replier itself (indicated by index 0),
12161 	// as opposed to those for service instance records, are never dropped.
12162 
12163 	drop = false;
12164 	if( inIndex > 0 )
12165 	{
12166 		if( inContext->maxDropCount > 0 )
12167 		{
12168 			uint8_t * const		dropCount = &inContext->dropCounters[ inIndex - 1 ];
12169 
12170 			if( *dropCount < inContext->maxDropCount )
12171 			{
12172 				if( ShouldDrop( dropRate ) ) drop = true;
12173 				*dropCount += 1;
12174 			}
12175 		}
12176 		else if( ShouldDrop( dropRate ) )
12177 		{
12178 			drop = true;
12179 		}
12180 	}
12181 
12182 	err = _MDNSReplierCreateResponse( inContext, inAnswerList, inIndex, &responsePtr, &responseLen );
12183 	require_noerr( err, exit );
12184 
12185 	if( inUnicast )
12186 	{
12187 		destAddr = &inQuerier->sa;
12188 	}
12189 	else
12190 	{
12191 		destAddr = ( inQuerier->sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
12192 	}
12193 
12194 	mr_ulog( kLogLevelInfo, "%s %zu byte response to %##a -- %#.1{du:dnsmsg}\n",
12195 		drop ? "Dropping" : "Sending", responseLen, destAddr, responsePtr, responseLen );
12196 
12197 	if( !drop )
12198 	{
12199 		n = sendto( inSock, (char *) responsePtr, responseLen, 0, destAddr, SockAddrGetSize( destAddr ) );
12200 		err = map_socket_value_errno( inSock, n == (ssize_t) responseLen, n );
12201 		require_noerr( err, exit );
12202 	}
12203 
12204 exit:
12205 	FreeNullSafe( responsePtr );
12206 	return( err );
12207 }
12208 
12209 //===========================================================================================================================
12210 //	_MDNSReplierCreateResponse
12211 //===========================================================================================================================
12212 
12213 static OSStatus
_MDNSReplierCreateResponse(MDNSReplierContext * inContext,MRResourceRecord * inAnswerList,unsigned int inIndex,uint8_t ** outResponsePtr,size_t * outResponseLen)12214 	_MDNSReplierCreateResponse(
12215 		MDNSReplierContext *	inContext,
12216 		MRResourceRecord *		inAnswerList,
12217 		unsigned int			inIndex,
12218 		uint8_t **				outResponsePtr,
12219 		size_t *				outResponseLen )
12220 {
12221 	OSStatus				err;
12222 	DataBuffer				responseDB;
12223 	DNSHeader				hdr;
12224 	MRResourceRecord *		answer;
12225 	uint8_t *				responsePtr;
12226 	size_t					responseLen, len;
12227 	unsigned int			answerCount, recordCount;
12228 	MRNameOffsetItem *		nameOffsetList = NULL;
12229 
12230 	DataBuffer_Init( &responseDB, NULL, 0, SIZE_MAX );
12231 
12232 	// The current answers in the answer list will make up the response's Answer Record Section.
12233 
12234 	answerCount = 0;
12235 	for( answer = inAnswerList; answer; answer = answer->next ) { ++answerCount; }
12236 
12237 	// Unless configured not to, add any additional answers to the answer list for the Additional Record Section.
12238 
12239 	if( !inContext->noAdditionals )
12240 	{
12241 		for( answer = inAnswerList; answer; answer = answer->next )
12242 		{
12243 			switch( answer->type )
12244 			{
12245 				case kDNSServiceType_PTR:
12246 					err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->rdata, kDNSServiceType_SRV,
12247 						answer->class );
12248 					require_noerr( err, exit );
12249 
12250 					err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->rdata, kDNSServiceType_TXT,
12251 						answer->class );
12252 					require_noerr( err, exit );
12253 					break;
12254 
12255 				case kDNSServiceType_SRV:
12256 					err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->target, kDNSServiceType_A,
12257 						answer->class );
12258 					require_noerr( err, exit );
12259 
12260 					err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->target, kDNSServiceType_AAAA,
12261 						answer->class );
12262 					require_noerr( err, exit );
12263 
12264 					err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
12265 						answer->class );
12266 					require_noerr( err, exit );
12267 					break;
12268 
12269 				case kDNSServiceType_TXT:
12270 					err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
12271 						answer->class );
12272 					require_noerr( err, exit );
12273 					break;
12274 
12275 				case kDNSServiceType_A:
12276 					err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_AAAA,
12277 						answer->class );
12278 					require_noerr( err, exit );
12279 
12280 					err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
12281 						answer->class );
12282 					require_noerr( err, exit );
12283 					break;
12284 
12285 				case kDNSServiceType_AAAA:
12286 					err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_A,
12287 						answer->class );
12288 					require_noerr( err, exit );
12289 
12290 					err = _MDNSReplierAnswerListAdd( inContext, &inAnswerList, inIndex, answer->name, kDNSServiceType_NSEC,
12291 						answer->class );
12292 					require_noerr( err, exit );
12293 					break;
12294 
12295 				default:
12296 					break;
12297 			}
12298 		}
12299 	}
12300 
12301 	// Append a provisional header to the response message.
12302 
12303 	memset( &hdr, 0, sizeof( hdr ) );
12304 	DNSHeaderSetFlags( &hdr, kDNSHeaderFlag_Response | kDNSHeaderFlag_AuthAnswer );
12305 
12306 	err = DataBuffer_Append( &responseDB, &hdr, sizeof( hdr ) );
12307 	require_noerr( err, exit );
12308 
12309 	// Append answers to response message.
12310 
12311 	responseLen = DataBuffer_GetLen( &responseDB );
12312 	recordCount = 0;
12313 	for( answer = inAnswerList; answer; answer = answer->next )
12314 	{
12315 		dns_fixed_fields_record		fields;
12316 		unsigned int				class;
12317 
12318 		// Append record NAME.
12319 
12320 		err = _MDNSReplierAppendNameToResponse( &responseDB, answer->name, &nameOffsetList );
12321 		require_noerr( err, exit );
12322 
12323 		// Append record TYPE, CLASS, TTL, and provisional RDLENGTH.
12324 
12325 		class = answer->class;
12326 		if( ( answer->type == kDNSServiceType_SRV ) || ( answer->type == kDNSServiceType_TXT )  ||
12327 			( answer->type == kDNSServiceType_A )   || ( answer->type == kDNSServiceType_AAAA ) ||
12328 			( answer->type == kDNSServiceType_NSEC ) )
12329 		{
12330 			class |= kMDNSClassCacheFlushBit;
12331 		}
12332 
12333 		dns_fixed_fields_record_init( &fields, answer->type, (uint16_t) class, answer->ttl, (uint16_t) answer->rdlength );
12334 		err = DataBuffer_Append( &responseDB, &fields, sizeof( fields ) );
12335 		require_noerr( err, exit );
12336 
12337 		// Append record RDATA.
12338 		// The RDATA of PTR, SRV, and NSEC records contain domain names, which are subject to name compression.
12339 
12340 		if( ( answer->type == kDNSServiceType_PTR ) || ( answer->type == kDNSServiceType_SRV ) ||
12341 			( answer->type == kDNSServiceType_NSEC ) )
12342 		{
12343 			size_t				rdlength;
12344 			uint8_t *			rdLengthPtr;
12345 			const size_t		rdLengthOffset	= DataBuffer_GetLen( &responseDB ) - 2;
12346 			const size_t		rdataOffset		= DataBuffer_GetLen( &responseDB );
12347 
12348 			if( answer->type == kDNSServiceType_PTR )
12349 			{
12350 				err = _MDNSReplierAppendNameToResponse( &responseDB, answer->rdata, &nameOffsetList );
12351 				require_noerr( err, exit );
12352 			}
12353 			else if( answer->type == kDNSServiceType_SRV )
12354 			{
12355 				require_fatal( answer->target == &answer->rdata[ 6 ], "Bad SRV record target pointer." );
12356 
12357 				err = DataBuffer_Append( &responseDB, answer->rdata, (size_t)( answer->target - answer->rdata ) );
12358 				require_noerr( err, exit );
12359 
12360 				err = _MDNSReplierAppendNameToResponse( &responseDB, answer->target, &nameOffsetList );
12361 				require_noerr( err, exit );
12362 			}
12363 			else
12364 			{
12365 				const size_t		nameLen = DomainNameLength( answer->rdata );
12366 
12367 				err = _MDNSReplierAppendNameToResponse( &responseDB, answer->rdata, &nameOffsetList );
12368 				require_noerr( err, exit );
12369 
12370 				require_fatal( answer->rdlength > nameLen, "Bad NSEC record data length." );
12371 
12372 				err = DataBuffer_Append( &responseDB, &answer->rdata[ nameLen ], answer->rdlength - nameLen );
12373 				require_noerr( err, exit );
12374 			}
12375 
12376 			// Set the actual RDLENGTH, which may be less than the original due to name compression.
12377 
12378 			rdlength = DataBuffer_GetLen( &responseDB ) - rdataOffset;
12379 			check( rdlength <= UINT16_MAX );
12380 
12381 			rdLengthPtr = DataBuffer_GetPtr( &responseDB ) + rdLengthOffset;
12382 			WriteBig16( rdLengthPtr, rdlength );
12383 		}
12384 		else
12385 		{
12386 			err = DataBuffer_Append( &responseDB, answer->rdata, answer->rdlength );
12387 			require_noerr( err, exit );
12388 		}
12389 
12390 		if( DataBuffer_GetLen( &responseDB ) > kMDNSMessageSizeMax ) break;
12391 		responseLen = DataBuffer_GetLen( &responseDB );
12392 		++recordCount;
12393 	}
12394 
12395 	// Set the response header's Answer and Additional record counts.
12396 	// Note: recordCount may be less than answerCount if including all answerCount records would cause the size of the
12397 	// response message to exceed the maximum mDNS message size.
12398 
12399 	if( recordCount <= answerCount )
12400 	{
12401 		DNSHeaderSetAnswerCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), recordCount );
12402 	}
12403 	else
12404 	{
12405 		DNSHeaderSetAnswerCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), answerCount );
12406 		DNSHeaderSetAdditionalCount( (DNSHeader *) DataBuffer_GetPtr( &responseDB ), recordCount - answerCount );
12407 	}
12408 
12409 	err = DataBuffer_Detach( &responseDB, &responsePtr, &len );
12410 	require_noerr( err, exit );
12411 
12412 	if( outResponsePtr ) *outResponsePtr = responsePtr;
12413 	if( outResponseLen ) *outResponseLen = responseLen;
12414 
12415 exit:
12416 	_MRNameOffsetItemFreeList( nameOffsetList );
12417 	DataBuffer_Free( &responseDB );
12418 	return( err );
12419 }
12420 
12421 //===========================================================================================================================
12422 //	_MDNSReplierAppendNameToResponse
12423 //===========================================================================================================================
12424 
12425 static OSStatus
_MDNSReplierAppendNameToResponse(DataBuffer * inResponse,const uint8_t * inName,MRNameOffsetItem ** inNameOffsetListPtr)12426 	_MDNSReplierAppendNameToResponse(
12427 		DataBuffer *		inResponse,
12428 		const uint8_t *		inName,
12429 		MRNameOffsetItem **	inNameOffsetListPtr )
12430 {
12431 	OSStatus				err;
12432 	const uint8_t *			subname;
12433 	const uint8_t *			limit;
12434 	size_t					nameOffset;
12435 	MRNameOffsetItem *		item;
12436 	uint8_t					compressionPtr[ 2 ];
12437 
12438 	nameOffset = DataBuffer_GetLen( inResponse );
12439 
12440 	// Find the name's longest subname (more accurately, its longest sub-FQDN) in the name compression list.
12441 
12442 	for( subname = inName; subname[ 0 ] != 0; subname += ( 1 + subname[ 0 ] ) )
12443 	{
12444 		for( item = *inNameOffsetListPtr; item; item = item->next )
12445 		{
12446 			if( DomainNameEqual( item->name, subname ) ) break;
12447 		}
12448 
12449 		// If an item was found for this subname, then append a name compression pointer and we're done. Otherwise, append
12450 		// the subname's first label.
12451 
12452 		if( item )
12453 		{
12454 			DNSMessageWriteLabelPointer( compressionPtr, item->offset );
12455 
12456 			err = DataBuffer_Append( inResponse, compressionPtr, sizeof( compressionPtr ) );
12457 			require_noerr( err, exit );
12458 			break;
12459 		}
12460 		else
12461 		{
12462 			err = DataBuffer_Append( inResponse, subname, 1 + subname[ 0 ] );
12463 			require_noerr( err, exit );
12464 		}
12465 	}
12466 
12467 	// If we made it to the root label, then no subname was able to be compressed. All of the name's labels up to the root
12468 	// label were appended to the response message, so a root label is needed to terminate the complete name.
12469 
12470 	if( subname[ 0 ] == 0 )
12471 	{
12472 		err = DataBuffer_Append( inResponse, "", 1 );
12473 		require_noerr( err, exit );
12474 	}
12475 
12476 	// Add subnames that weren't able to be compressed and their offsets to the name compression list.
12477 
12478 	limit = subname;
12479 	for( subname = inName; subname < limit; subname += ( 1 + subname[ 0 ] ) )
12480 	{
12481 		const size_t		subnameOffset = nameOffset + (size_t)( subname - inName );
12482 
12483 		if( subnameOffset > kDNSCompressionOffsetMax ) break;
12484 
12485 		err = _MRNameOffsetItemCreate( subname, (uint16_t) subnameOffset, &item );
12486 		require_noerr( err, exit );
12487 
12488 		item->next = *inNameOffsetListPtr;
12489 		*inNameOffsetListPtr = item;
12490 	}
12491 	err = kNoErr;
12492 
12493 exit:
12494 	return( err );
12495 }
12496 
12497 //===========================================================================================================================
12498 //	_MDNSReplierServiceTypeMatch
12499 //===========================================================================================================================
12500 
12501 static Boolean
_MDNSReplierServiceTypeMatch(const MDNSReplierContext * inContext,const uint8_t * inName,unsigned int * outTXTSize,unsigned int * outCount)12502 	_MDNSReplierServiceTypeMatch(
12503 		const MDNSReplierContext *	inContext,
12504 		const uint8_t *				inName,
12505 		unsigned int *				outTXTSize,
12506 		unsigned int *				outCount )
12507 {
12508 	OSStatus					err;
12509 	const char *				ptr;
12510 	const char *				end;
12511 	uint32_t					txtSize, count;
12512 	const uint8_t * const		serviceLabel	= inContext->serviceLabel;
12513 	int							nameMatches		= false;
12514 
12515 	require_quiet( inName[ 0 ] >= serviceLabel[ 0 ], exit );
12516 	if( _memicmp( &inName[ 1 ], &serviceLabel[ 1 ], serviceLabel[ 0 ] ) != 0 ) goto exit;
12517 
12518 	ptr = (const char *) &inName[ 1 + serviceLabel[ 0 ] ];
12519 	end = (const char *) &inName[ 1 + inName[ 0 ] ];
12520 
12521 	require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
12522 	++ptr;
12523 
12524 	err = DecimalTextToUInt32( ptr, end, &txtSize, &ptr );
12525 	require_noerr_quiet( err, exit );
12526 	require_quiet( txtSize <= UINT16_MAX, exit );
12527 
12528 	require_quiet( ( ptr < end ) && ( *ptr == '-' ), exit );
12529 	++ptr;
12530 
12531 	err = DecimalTextToUInt32( ptr, end, &count, &ptr );
12532 	require_noerr_quiet( err, exit );
12533 	require_quiet( count <= UINT16_MAX, exit );
12534 	require_quiet( ptr == end, exit );
12535 
12536 	if( !DomainNameEqual( (const uint8_t *) ptr, (const uint8_t *) "\x04" "_tcp" "\x05" "local" ) ) goto exit;
12537 	nameMatches = true;
12538 
12539 	if( outTXTSize )	*outTXTSize	= txtSize;
12540 	if( outCount )		*outCount	= count;
12541 
12542 exit:
12543 	return( nameMatches ? true : false );
12544 }
12545 
12546 //===========================================================================================================================
12547 //	_MDNSReplierServiceInstanceNameMatch
12548 //===========================================================================================================================
12549 
12550 static Boolean
_MDNSReplierServiceInstanceNameMatch(const MDNSReplierContext * inContext,const uint8_t * inName,unsigned int * outIndex,unsigned int * outTXTSize,unsigned int * outCount)12551 	_MDNSReplierServiceInstanceNameMatch(
12552 		const MDNSReplierContext *	inContext,
12553 		const uint8_t *				inName,
12554 		unsigned int *				outIndex,
12555 		unsigned int *				outTXTSize,
12556 		unsigned int *				outCount )
12557 {
12558 	OSStatus					err;
12559 	const uint8_t *				ptr;
12560 	const uint8_t *				end;
12561 	uint32_t					index;
12562 	unsigned int				txtSize, count;
12563 	const uint8_t * const		hostname	= inContext->hostname;
12564 	int							nameMatches	= false;
12565 
12566 	require_quiet( inName[ 0 ] >= hostname[ 0 ], exit );
12567 	if( _memicmp( &inName[ 1 ], &hostname[ 1 ], hostname[ 0 ] ) != 0 ) goto exit;
12568 
12569 	ptr = &inName[ 1 + hostname[ 0 ] ];
12570 	end = &inName[ 1 + inName[ 0 ] ];
12571 	if( ptr < end )
12572 	{
12573 		require_quiet( ( end - ptr ) >= 2, exit );
12574 		require_quiet( ( ptr[ 0 ] == ' ' ) && ( ptr[ 1 ] == '(' ), exit );
12575 		ptr += 2;
12576 
12577         err = DecimalTextToUInt32( (const char *) ptr, (const char *) end, &index, (const char **) &ptr );
12578 		require_noerr_quiet( err, exit );
12579 		require_quiet( ( index >= 2 ) && ( index <= UINT16_MAX ), exit );
12580 
12581 		require_quiet( ( ( end - ptr ) == 1 ) && ( *ptr == ')' ), exit );
12582 		++ptr;
12583 	}
12584 	else
12585 	{
12586 		index = 1;
12587 	}
12588 
12589 	if( !_MDNSReplierServiceTypeMatch( inContext, ptr, &txtSize, &count ) ) goto exit;
12590 	nameMatches = true;
12591 
12592 	if( outIndex )		*outIndex	= index;
12593 	if( outTXTSize )	*outTXTSize	= txtSize;
12594 	if( outCount )		*outCount	= count;
12595 
12596 exit:
12597 	return( nameMatches ? true : false );
12598 }
12599 
12600 //===========================================================================================================================
12601 //	_MDNSReplierAboutRecordNameMatch
12602 //===========================================================================================================================
12603 
12604 #define _MemIEqual( PTR1, LEN1, PTR2, LEN2 ) \
12605 	( ( ( LEN1 ) == ( LEN2 ) ) && ( _memicmp( ( PTR1 ), ( PTR2 ), ( LEN1 ) ) == 0 ) )
12606 
_MDNSReplierAboutRecordNameMatch(const MDNSReplierContext * inContext,const uint8_t * inName)12607 static Boolean	_MDNSReplierAboutRecordNameMatch( const MDNSReplierContext *inContext, const uint8_t *inName )
12608 {
12609 	const uint8_t *				subname;
12610 	const uint8_t * const		hostname	= inContext->hostname;
12611 	int							nameMatches	= false;
12612 
12613 	if( strnicmpx( &inName[ 1 ], inName[ 0 ], "about" ) != 0 ) goto exit;
12614 	subname = DomainNameGetNextLabel( inName );
12615 
12616 	if( !_MemIEqual( &subname[ 1 ], subname[ 0 ], &hostname[ 1 ], hostname[ 0 ] ) ) goto exit;
12617 	subname = DomainNameGetNextLabel( subname );
12618 
12619 	if( !DomainNameEqual( subname, kLocalName ) ) goto exit;
12620 	nameMatches = true;
12621 
12622 exit:
12623 	return( nameMatches ? true : false );
12624 }
12625 
12626 //===========================================================================================================================
12627 //	_MDNSReplierHostnameMatch
12628 //===========================================================================================================================
12629 
12630 static Boolean
_MDNSReplierHostnameMatch(const MDNSReplierContext * inContext,const uint8_t * inName,unsigned int * outIndex)12631 	_MDNSReplierHostnameMatch(
12632 		const MDNSReplierContext *	inContext,
12633 		const uint8_t *				inName,
12634 		unsigned int *				outIndex )
12635 {
12636 	OSStatus					err;
12637 	const uint8_t *				ptr;
12638 	const uint8_t *				end;
12639 	uint32_t					index;
12640 	const uint8_t * const		hostname	= inContext->hostname;
12641 	int							nameMatches	= false;
12642 
12643 	require_quiet( inName[ 0 ] >= hostname[ 0 ], exit );
12644 	if( _memicmp( &inName[ 1 ], &hostname[ 1 ], hostname[ 0 ] ) != 0 ) goto exit;
12645 
12646 	ptr = &inName[ 1 + hostname[ 0 ] ];
12647 	end = &inName[ 1 + inName[ 0 ] ];
12648 	if( ptr < end )
12649 	{
12650 		require_quiet( *ptr == '-', exit );
12651 		++ptr;
12652 
12653 		err = DecimalTextToUInt32( (const char *) ptr, (const char *) end, &index, (const char **) &ptr );
12654 		require_noerr_quiet( err, exit );
12655 		require_quiet( ( index >= 2 ) && ( index <= UINT16_MAX ), exit );
12656 		require_quiet( ptr == end, exit );
12657 	}
12658 	else
12659 	{
12660 		index = 1;
12661 	}
12662 
12663 	if( !DomainNameEqual( ptr, kLocalName ) ) goto exit;
12664 	nameMatches = true;
12665 
12666 	if( outIndex ) *outIndex = index;
12667 
12668 exit:
12669 	return( nameMatches ? true : false );
12670 }
12671 
12672 //===========================================================================================================================
12673 //	_MDNSReplierCreateTXTRecord
12674 //===========================================================================================================================
12675 
_MDNSReplierCreateTXTRecord(const uint8_t * inRecordName,size_t inSize,uint8_t ** outTXT)12676 static OSStatus	_MDNSReplierCreateTXTRecord( const uint8_t *inRecordName, size_t inSize, uint8_t **outTXT )
12677 {
12678 	OSStatus		err;
12679 	uint8_t *		txt;
12680 	uint8_t *		ptr;
12681 	size_t			i, wholeCount, remCount;
12682 	uint32_t		hash;
12683 	int				n;
12684 	uint8_t			txtStr[ 16 ];
12685 
12686 	require_action_quiet( inSize > 0, exit, err = kSizeErr );
12687 
12688 	txt = (uint8_t *) malloc( inSize );
12689 	require_action( txt, exit, err = kNoMemoryErr );
12690 
12691 	hash = _FNV1( inRecordName, DomainNameLength( inRecordName ) );
12692 
12693 	txtStr[ 0 ] = 15;
12694 	n = MemPrintF( &txtStr[ 1 ], 15, "hash=0x%08X", hash );
12695 	check( n == 15 );
12696 
12697 	ptr = txt;
12698 	wholeCount = inSize / 16;
12699 	for( i = 0; i < wholeCount; ++i )
12700 	{
12701 		memcpy( ptr, txtStr, 16 );
12702 		ptr += 16;
12703 	}
12704 
12705 	remCount = inSize % 16;
12706 	if( remCount > 0 )
12707 	{
12708 		txtStr[ 0 ] = (uint8_t)( remCount - 1 );
12709 		memcpy( ptr, txtStr, remCount );
12710 		ptr += remCount;
12711 	}
12712 	check( ptr == &txt[ inSize ] );
12713 
12714 	*outTXT = txt;
12715 	err = kNoErr;
12716 
12717 exit:
12718 	return( err );
12719 }
12720 
12721 //===========================================================================================================================
12722 //	_MRResourceRecordCreate
12723 //===========================================================================================================================
12724 
12725 static OSStatus
_MRResourceRecordCreate(uint8_t * inName,uint16_t inType,uint16_t inClass,uint32_t inTTL,uint16_t inRDLength,uint8_t * inRData,MRResourceRecord ** outRecord)12726 	_MRResourceRecordCreate(
12727 		uint8_t *			inName,
12728 		uint16_t			inType,
12729 		uint16_t			inClass,
12730 		uint32_t			inTTL,
12731 		uint16_t			inRDLength,
12732 		uint8_t *			inRData,
12733 		MRResourceRecord **	outRecord )
12734 {
12735 	OSStatus				err;
12736 	MRResourceRecord *		obj;
12737 
12738 	obj = (MRResourceRecord *) calloc( 1, sizeof( *obj ) );
12739 	require_action( obj, exit, err = kNoMemoryErr );
12740 
12741 	obj->name		= inName;
12742 	obj->type		= inType;
12743 	obj->class		= inClass;
12744 	obj->ttl		= inTTL;
12745 	obj->rdlength	= inRDLength;
12746 	obj->rdata		= inRData;
12747 
12748 	if( inType == kDNSServiceType_SRV )
12749 	{
12750 		require_action_quiet( obj->rdlength > sizeof( dns_fixed_fields_srv ), exit, err = kMalformedErr );
12751 		obj->target = obj->rdata + sizeof( dns_fixed_fields_srv );
12752 	}
12753 
12754 	*outRecord = obj;
12755 	obj = NULL;
12756 	err = kNoErr;
12757 
12758 exit:
12759 	FreeNullSafe( obj );
12760 	return( err );
12761 }
12762 
12763 //===========================================================================================================================
12764 //	_MRResourceRecordFree
12765 //===========================================================================================================================
12766 
_MRResourceRecordFree(MRResourceRecord * inRecord)12767 static void	_MRResourceRecordFree( MRResourceRecord *inRecord )
12768 {
12769 	ForgetMem( &inRecord->name );
12770 	ForgetMem( &inRecord->rdata );
12771 	free( inRecord );
12772 }
12773 
12774 //===========================================================================================================================
12775 //	_MRResourceRecordFreeList
12776 //===========================================================================================================================
12777 
_MRResourceRecordFreeList(MRResourceRecord * inList)12778 static void	_MRResourceRecordFreeList( MRResourceRecord *inList )
12779 {
12780 	MRResourceRecord *		record;
12781 
12782 	while( ( record = inList ) != NULL )
12783 	{
12784 		inList = record->next;
12785 		_MRResourceRecordFree( record );
12786 	}
12787 }
12788 
12789 //===========================================================================================================================
12790 //	_MRNameOffsetItemCreate
12791 //===========================================================================================================================
12792 
_MRNameOffsetItemCreate(const uint8_t * inName,uint16_t inOffset,MRNameOffsetItem ** outItem)12793 static OSStatus	_MRNameOffsetItemCreate( const uint8_t *inName, uint16_t inOffset, MRNameOffsetItem **outItem )
12794 {
12795 	OSStatus				err;
12796 	MRNameOffsetItem *		obj;
12797 	size_t					nameLen;
12798 
12799 	require_action_quiet( inOffset <= kDNSCompressionOffsetMax, exit, err = kSizeErr );
12800 
12801 	nameLen = DomainNameLength( inName );
12802 	obj = (MRNameOffsetItem *) calloc( 1, offsetof( MRNameOffsetItem, name ) + nameLen );
12803 	require_action( obj, exit, err = kNoMemoryErr );
12804 
12805 	obj->offset = inOffset;
12806 	memcpy( obj->name, inName, nameLen );
12807 
12808 	*outItem = obj;
12809 	err = kNoErr;
12810 
12811 exit:
12812 	return( err );
12813 }
12814 
12815 //===========================================================================================================================
12816 //	_MRNameOffsetItemFree
12817 //===========================================================================================================================
12818 
_MRNameOffsetItemFree(MRNameOffsetItem * inItem)12819 static void	_MRNameOffsetItemFree( MRNameOffsetItem *inItem )
12820 {
12821 	free( inItem );
12822 }
12823 
12824 //===========================================================================================================================
12825 //	_MRNameOffsetItemFreeList
12826 //===========================================================================================================================
12827 
_MRNameOffsetItemFreeList(MRNameOffsetItem * inList)12828 static void	_MRNameOffsetItemFreeList( MRNameOffsetItem *inList )
12829 {
12830 	MRNameOffsetItem *		item;
12831 
12832 	while( ( item = inList ) != NULL )
12833 	{
12834 		inList = item->next;
12835 		_MRNameOffsetItemFree( item );
12836 	}
12837 }
12838 
12839 //===========================================================================================================================
12840 //	GAIPerfCmd
12841 //===========================================================================================================================
12842 
12843 #define kGAIPerfStandardTTL		( 1 * kSecondsPerHour )
12844 
12845 typedef struct GAITesterPrivate *		GAITesterRef;
12846 typedef struct GAITestCase				GAITestCase;
12847 
12848 typedef struct
12849 {
12850 	const char *		name;				// Domain name that was resolved.
12851 	uint64_t			connectionTimeUs;	// Time in microseconds that it took to create a DNS-SD connection.
12852 	uint64_t			firstTimeUs;		// Time in microseconds that it took to get the first address result.
12853 	uint64_t			timeUs;				// Time in microseconds that it took to get all expected address results.
12854 	OSStatus			error;
12855 
12856 }	GAITestItemResult;
12857 
12858 typedef void ( *GAITesterStopHandler_f )( void *inContext, OSStatus inError );
12859 typedef void
12860 	( *GAITesterResultsHandler_f )(
12861 		const char *				inCaseTitle,
12862 		NanoTime64					inCaseStartTime,
12863 		NanoTime64					inCaseEndTime,
12864 		const GAITestItemResult *	inResultArray,
12865 		size_t						inResultCount,
12866 		void *						inContext );
12867 
12868 typedef unsigned int		GAITestAddrType;
12869 #define kGAITestAddrType_None		0
12870 #define kGAITestAddrType_IPv4		( 1U << 0 )
12871 #define kGAITestAddrType_IPv6		( 1U << 1 )
12872 #define kGAITestAddrType_Both		( kGAITestAddrType_IPv4 | kGAITestAddrType_IPv6 )
12873 
12874 #define GAITestAddrTypeIsValid( X ) \
12875 	( ( (X) & kGAITestAddrType_Both ) && ( ( (X) & ~kGAITestAddrType_Both ) == 0 ) )
12876 
12877 typedef struct
12878 {
12879 	GAITesterRef			tester;				// GAI tester object.
12880 	CFMutableArrayRef		testCaseResults;	// Array of test case results.
12881 	unsigned int			iterTimeLimitMs;	// Amount of time to allow each iteration to complete.
12882 	unsigned int			callDelayMs;		// Amount of time to wait before calling DNSServiceGetAddrInfo().
12883 	unsigned int			serverDelayMs;		// Amount of additional time to have server delay its responses.
12884 	unsigned int			defaultIterCount;	// Default test case iteration count.
12885 	dispatch_source_t		sigIntSource;		// Dispatch source for SIGINT.
12886 	dispatch_source_t		sigTermSource;		// Dispatch source for SIGTERM.
12887 	char *					outputFilePath;		// File to write test results to. If NULL, then write to stdout.
12888 	OutputFormatType		outputFormat;		// Format of test results output.
12889 	Boolean					skipPathEval;		// True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
12890 	Boolean					badUDPMode;			// True if the test DNS server is to run in Bad UDP mode.
12891 	Boolean					testFailed;			// True if at least one test case iteration failed.
12892 
12893 }	GAIPerfContext;
12894 
12895 static void		GAIPerfContextFree( GAIPerfContext *inContext );
12896 static OSStatus	GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext );
12897 static OSStatus	GAIPerfAddBasicTestCases( GAIPerfContext *inContext );
12898 static void		GAIPerfTesterStopHandler( void *inContext, OSStatus inError );
12899 static void
12900 	GAIPerfResultsHandler(
12901 		const char *				inCaseTitle,
12902 		NanoTime64					inCaseStartTime,
12903 		NanoTime64					inCaseEndTime,
12904 		const GAITestItemResult *	inResultArray,
12905 		size_t						inResultCount,
12906 		void *						inContext );
12907 static void		GAIPerfSignalHandler( void *inContext );
12908 
12909 static CFTypeID	GAITesterGetTypeID( void );
12910 static OSStatus
12911 	GAITesterCreate(
12912 		dispatch_queue_t	inQueue,
12913 		unsigned int		inCallDelayMs,
12914 		int					inServerDelayMs,
12915 		int					inServerDefaultTTL,
12916 		Boolean				inSkipPathEvaluation,
12917 		Boolean				inBadUDPMode,
12918 		GAITesterRef *		outTester );
12919 static void		GAITesterStart( GAITesterRef inTester );
12920 static void		GAITesterStop( GAITesterRef inTester );
12921 static OSStatus	GAITesterAddTestCase( GAITesterRef inTester, GAITestCase *inCase );
12922 static void
12923 	GAITesterSetStopHandler(
12924 		GAITesterRef			inTester,
12925 		GAITesterStopHandler_f	inEventHandler,
12926 		void *					inEventContext );
12927 static void
12928 	GAITesterSetResultsHandler(
12929 		GAITesterRef				inTester,
12930 		GAITesterResultsHandler_f	inResultsHandler,
12931 		void *						inResultsContext );
12932 
12933 static OSStatus	GAITestCaseCreate( const char *inTitle, GAITestCase **outCase );
12934 static void		GAITestCaseFree( GAITestCase *inCase );
12935 static OSStatus
12936 	GAITestCaseAddItem(
12937 		GAITestCase *	inCase,
12938 		unsigned int	inAliasCount,
12939 		unsigned int	inAddressCount,
12940 		int				inTTL,
12941 		GAITestAddrType	inHasAddrs,
12942 		GAITestAddrType	inWantAddrs,
12943 		unsigned int	inTimeLimitMs,
12944 		unsigned int	inItemCount );
12945 static OSStatus
12946 	GAITestCaseAddLocalHostItem(
12947 		GAITestCase *	inCase,
12948 		GAITestAddrType	inWantAddrs,
12949 		unsigned int	inTimeLimitMs,
12950 		unsigned int	inItemCount );
12951 
GAIPerfCmd(void)12952 static void	GAIPerfCmd( void )
12953 {
12954 	OSStatus				err;
12955 	GAIPerfContext *		context = NULL;
12956 
12957 	err = CheckRootUser();
12958 	require_noerr_quiet( err, exit );
12959 
12960 	err = CheckIntegerArgument( gGAIPerf_CallDelayMs, "call delay (ms)", 0, INT_MAX );
12961 	require_noerr_quiet( err, exit );
12962 
12963 	err = CheckIntegerArgument( gGAIPerf_ServerDelayMs, "server delay (ms)", 0, INT_MAX );
12964 	require_noerr_quiet( err, exit );
12965 
12966 	err = CheckIntegerArgument( gGAIPerf_IterationCount, "iteration count", 1, INT_MAX );
12967 	require_noerr_quiet( err, exit );
12968 
12969 	err = CheckIntegerArgument( gGAIPerf_IterationTimeLimitMs, "iteration time limit (ms)", 0, INT_MAX );
12970 	require_noerr_quiet( err, exit );
12971 
12972 	context = (GAIPerfContext *) calloc( 1, sizeof( *context ) );
12973 	require_action( context, exit, err = kNoMemoryErr );
12974 
12975 	context->testCaseResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
12976 	require_action( context->testCaseResults, exit, err = kNoMemoryErr );
12977 
12978 	context->iterTimeLimitMs	= (unsigned int) gGAIPerf_IterationTimeLimitMs;
12979 	context->callDelayMs		= (unsigned int) gGAIPerf_CallDelayMs;
12980 	context->serverDelayMs		= (unsigned int) gGAIPerf_ServerDelayMs;
12981 	context->defaultIterCount	= (unsigned int) gGAIPerf_IterationCount;
12982 	context->skipPathEval		= gGAIPerf_SkipPathEvalulation	? true : false;
12983 	context->badUDPMode			= gGAIPerf_BadUDPMode			? true : false;
12984 
12985 	if( gGAIPerf_OutputFilePath )
12986 	{
12987 		context->outputFilePath = strdup( gGAIPerf_OutputFilePath );
12988 		require_action( context->outputFilePath, exit, err = kNoMemoryErr );
12989 	}
12990 
12991 	err = OutputFormatFromArgString( gGAIPerf_OutputFormat, &context->outputFormat );
12992 	require_noerr_quiet( err, exit );
12993 
12994 	err = GAITesterCreate( dispatch_get_main_queue(), context->callDelayMs, (int) context->serverDelayMs,
12995 		kGAIPerfStandardTTL, context->skipPathEval, context->badUDPMode, &context->tester );
12996 	require_noerr( err, exit );
12997 
12998 	check( gGAIPerf_TestSuite );
12999 	if( strcasecmp( gGAIPerf_TestSuite, kGAIPerfTestSuiteName_Basic ) == 0 )
13000 	{
13001 		err = GAIPerfAddBasicTestCases( context );
13002 		require_noerr( err, exit );
13003 	}
13004 	else if( strcasecmp( gGAIPerf_TestSuite, kGAIPerfTestSuiteName_Advanced ) == 0 )
13005 	{
13006 		err = GAIPerfAddAdvancedTestCases( context );
13007 		require_noerr( err, exit );
13008 	}
13009 	else
13010 	{
13011 		FPrintF( stderr, "error: Invalid test suite name: %s.\n", gGAIPerf_TestSuite );
13012 		goto exit;
13013 	}
13014 
13015 	GAITesterSetStopHandler( context->tester, GAIPerfTesterStopHandler, context );
13016 	GAITesterSetResultsHandler( context->tester, GAIPerfResultsHandler, context );
13017 
13018 	signal( SIGINT, SIG_IGN );
13019 	err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), GAIPerfSignalHandler, context,
13020 		&context->sigIntSource );
13021 	require_noerr( err, exit );
13022 	dispatch_resume( context->sigIntSource );
13023 
13024 	signal( SIGTERM, SIG_IGN );
13025 	err = DispatchSignalSourceCreate( SIGTERM, dispatch_get_main_queue(), GAIPerfSignalHandler, context,
13026 		&context->sigTermSource );
13027 	require_noerr( err, exit );
13028 	dispatch_resume( context->sigTermSource );
13029 
13030 	GAITesterStart( context->tester );
13031 	dispatch_main();
13032 
13033 exit:
13034 	if( context ) GAIPerfContextFree( context );
13035 	exit( 1 );
13036 }
13037 
13038 //===========================================================================================================================
13039 //	GAIPerfContextFree
13040 //===========================================================================================================================
13041 
GAIPerfContextFree(GAIPerfContext * inContext)13042 static void	GAIPerfContextFree( GAIPerfContext *inContext )
13043 {
13044 	ForgetCF( &inContext->tester );
13045 	ForgetCF( &inContext->testCaseResults );
13046 	ForgetMem( &inContext->outputFilePath );
13047 	dispatch_source_forget( &inContext->sigIntSource );
13048 	dispatch_source_forget( &inContext->sigTermSource );
13049 	free( inContext );
13050 }
13051 
13052 //===========================================================================================================================
13053 //	GAIPerfAddAdvancedTestCases
13054 //===========================================================================================================================
13055 
13056 #define kTestCaseTitleBufferSize		128
13057 
13058 static void
13059 	_GAIPerfWriteTestCaseTitle(
13060 		char			inBuffer[ kTestCaseTitleBufferSize ],
13061 		unsigned int	inCNAMERecordCount,
13062 		unsigned int	inARecordCount,
13063 		unsigned int	inAAAARecordCount,
13064 		GAITestAddrType	inRequested,
13065 		unsigned int	inIterationCount,
13066 		Boolean			inIterationsAreUnique );
13067 static void
13068 	_GAIPerfWriteLocalHostTestCaseTitle(
13069 		char			inBuffer[ kTestCaseTitleBufferSize ],
13070 		GAITestAddrType	inRequested,
13071 		unsigned int	inIterationCount );
13072 
13073 #define kGAIPerfAdvancedTestSuite_MaxAliasCount		4
13074 #define kGAIPerfAdvancedTestSuite_MaxAddrCount		8
13075 
GAIPerfAddAdvancedTestCases(GAIPerfContext * inContext)13076 static OSStatus	GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext )
13077 {
13078 	OSStatus			err;
13079 	unsigned int		aliasCount, addressCount, i;
13080 	GAITestCase *		testCase = NULL;
13081 	char				title[ kTestCaseTitleBufferSize ];
13082 
13083 	aliasCount = 0;
13084 	while( aliasCount <= kGAIPerfAdvancedTestSuite_MaxAliasCount )
13085 	{
13086 		for( addressCount = 1; addressCount <= kGAIPerfAdvancedTestSuite_MaxAddrCount; addressCount *= 2 )
13087 		{
13088 			// Add a test case to resolve a domain name with
13089 			//
13090 			//     <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
13091 			//
13092 			// to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which
13093 			// requires server queries.
13094 
13095 			_GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both,
13096 				inContext->defaultIterCount, true );
13097 
13098 			err = GAITestCaseCreate( title, &testCase );
13099 			require_noerr( err, exit );
13100 
13101 			for( i = 0; i < inContext->defaultIterCount; ++i )
13102 			{
13103 				err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL,
13104 					kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, 1 );
13105 				require_noerr( err, exit );
13106 			}
13107 
13108 			err = GAITesterAddTestCase( inContext->tester, testCase );
13109 			require_noerr( err, exit );
13110 			testCase = NULL;
13111 
13112 			// Add a test case to resolve a domain name with
13113 			//
13114 			//     <aliasCount> CNAME records, <addressCount> A records, and <addressCount> AAAA records
13115 			//
13116 			// to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique domain name, which requires a server
13117 			// query. The subsequent iterations resolve the same domain name as the preliminary iteration, which should
13118 			// ideally require no server queries, i.e., the results should come from the cache.
13119 
13120 			_GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both,
13121 				inContext->defaultIterCount, false );
13122 
13123 			err = GAITestCaseCreate( title, &testCase );
13124 			require_noerr( err, exit );
13125 
13126 			err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL,
13127 				kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, inContext->defaultIterCount + 1 );
13128 			require_noerr( err, exit );
13129 
13130 			err = GAITesterAddTestCase( inContext->tester, testCase );
13131 			require_noerr( err, exit );
13132 			testCase = NULL;
13133 		}
13134 
13135 		aliasCount = ( aliasCount == 0 ) ? 1 : ( 2 * aliasCount );
13136 	}
13137 
13138 	// Finally, add a test case to resolve localhost to its IPv4 and IPv6 addresses.
13139 
13140 	_GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount );
13141 
13142 	err = GAITestCaseCreate( title, &testCase );
13143 	require_noerr( err, exit );
13144 
13145 	err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
13146 		inContext->defaultIterCount );
13147 	require_noerr( err, exit );
13148 
13149 	err = GAITesterAddTestCase( inContext->tester, testCase );
13150 	require_noerr( err, exit );
13151 	testCase = NULL;
13152 
13153 exit:
13154 	if( testCase ) GAITestCaseFree( testCase );
13155 	return( err );
13156 }
13157 
13158 //===========================================================================================================================
13159 //	_GAIPerfWriteTestCaseTitle
13160 //===========================================================================================================================
13161 
13162 #define GAITestAddrTypeToRequestKeyValue( X ) (				\
13163 	( (X) == kGAITestAddrType_Both ) ? "ipv4\\,ipv6"	:	\
13164 	( (X) == kGAITestAddrType_IPv4 ) ? "ipv4"			:	\
13165 	( (X) == kGAITestAddrType_IPv6 ) ? "ipv6"			:	\
13166 									   "" )
13167 
13168 static void
_GAIPerfWriteTestCaseTitle(char inBuffer[kTestCaseTitleBufferSize],unsigned int inCNAMERecordCount,unsigned int inARecordCount,unsigned int inAAAARecordCount,GAITestAddrType inRequested,unsigned int inIterationCount,Boolean inIterationsAreUnique)13169 	_GAIPerfWriteTestCaseTitle(
13170 		char			inBuffer[ kTestCaseTitleBufferSize ],
13171 		unsigned int	inCNAMERecordCount,
13172 		unsigned int	inARecordCount,
13173 		unsigned int	inAAAARecordCount,
13174 		GAITestAddrType	inRequested,
13175 		unsigned int	inIterationCount,
13176 		Boolean			inIterationsAreUnique )
13177 {
13178 	SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=dynamic,cname=%u,a=%u,aaaa=%u,req=%s,iterations=%u%?s",
13179 		inCNAMERecordCount, inARecordCount, inAAAARecordCount, GAITestAddrTypeToRequestKeyValue( inRequested ),
13180 		inIterationCount, inIterationsAreUnique, ",unique" );
13181 }
13182 
13183 //===========================================================================================================================
13184 //	_GAIPerfWriteLocalHostTestCaseTitle
13185 //===========================================================================================================================
13186 
13187 static void
_GAIPerfWriteLocalHostTestCaseTitle(char inBuffer[kTestCaseTitleBufferSize],GAITestAddrType inRequested,unsigned int inIterationCount)13188 	_GAIPerfWriteLocalHostTestCaseTitle(
13189 		char			inBuffer[ kTestCaseTitleBufferSize ],
13190 		GAITestAddrType	inRequested,
13191 		unsigned int	inIterationCount )
13192 {
13193 	SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=localhost,req=%s,iterations=%u",
13194 		GAITestAddrTypeToRequestKeyValue( inRequested ), inIterationCount );
13195 }
13196 
13197 //===========================================================================================================================
13198 //	GAIPerfAddBasicTestCases
13199 //===========================================================================================================================
13200 
13201 #define kGAIPerfBasicTestSuite_AliasCount		2
13202 #define kGAIPerfBasicTestSuite_AddrCount		4
13203 
GAIPerfAddBasicTestCases(GAIPerfContext * inContext)13204 static OSStatus	GAIPerfAddBasicTestCases( GAIPerfContext *inContext )
13205 {
13206 	OSStatus			err;
13207 	GAITestCase *		testCase = NULL;
13208 	char				title[ kTestCaseTitleBufferSize ];
13209 	unsigned int		i;
13210 
13211 	// Test Case #1:
13212 	// Resolve a domain name with
13213 	//
13214 	//     2 CNAME records, 4 A records, and 4 AAAA records
13215 	//
13216 	// to its IPv4 and IPv6 addresses. Each of the iterations resolves a unique domain name, which requires server
13217 	// queries.
13218 
13219 	_GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount,
13220 		kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both,
13221 		inContext->defaultIterCount, true );
13222 
13223 	err = GAITestCaseCreate( title, &testCase );
13224 	require_noerr( err, exit );
13225 
13226 	for( i = 0; i < inContext->defaultIterCount; ++i )
13227 	{
13228 		err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount,
13229 			kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs, 1 );
13230 		require_noerr( err, exit );
13231 	}
13232 
13233 	err = GAITesterAddTestCase( inContext->tester, testCase );
13234 	require_noerr( err, exit );
13235 	testCase = NULL;
13236 
13237 	// Test Case #2:
13238 	// Resolve a domain name with
13239 	//
13240 	//     2 CNAME records, 4 A records, and 4 AAAA records
13241 	//
13242 	// to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which
13243 	// requires server queries. Each of the subsequent iterations resolves the same domain name as the preliminary
13244 	// iteration, which should ideally require no additional server queries, i.e., the results should come from the cache.
13245 
13246 	_GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount,
13247 		kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both,
13248 		inContext->defaultIterCount, false );
13249 
13250 	err = GAITestCaseCreate( title, &testCase );
13251 	require_noerr( err, exit );
13252 
13253 	err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount,
13254 		kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
13255 		inContext->defaultIterCount + 1 );
13256 	require_noerr( err, exit );
13257 
13258 	err = GAITesterAddTestCase( inContext->tester, testCase );
13259 	require_noerr( err, exit );
13260 	testCase = NULL;
13261 
13262 	// Test Case #3:
13263 	// Each iteration resolves localhost to its IPv4 and IPv6 addresses.
13264 
13265 	_GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount );
13266 
13267 	err = GAITestCaseCreate( title, &testCase );
13268 	require_noerr( err, exit );
13269 
13270 	err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, inContext->iterTimeLimitMs,
13271 		inContext->defaultIterCount );
13272 	require_noerr( err, exit );
13273 
13274 	err = GAITesterAddTestCase( inContext->tester, testCase );
13275 	require_noerr( err, exit );
13276 	testCase = NULL;
13277 
13278 exit:
13279 	if( testCase ) GAITestCaseFree( testCase );
13280 	return( err );
13281 }
13282 
13283 //===========================================================================================================================
13284 //	GAIPerfTesterStopHandler
13285 //===========================================================================================================================
13286 
13287 #define kGAIPerfResultsKey_Info				CFSTR( "info" )
13288 #define kGAIPerfResultsKey_TestCases		CFSTR( "testCases" )
13289 #define kGAIPerfResultsKey_Success			CFSTR( "success" )
13290 
13291 #define kGAIPerfInfoKey_CallDelay			CFSTR( "callDelayMs" )
13292 #define kGAIPerfInfoKey_ServerDelay			CFSTR( "serverDelayMs" )
13293 #define kGAIPerfInfoKey_SkippedPathEval		CFSTR( "skippedPathEval" )
13294 #define kGAIPerfInfoKey_UsedBadUDPMode		CFSTR( "usedBadUPDMode" )
13295 
GAIPerfTesterStopHandler(void * inContext,OSStatus inError)13296 static void	GAIPerfTesterStopHandler( void *inContext, OSStatus inError )
13297 {
13298 	OSStatus					err;
13299 	GAIPerfContext * const		context = (GAIPerfContext *) inContext;
13300 	CFPropertyListRef			plist;
13301 	int							exitCode;
13302 
13303 	err = inError;
13304 	require_noerr_quiet( err, exit );
13305 
13306 	err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
13307 		"{"
13308 			"%kO="			// info
13309 			"{"
13310 				"%kO=%lli"	// callDelayMs
13311 				"%kO=%lli"	// serverDelayMs
13312 				"%kO=%b"	// skippedPathEval
13313 				"%kO=%b"	// usedBadUPDMode
13314 			"}"
13315 			"%kO=%O"		// testCases
13316 			"%kO=%b"		// success
13317 		"}",
13318 		kGAIPerfResultsKey_Info,
13319 		kGAIPerfInfoKey_CallDelay,			(int64_t) context->callDelayMs,
13320 		kGAIPerfInfoKey_ServerDelay,		(int64_t) context->serverDelayMs,
13321 		kGAIPerfInfoKey_SkippedPathEval,	context->skipPathEval,
13322 		kGAIPerfInfoKey_UsedBadUDPMode,		context->badUDPMode,
13323 		kGAIPerfResultsKey_TestCases,		context->testCaseResults,
13324 		kGAIPerfResultsKey_Success,			!context->testFailed );
13325 	require_noerr( err, exit );
13326 
13327 	err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
13328 	CFRelease( plist );
13329 	require_noerr( err, exit );
13330 
13331 exit:
13332 	exitCode = err ? 1 : ( context->testFailed ? 2 : 0 );
13333 	GAIPerfContextFree( context );
13334 	exit( exitCode );
13335 }
13336 
13337 //===========================================================================================================================
13338 //	GAIPerfResultsHandler
13339 //===========================================================================================================================
13340 
13341 // Keys for test case dictionary
13342 
13343 #define kGAIPerfTestCaseKey_Title				CFSTR( "title" )
13344 #define kGAIPerfTestCaseKey_StartTime			CFSTR( "startTime" )
13345 #define kGAIPerfTestCaseKey_EndTime				CFSTR( "endTime" )
13346 #define kGAIPerfTestCaseKey_Results				CFSTR( "results" )
13347 #define kGAIPerfTestCaseKey_FirstStats			CFSTR( "firstStats" )
13348 #define kGAIPerfTestCaseKey_ConnectionStats		CFSTR( "connectionStats" )
13349 #define kGAIPerfTestCaseKey_Stats				CFSTR( "stats" )
13350 
13351 // Keys for test case results array entry dictionaries
13352 
13353 #define kGAIPerfTestCaseResultKey_Name					CFSTR( "name" )
13354 #define kGAIPerfTestCaseResultKey_ConnectionTime		CFSTR( "connectionTimeUs" )
13355 #define kGAIPerfTestCaseResultKey_FirstTime				CFSTR( "firstTimeUs" )
13356 #define kGAIPerfTestCaseResultKey_Time					CFSTR( "timeUs" )
13357 
13358 // Keys for test case stats dictionaries
13359 
13360 #define kGAIPerfTestCaseStatsKey_Count		CFSTR( "count" )
13361 #define kGAIPerfTestCaseStatsKey_Min		CFSTR( "min" )
13362 #define kGAIPerfTestCaseStatsKey_Max		CFSTR( "max" )
13363 #define kGAIPerfTestCaseStatsKey_Mean		CFSTR( "mean" )
13364 #define kGAIPerfTestCaseStatsKey_StdDev		CFSTR( "sd" )
13365 
13366 typedef struct
13367 {
13368 	double		min;
13369 	double		max;
13370 	double		mean;
13371 	double		stdDev;
13372 
13373 }	GAIPerfStats;
13374 
13375 #define GAIPerfStatsInit( X ) \
13376 	do { (X)->min = DBL_MAX; (X)->max = DBL_MIN; (X)->mean = 0.0; (X)->stdDev = 0.0; } while( 0 )
13377 
13378 static void
GAIPerfResultsHandler(const char * inCaseTitle,NanoTime64 inCaseStartTime,NanoTime64 inCaseEndTime,const GAITestItemResult * inResultArray,size_t inResultCount,void * inContext)13379 	GAIPerfResultsHandler(
13380 		const char *				inCaseTitle,
13381 		NanoTime64					inCaseStartTime,
13382 		NanoTime64					inCaseEndTime,
13383 		const GAITestItemResult *	inResultArray,
13384 		size_t						inResultCount,
13385 		void *						inContext )
13386 {
13387 	OSStatus						err;
13388 	GAIPerfContext * const			context	= (GAIPerfContext *) inContext;
13389 	int								namesAreDynamic, namesAreUnique;
13390 	const char *					ptr;
13391 	size_t							startIndex;
13392 	CFMutableArrayRef				results	= NULL;
13393 	GAIPerfStats					stats, firstStats, connStats;
13394 	double							sum, firstSum, connSum;
13395 	size_t							keyValueLen, i;
13396 	uint32_t						count;
13397 	char							keyValue[ 16 ];	// Size must be at least strlen( "name=dynamic" ) + 1 bytes.
13398 	char							startTime[ 32 ];
13399 	char							endTime[ 32 ];
13400 	const GAITestItemResult *		result;
13401 
13402 	// If this test case resolves the same "d.test." name in each iteration (title contains the "name=dynamic" key-value
13403 	// pair, but not the "unique" key), then don't count the first iteration, whose purpose is to populate the cache with
13404 	// the domain name's CNAME, A, and AAAA records.
13405 
13406 	namesAreDynamic	= false;
13407 	namesAreUnique	= false;
13408 	ptr = inCaseTitle;
13409 	while( _ParseQuotedEscapedString( ptr, NULL, ",", keyValue, sizeof( keyValue ), &keyValueLen, NULL, &ptr ) )
13410 	{
13411 		if( strnicmpx( keyValue, keyValueLen, "name=dynamic" ) == 0 )
13412 		{
13413 			namesAreDynamic = true;
13414 		}
13415 		else if( strnicmpx( keyValue, keyValueLen, "unique" ) == 0 )
13416 		{
13417 			namesAreUnique = true;
13418 		}
13419 		if( namesAreDynamic && namesAreUnique ) break;
13420 	}
13421 
13422 	startIndex = ( ( inResultCount > 0 ) && namesAreDynamic && !namesAreUnique ) ? 1 : 0;
13423 	results = CFArrayCreateMutable( NULL, (CFIndex)( inResultCount - startIndex ), &kCFTypeArrayCallBacks );
13424 	require_action( results, exit, err = kNoMemoryErr );
13425 
13426 	GAIPerfStatsInit( &stats );
13427 	GAIPerfStatsInit( &firstStats );
13428 	GAIPerfStatsInit( &connStats );
13429 
13430 	sum			= 0.0;
13431 	firstSum	= 0.0;
13432 	connSum		= 0.0;
13433 	count		= 0;
13434 	for( i = startIndex; i < inResultCount; ++i )
13435 	{
13436 		double		value;
13437 
13438 		result = &inResultArray[ i ];
13439 
13440 		err = CFPropertyListAppendFormatted( kCFAllocatorDefault, results,
13441 			"{"
13442 				"%kO=%s"	// name
13443 				"%kO=%lli"	// connectionTimeUs
13444 				"%kO=%lli"	// firstTimeUs
13445 				"%kO=%lli"	// timeUs
13446 				"%kO=%lli"	// error
13447 			"}",
13448 			kGAIPerfTestCaseResultKey_Name,				result->name,
13449 			kGAIPerfTestCaseResultKey_ConnectionTime,	(int64_t) result->connectionTimeUs,
13450 			kGAIPerfTestCaseResultKey_FirstTime,		(int64_t) result->firstTimeUs,
13451 			kGAIPerfTestCaseResultKey_Time,				(int64_t) result->timeUs,
13452 			CFSTR( "error" ),							(int64_t) result->error );
13453 		require_noerr( err, exit );
13454 
13455 		if( !result->error )
13456 		{
13457 			value = (double) result->timeUs;
13458 			if( value < stats.min ) stats.min = value;
13459 			if( value > stats.max ) stats.max = value;
13460 			sum += value;
13461 
13462 			value = (double) result->firstTimeUs;
13463 			if( value < firstStats.min ) firstStats.min = value;
13464 			if( value > firstStats.max ) firstStats.max = value;
13465 			firstSum += value;
13466 
13467 			value = (double) result->connectionTimeUs;
13468 			if( value < connStats.min ) connStats.min = value;
13469 			if( value > connStats.max ) connStats.max = value;
13470 			connSum += value;
13471 
13472 			++count;
13473 		}
13474 		else
13475 		{
13476 			context->testFailed = true;
13477 		}
13478 	}
13479 
13480 	if( count > 0 )
13481 	{
13482 		stats.mean		= sum      / (double) count;
13483 		firstStats.mean	= firstSum / (double) count;
13484 		connStats.mean	= connSum  / (double) count;
13485 
13486 		sum			= 0.0;
13487 		firstSum	= 0.0;
13488 		connSum		= 0.0;
13489 		for( i = startIndex; i < inResultCount; ++i )
13490 		{
13491 			double		diff;
13492 
13493 			result = &inResultArray[ i ];
13494 			if( result->error ) continue;
13495 
13496 			diff		 = stats.mean - (double) result->timeUs;
13497 			sum			+= ( diff * diff );
13498 
13499 			diff		 = firstStats.mean - (double) result->firstTimeUs;
13500 			firstSum	+= ( diff * diff );
13501 
13502 			diff		 = connStats.mean - (double) result->connectionTimeUs;
13503 			connSum		+= ( diff * diff );
13504 		}
13505 		stats.stdDev		= sqrt( sum      / (double) count );
13506 		firstStats.stdDev	= sqrt( firstSum / (double) count );
13507 		connStats.stdDev	= sqrt( connSum  / (double) count );
13508 	}
13509 
13510 	err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->testCaseResults,
13511 		"{"
13512 			"%kO=%s"
13513 			"%kO=%s"
13514 			"%kO=%s"
13515 			"%kO=%O"
13516 			"%kO="
13517 			"{"
13518 				"%kO=%lli"
13519 				"%kO=%f"
13520 				"%kO=%f"
13521 				"%kO=%f"
13522 				"%kO=%f"
13523 			"}"
13524 			"%kO="
13525 			"{"
13526 				"%kO=%lli"
13527 				"%kO=%f"
13528 				"%kO=%f"
13529 				"%kO=%f"
13530 				"%kO=%f"
13531 			"}"
13532 			"%kO="
13533 			"{"
13534 				"%kO=%lli"
13535 				"%kO=%f"
13536 				"%kO=%f"
13537 				"%kO=%f"
13538 				"%kO=%f"
13539 			"}"
13540 		"}",
13541 		kGAIPerfTestCaseKey_Title,			inCaseTitle,
13542 		kGAIPerfTestCaseKey_StartTime,		_NanoTime64ToTimestamp( inCaseStartTime, startTime, sizeof( startTime ) ),
13543 		kGAIPerfTestCaseKey_EndTime,		_NanoTime64ToTimestamp( inCaseEndTime, endTime, sizeof( endTime ) ),
13544 		kGAIPerfTestCaseKey_Results,		results,
13545 		kGAIPerfTestCaseKey_Stats,
13546 		kGAIPerfTestCaseStatsKey_Count,		(int64_t) count,
13547 		kGAIPerfTestCaseStatsKey_Min,		stats.min,
13548 		kGAIPerfTestCaseStatsKey_Max,		stats.max,
13549 		kGAIPerfTestCaseStatsKey_Mean,		stats.mean,
13550 		kGAIPerfTestCaseStatsKey_StdDev,	stats.stdDev,
13551 		kGAIPerfTestCaseKey_FirstStats,
13552 		kGAIPerfTestCaseStatsKey_Count,		(int64_t) count,
13553 		kGAIPerfTestCaseStatsKey_Min,		firstStats.min,
13554 		kGAIPerfTestCaseStatsKey_Max,		firstStats.max,
13555 		kGAIPerfTestCaseStatsKey_Mean,		firstStats.mean,
13556 		kGAIPerfTestCaseStatsKey_StdDev,	firstStats.stdDev,
13557 		kGAIPerfTestCaseKey_ConnectionStats,
13558 		kGAIPerfTestCaseStatsKey_Count,		(int64_t) count,
13559 		kGAIPerfTestCaseStatsKey_Min,		connStats.min,
13560 		kGAIPerfTestCaseStatsKey_Max,		connStats.max,
13561 		kGAIPerfTestCaseStatsKey_Mean,		connStats.mean,
13562 		kGAIPerfTestCaseStatsKey_StdDev,	connStats.stdDev );
13563 	require_noerr( err, exit );
13564 
13565 exit:
13566 	CFReleaseNullSafe( results );
13567 	if( err ) exit( 1 );
13568 }
13569 
13570 //===========================================================================================================================
13571 //	GAIPerfSignalHandler
13572 //===========================================================================================================================
13573 
GAIPerfSignalHandler(void * inContext)13574 static void	GAIPerfSignalHandler( void *inContext )
13575 {
13576 	GAIPerfContext * const		context = (GAIPerfContext *) inContext;
13577 
13578 	if( !context->tester ) exit( 1 );
13579 	GAITesterStop( context->tester );
13580 	context->tester = NULL;
13581 }
13582 
13583 //===========================================================================================================================
13584 //	GAITesterCreate
13585 //===========================================================================================================================
13586 
13587 // A character set of lower-case alphabet characters and digits and a string length of six allows for 36^6 = 2,176,782,336
13588 // possible strings to use in the Tag label.
13589 
13590 #define kGAITesterTagStringLen		6
13591 
13592 typedef struct GAITestItem		GAITestItem;
13593 struct GAITestItem
13594 {
13595 	GAITestItem *		next;				// Next test item in list.
13596 	char *				name;				// Domain name to resolve.
13597 	uint64_t			connectionTimeUs;	// Time in microseconds that it took to create a DNS-SD connection.
13598 	uint64_t			firstTimeUs;		// Time in microseconds that it took to get the first address result.
13599 	uint64_t			timeUs;				// Time in microseconds that it took to get all expected address results.
13600 	unsigned int		addressCount;		// Address count of the domain name, i.e., the Count label argument.
13601 	OSStatus			error;				// Current status/error.
13602 	unsigned int		timeLimitMs;		// Time limit in milliseconds for the test item's completion.
13603 	Boolean				hasV4;				// True if the domain name has one or more IPv4 addresses.
13604 	Boolean				hasV6;				// True if the domain name has one or more IPv6 addresses.
13605 	Boolean				wantV4;				// True if DNSServiceGetAddrInfo() should be called to get IPv4 addresses.
13606 	Boolean				wantV6;				// True if DNSServiceGetAddrInfo() should be called to get IPv6 addresses.
13607 };
13608 
13609 struct GAITestCase
13610 {
13611 	GAITestCase *		next;		// Next test case in list.
13612 	GAITestItem *		itemList;	// List of test items.
13613 	char *				title;		// Title of the test case.
13614 };
13615 
13616 struct GAITesterPrivate
13617 {
13618 	CFRuntimeBase					base;				// CF object base.
13619 	dispatch_queue_t				queue;				// Serial work queue.
13620 	DNSServiceRef					connection;			// Reference to the shared DNS-SD connection.
13621 	DNSServiceRef					getAddrInfo;		// Reference to the current DNSServiceGetAddrInfo operation.
13622 	GAITestCase *					caseList;			// List of test cases.
13623 	GAITestCase *					currentCase;		// Pointer to the current test case.
13624 	GAITestItem *					currentItem;		// Pointer to the current test item.
13625 	NanoTime64						caseStartTime;		// Start time of current test case in Unix time as nanoseconds.
13626 	NanoTime64						caseEndTime;		// End time of current test case in Unix time as nanoseconds.
13627 	unsigned int					callDelayMs;		// Amount of time to wait before calling DNSServiceGetAddrInfo().
13628 	Boolean							skipPathEval;		// True if DNSServiceGetAddrInfo() path evaluation is to be skipped.
13629 	Boolean							stopped;			// True if the tester has been stopped.
13630 	Boolean							badUDPMode;			// True if the test DNS server is to run in Bad UDP mode.
13631 	dispatch_source_t				timer;				// Timer for enforcing a test item's time limit.
13632 	pcap_t *						pcap;				// Captures traffic between mDNSResponder and test DNS server.
13633 	pid_t							serverPID;			// PID of the test DNS server.
13634 	int								serverDelayMs;		// Additional time to have the server delay its responses by.
13635 	int								serverDefaultTTL;	// Default TTL for the server's records.
13636 	GAITesterStopHandler_f			stopHandler;		// User's stop handler.
13637 	void *							stopContext;		// User's event handler context.
13638 	GAITesterResultsHandler_f		resultsHandler;		// User's results handler.
13639 	void *							resultsContext;		// User's results handler context.
13640 
13641 	// Variables for current test item.
13642 
13643 	uint64_t						bitmapV4;		// Bitmap of IPv4 results that have yet to be received.
13644 	uint64_t						bitmapV6;		// Bitmap of IPv6 results that have yet to be received.
13645 	uint64_t						startTicks;		// Start ticks of DNSServiceGetAddrInfo().
13646 	uint64_t						connTicks;		// Ticks when the connection was created.
13647 	uint64_t						firstTicks;		// Ticks when the first DNSServiceGetAddrInfo result was received.
13648 	uint64_t						endTicks;		// Ticks when the last DNSServiceGetAddrInfo result was received.
13649 	Boolean							gotFirstResult;	// True if the first result has been received.
13650 };
13651 
13652 CF_CLASS_DEFINE( GAITester );
13653 
13654 static void		_GAITesterStartNextTest( GAITesterRef inTester );
13655 static OSStatus	_GAITesterCreatePacketCapture( pcap_t **outPCap );
13656 static void		_GAITesterFirstGAITimeout( void *inContext );
13657 static void		_GAITesterTimeout( void *inContext );
13658 static void DNSSD_API
13659 	_GAITesterFirstGAICallback(
13660 		DNSServiceRef			inSDRef,
13661 		DNSServiceFlags			inFlags,
13662 		uint32_t				inInterfaceIndex,
13663 		DNSServiceErrorType		inError,
13664 		const char *			inHostname,
13665 		const struct sockaddr *	inSockAddr,
13666 		uint32_t				inTTL,
13667 		void *					inContext );
13668 static void DNSSD_API
13669 	_GAITesterGetAddrInfoCallback(
13670 		DNSServiceRef			inSDRef,
13671 		DNSServiceFlags			inFlags,
13672 		uint32_t				inInterfaceIndex,
13673 		DNSServiceErrorType		inError,
13674 		const char *			inHostname,
13675 		const struct sockaddr *	inSockAddr,
13676 		uint32_t				inTTL,
13677 		void *					inContext );
13678 static void		_GAITesterCompleteCurrentTest( GAITesterRef inTester, OSStatus inError );
13679 
13680 #define ForgetPacketCapture( X )		ForgetCustom( X, pcap_close )
13681 
13682 static OSStatus
13683 	GAITestItemCreate(
13684 		const char *	inName,
13685 		unsigned int	inAddressCount,
13686 		GAITestAddrType	inHasAddrs,
13687 		GAITestAddrType	inWantAddrs,
13688 		unsigned int	inTimeLimitMs,
13689 		GAITestItem **	outItem );
13690 static OSStatus	GAITestItemDup( const GAITestItem *inItem, GAITestItem **outItem );
13691 static void		GAITestItemFree( GAITestItem *inItem );
13692 
13693 static OSStatus
GAITesterCreate(dispatch_queue_t inQueue,unsigned int inCallDelayMs,int inServerDelayMs,int inServerDefaultTTL,Boolean inSkipPathEvaluation,Boolean inBadUDPMode,GAITesterRef * outTester)13694 	GAITesterCreate(
13695 		dispatch_queue_t	inQueue,
13696 		unsigned int		inCallDelayMs,
13697 		int					inServerDelayMs,
13698 		int					inServerDefaultTTL,
13699 		Boolean				inSkipPathEvaluation,
13700 		Boolean				inBadUDPMode,
13701 		GAITesterRef *		outTester )
13702 {
13703 	OSStatus			err;
13704 	GAITesterRef		obj = NULL;
13705 
13706 	CF_OBJECT_CREATE( GAITester, obj, err, exit );
13707 
13708 	ReplaceDispatchQueue( &obj->queue, inQueue );
13709 	obj->callDelayMs		= inCallDelayMs;
13710 	obj->serverPID			= -1;
13711 	obj->serverDelayMs		= inServerDelayMs;
13712 	obj->serverDefaultTTL	= inServerDefaultTTL;
13713 	obj->skipPathEval		= inSkipPathEvaluation;
13714 	obj->badUDPMode			= inBadUDPMode;
13715 
13716 	*outTester = obj;
13717 	obj = NULL;
13718 	err = kNoErr;
13719 
13720 exit:
13721 	CFReleaseNullSafe( obj );
13722 	return( err );
13723 }
13724 
13725 //===========================================================================================================================
13726 //	_GAITesterFinalize
13727 //===========================================================================================================================
13728 
_GAITesterFinalize(CFTypeRef inObj)13729 static void	_GAITesterFinalize( CFTypeRef inObj )
13730 {
13731 	GAITesterRef const		me = (GAITesterRef) inObj;
13732 	GAITestCase *			testCase;
13733 
13734 	check( !me->getAddrInfo );
13735 	check( !me->connection );
13736 	check( !me->timer );
13737 	dispatch_forget( &me->queue );
13738 	while( ( testCase = me->caseList ) != NULL )
13739 	{
13740 		me->caseList = testCase->next;
13741 		GAITestCaseFree( testCase );
13742 	}
13743 }
13744 
13745 //===========================================================================================================================
13746 //	GAITesterStart
13747 //===========================================================================================================================
13748 
13749 static void	_GAITesterStart( void *inContext );
13750 static void	_GAITesterStop( GAITesterRef me, OSStatus inError );
13751 
GAITesterStart(GAITesterRef me)13752 static void	GAITesterStart( GAITesterRef me )
13753 {
13754 	CFRetain( me );
13755 	dispatch_async_f( me->queue, me, _GAITesterStart );
13756 }
13757 
13758 #define kGAITesterFirstGAITimeoutSecs		4
13759 
_GAITesterStart(void * inContext)13760 static void	_GAITesterStart( void *inContext )
13761 {
13762 	OSStatus				err;
13763 	GAITesterRef const		me = (GAITesterRef) inContext;
13764 	DNSServiceFlags			flags;
13765 	char					name[ 64 ];
13766 	char					tag[ kGAITesterTagStringLen + 1 ];
13767 
13768 	err = _SpawnCommand( &me->serverPID, NULL, NULL, "dnssdutil server --loopback --follow %lld%?s%?d%?s%?d%?s",
13769 		(int64_t) getpid(),
13770 		me->serverDefaultTTL >= 0,	" --defaultTTL ",
13771 		me->serverDefaultTTL >= 0,	me->serverDefaultTTL,
13772 		me->serverDelayMs    >= 0,	" --responseDelay ",
13773 		me->serverDelayMs    >= 0,	me->serverDelayMs,
13774 		me->badUDPMode,				" --badUDPMode" );
13775 	require_noerr_quiet( err, exit );
13776 
13777 	SNPrintF( name, sizeof( name ), "tag-gaitester-probe-%s.ipv4.d.test.",
13778 		_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
13779 
13780 	flags = 0;
13781 	if( me->skipPathEval ) flags |= kDNSServiceFlagsPathEvaluationDone;
13782 
13783 	err = DNSServiceGetAddrInfo( &me->getAddrInfo, flags, kDNSServiceInterfaceIndexAny, kDNSServiceProtocol_IPv4, name,
13784 		_GAITesterFirstGAICallback, me );
13785 	require_noerr( err, exit );
13786 
13787 	err = DNSServiceSetDispatchQueue( me->getAddrInfo, me->queue );
13788 	require_noerr( err, exit );
13789 
13790 	err = DispatchTimerOneShotCreate( dispatch_time_seconds( kGAITesterFirstGAITimeoutSecs ),
13791 		UINT64_C_safe( kGAITesterFirstGAITimeoutSecs ) * kNanosecondsPerSecond / 10, me->queue,
13792 		_GAITesterFirstGAITimeout, me, &me->timer );
13793 	require_noerr( err, exit );
13794 	dispatch_resume( me->timer );
13795 
13796 exit:
13797 	if( err ) _GAITesterStop( me, err );
13798 }
13799 
13800 //===========================================================================================================================
13801 //	GAITesterStop
13802 //===========================================================================================================================
13803 
13804 static void	_GAITesterUserStop( void *inContext );
13805 
GAITesterStop(GAITesterRef me)13806 static void	GAITesterStop( GAITesterRef me )
13807 {
13808 	CFRetain( me );
13809 	dispatch_async_f( me->queue, me, _GAITesterUserStop );
13810 }
13811 
_GAITesterUserStop(void * inContext)13812 static void	_GAITesterUserStop( void *inContext )
13813 {
13814 	GAITesterRef const		me = (GAITesterRef) inContext;
13815 
13816 	_GAITesterStop( me, kCanceledErr );
13817 	CFRelease( me );
13818 }
13819 
_GAITesterStop(GAITesterRef me,OSStatus inError)13820 static void	_GAITesterStop( GAITesterRef me, OSStatus inError )
13821 {
13822 	OSStatus		err;
13823 
13824 	ForgetPacketCapture( &me->pcap );
13825 	dispatch_source_forget( &me->timer );
13826 	DNSServiceForget( &me->getAddrInfo );
13827 	DNSServiceForget( &me->connection );
13828 	if( me->serverPID != -1 )
13829 	{
13830 		err = kill( me->serverPID, SIGTERM );
13831 		err = map_global_noerr_errno( err );
13832 		check_noerr( err );
13833 		me->serverPID = -1;
13834 	}
13835 
13836 	if( !me->stopped )
13837 	{
13838 		me->stopped = true;
13839 		if( me->stopHandler ) me->stopHandler( me->stopContext, inError );
13840 		CFRelease( me );
13841 	}
13842 }
13843 
13844 //===========================================================================================================================
13845 //	GAITesterAddTestCase
13846 //===========================================================================================================================
13847 
GAITesterAddTestCase(GAITesterRef me,GAITestCase * inCase)13848 static OSStatus	GAITesterAddTestCase( GAITesterRef me, GAITestCase *inCase )
13849 {
13850 	OSStatus			err;
13851 	GAITestCase **		ptr;
13852 
13853 	require_action_quiet( inCase->itemList, exit, err = kCountErr );
13854 
13855 	for( ptr = &me->caseList; *ptr; ptr = &( *ptr )->next ) {}
13856 	*ptr = inCase;
13857 	err = kNoErr;
13858 
13859 exit:
13860 	return( err );
13861 }
13862 
13863 //===========================================================================================================================
13864 //	GAITesterSetStopHandler
13865 //===========================================================================================================================
13866 
GAITesterSetStopHandler(GAITesterRef me,GAITesterStopHandler_f inStopHandler,void * inStopContext)13867 static void	GAITesterSetStopHandler( GAITesterRef me, GAITesterStopHandler_f inStopHandler, void *inStopContext )
13868 {
13869 	me->stopHandler = inStopHandler;
13870 	me->stopContext = inStopContext;
13871 }
13872 
13873 //===========================================================================================================================
13874 //	GAITesterSetResultsHandler
13875 //===========================================================================================================================
13876 
GAITesterSetResultsHandler(GAITesterRef me,GAITesterResultsHandler_f inResultsHandler,void * inResultsContext)13877 static void	GAITesterSetResultsHandler( GAITesterRef me, GAITesterResultsHandler_f inResultsHandler, void *inResultsContext )
13878 {
13879 	me->resultsHandler = inResultsHandler;
13880 	me->resultsContext = inResultsContext;
13881 }
13882 
13883 //===========================================================================================================================
13884 //	_GAITesterStartNextTest
13885 //===========================================================================================================================
13886 
_GAITesterStartNextTest(GAITesterRef me)13887 static void	_GAITesterStartNextTest( GAITesterRef me )
13888 {
13889 	OSStatus				err;
13890 	GAITestItem *			item;
13891 	DNSServiceFlags			flags;
13892 	DNSServiceProtocol		protocols;
13893 	int						done = false;
13894 
13895 	if( me->currentItem ) me->currentItem = me->currentItem->next;
13896 
13897 	if( !me->currentItem )
13898 	{
13899 		if( me->currentCase )
13900 		{
13901 			// No more test items means that the current test case has completed.
13902 
13903 			me->caseEndTime = NanoTimeGetCurrent();
13904 
13905 			if( me->resultsHandler )
13906 			{
13907 				size_t					resultCount, i;
13908 				GAITestItemResult *		resultArray;
13909 
13910 				resultCount	= 0;
13911 				for( item = me->currentCase->itemList; item; item = item->next ) ++resultCount;
13912 				check( resultCount > 0 );
13913 
13914 				resultArray = (GAITestItemResult *) calloc( resultCount, sizeof( *resultArray ) );
13915 				require_action( resultArray, exit, err = kNoMemoryErr );
13916 
13917 				item = me->currentCase->itemList;
13918 				for( i = 0; i < resultCount; ++i )
13919 				{
13920 					resultArray[ i ].name				= item->name;
13921 					resultArray[ i ].connectionTimeUs	= item->connectionTimeUs;
13922 					resultArray[ i ].firstTimeUs		= item->firstTimeUs;
13923 					resultArray[ i ].timeUs				= item->timeUs;
13924 					resultArray[ i ].error				= item->error;
13925 					item = item->next;
13926 				}
13927 				me->resultsHandler( me->currentCase->title, me->caseStartTime, me->caseEndTime, resultArray, resultCount,
13928 					me->resultsContext );
13929 				ForgetMem( &resultArray );
13930 			}
13931 
13932 			me->currentCase = me->currentCase->next;
13933 			if( !me->currentCase )
13934 			{
13935 				done = true;
13936 				err = kNoErr;
13937 				goto exit;
13938 			}
13939 		}
13940 		else
13941 		{
13942 			me->currentCase = me->caseList;
13943 		}
13944 		require_action_quiet( me->currentCase->itemList, exit, err = kInternalErr );
13945 		me->currentItem = me->currentCase->itemList;
13946 	}
13947 
13948 	item = me->currentItem;
13949 	check( ( item->addressCount >= 1 ) && ( item->addressCount <= 64 ) );
13950 
13951 	if(      !item->wantV4 )			me->bitmapV4 = 0;
13952 	else if( !item->hasV4 )				me->bitmapV4 = 1;
13953 	else if(  item->addressCount < 64 )	me->bitmapV4 = ( UINT64_C( 1 ) << item->addressCount ) - 1;
13954 	else								me->bitmapV4 =  ~UINT64_C( 0 );
13955 
13956 	if(      !item->wantV6 )			me->bitmapV6 = 0;
13957 	else if( !item->hasV6 )				me->bitmapV6 = 1;
13958 	else if(  item->addressCount < 64 )	me->bitmapV6 = ( UINT64_C( 1 ) << item->addressCount ) - 1;
13959 	else								me->bitmapV6 =  ~UINT64_C( 0 );
13960 	check( ( me->bitmapV4 != 0 ) || ( me->bitmapV6 != 0 ) );
13961 	me->gotFirstResult = false;
13962 
13963 	// Perform preliminary tasks if this is the start of a new test case.
13964 
13965 	if( item == me->currentCase->itemList )
13966 	{
13967 		// Flush mDNSResponder's cache.
13968 
13969 		err = systemf( NULL, "killall -HUP mDNSResponder" );
13970 		require_noerr( err, exit );
13971 		sleep( 1 );
13972 
13973 		me->caseStartTime	= NanoTimeGetCurrent();
13974 		me->caseEndTime		= kNanoTime_Invalid;
13975 	}
13976 
13977 	// Start a packet capture.
13978 
13979 	check( !me->pcap );
13980 	err = _GAITesterCreatePacketCapture( &me->pcap );
13981 	require_noerr( err, exit );
13982 
13983 	// Start timer for test item's time limit.
13984 
13985 	check( !me->timer );
13986 	if( item->timeLimitMs > 0 )
13987 	{
13988 		unsigned int		timeLimitMs;
13989 
13990 		timeLimitMs = item->timeLimitMs;
13991 		if( me->callDelayMs   > 0 ) timeLimitMs += (unsigned int) me->callDelayMs;
13992 		if( me->serverDelayMs > 0 ) timeLimitMs += (unsigned int) me->serverDelayMs;
13993 
13994 		err = DispatchTimerCreate( dispatch_time_milliseconds( timeLimitMs ), DISPATCH_TIME_FOREVER,
13995 			( (uint64_t) timeLimitMs ) * kNanosecondsPerMillisecond / 10,
13996 			me->queue, _GAITesterTimeout, NULL, me, &me->timer );
13997 		require_noerr( err, exit );
13998 		dispatch_resume( me->timer );
13999 	}
14000 
14001 	// Call DNSServiceGetAddrInfo().
14002 
14003 	if( me->callDelayMs > 0 ) usleep( ( (useconds_t) me->callDelayMs ) * kMicrosecondsPerMillisecond );
14004 
14005 	flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates;
14006 	if( me->skipPathEval ) flags |= kDNSServiceFlagsPathEvaluationDone;
14007 
14008 	protocols = 0;
14009 	if( item->wantV4 ) protocols |= kDNSServiceProtocol_IPv4;
14010 	if( item->wantV6 ) protocols |= kDNSServiceProtocol_IPv6;
14011 
14012 	me->startTicks = UpTicks();
14013 
14014 	check( !me->connection );
14015 	err = DNSServiceCreateConnection( &me->connection );
14016 	require_noerr( err, exit );
14017 
14018 	err = DNSServiceSetDispatchQueue( me->connection, me->queue );
14019 	require_noerr( err, exit );
14020 
14021 	me->connTicks = UpTicks();
14022 
14023 	check( !me->getAddrInfo );
14024 	me->getAddrInfo = me->connection;
14025 	err = DNSServiceGetAddrInfo( &me->getAddrInfo, flags, kDNSServiceInterfaceIndexAny, protocols, item->name,
14026 		_GAITesterGetAddrInfoCallback, me );
14027 	require_noerr( err, exit );
14028 
14029 exit:
14030 	if( err || done ) _GAITesterStop( me, err );
14031 }
14032 
14033 //===========================================================================================================================
14034 //	_GAITesterCreatePacketCapture
14035 //===========================================================================================================================
14036 
_GAITesterCreatePacketCapture(pcap_t ** outPCap)14037 static OSStatus	_GAITesterCreatePacketCapture( pcap_t **outPCap )
14038 {
14039 	OSStatus				err;
14040 	pcap_t *				pcap;
14041 	struct bpf_program		program;
14042 	char					errBuf[ PCAP_ERRBUF_SIZE ];
14043 
14044 	pcap = pcap_create( "lo0", errBuf );
14045 	require_action_string( pcap, exit, err = kUnknownErr, errBuf );
14046 
14047 	err = pcap_set_buffer_size( pcap, 512 * kBytesPerKiloByte );
14048 	require_noerr_action( err, exit, err = kUnknownErr );
14049 
14050 	err = pcap_set_snaplen( pcap, 512 );
14051 	require_noerr_action( err, exit, err = kUnknownErr );
14052 
14053 	err = pcap_set_immediate_mode( pcap, 0 );
14054 	require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
14055 
14056 	err = pcap_activate( pcap );
14057 	require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
14058 
14059 	err = pcap_setdirection( pcap, PCAP_D_INOUT );
14060 	require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
14061 
14062 	err = pcap_setnonblock( pcap, 1, errBuf );
14063 	require_noerr_action_string( err, exit, err = kUnknownErr, errBuf );
14064 
14065 	err = pcap_compile( pcap, &program, "udp port 53", 1, PCAP_NETMASK_UNKNOWN );
14066 	require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
14067 
14068 	err = pcap_setfilter( pcap, &program );
14069 	pcap_freecode( &program );
14070 	require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) );
14071 
14072 	*outPCap = pcap;
14073 	pcap = NULL;
14074 
14075 exit:
14076 	if( pcap ) pcap_close( pcap );
14077 	return( err );
14078 }
14079 
14080 //===========================================================================================================================
14081 //	_GAITesterFirstGAITimeout
14082 //===========================================================================================================================
14083 
_GAITesterFirstGAITimeout(void * inContext)14084 static void	_GAITesterFirstGAITimeout( void *inContext )
14085 {
14086 	GAITesterRef const		me = (GAITesterRef) inContext;
14087 
14088 	_GAITesterStop( me, kNoResourcesErr );
14089 }
14090 
14091 //===========================================================================================================================
14092 //	_GAITesterTimeout
14093 //===========================================================================================================================
14094 
_GAITesterTimeout(void * inContext)14095 static void	_GAITesterTimeout( void *inContext )
14096 {
14097 	GAITesterRef const		me = (GAITesterRef) inContext;
14098 
14099 	_GAITesterCompleteCurrentTest( me, kTimeoutErr );
14100 }
14101 
14102 //===========================================================================================================================
14103 //	_GAITesterFirstGAICallback
14104 //===========================================================================================================================
14105 
14106 static void DNSSD_API
_GAITesterFirstGAICallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inHostname,const struct sockaddr * inSockAddr,uint32_t inTTL,void * inContext)14107 	_GAITesterFirstGAICallback(
14108 		DNSServiceRef			inSDRef,
14109 		DNSServiceFlags			inFlags,
14110 		uint32_t				inInterfaceIndex,
14111 		DNSServiceErrorType		inError,
14112 		const char *			inHostname,
14113 		const struct sockaddr *	inSockAddr,
14114 		uint32_t				inTTL,
14115 		void *					inContext )
14116 {
14117 	GAITesterRef const		me = (GAITesterRef) inContext;
14118 
14119 	Unused( inSDRef );
14120 	Unused( inInterfaceIndex );
14121 	Unused( inHostname );
14122 	Unused( inSockAddr );
14123 	Unused( inTTL );
14124 
14125 	if( ( inFlags & kDNSServiceFlagsAdd ) && !inError )
14126 	{
14127 		dispatch_source_forget( &me->timer );
14128 		DNSServiceForget( &me->getAddrInfo );
14129 
14130 		_GAITesterStartNextTest( me );
14131 	}
14132 }
14133 
14134 //===========================================================================================================================
14135 //	_GAITesterGetAddrInfoCallback
14136 //===========================================================================================================================
14137 
14138 static void DNSSD_API
_GAITesterGetAddrInfoCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inHostname,const struct sockaddr * inSockAddr,uint32_t inTTL,void * inContext)14139 	_GAITesterGetAddrInfoCallback(
14140 		DNSServiceRef			inSDRef,
14141 		DNSServiceFlags			inFlags,
14142 		uint32_t				inInterfaceIndex,
14143 		DNSServiceErrorType		inError,
14144 		const char *			inHostname,
14145 		const struct sockaddr *	inSockAddr,
14146 		uint32_t				inTTL,
14147 		void *					inContext )
14148 {
14149 	OSStatus						err;
14150 	GAITesterRef const				me		= (GAITesterRef) inContext;
14151 	GAITestItem * const				item	= me->currentItem;
14152 	const sockaddr_ip * const		sip		= (const sockaddr_ip *) inSockAddr;
14153 	uint64_t						nowTicks;
14154 	uint64_t *						bitmapPtr;
14155 	uint64_t						bitmask;
14156 	int								hasAddr;
14157 
14158 	Unused( inSDRef );
14159 	Unused( inInterfaceIndex );
14160 	Unused( inHostname );
14161 	Unused( inTTL );
14162 
14163 	nowTicks = UpTicks();
14164 
14165 	require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
14166 
14167 	// Check if we were expecting an IP address result of this type.
14168 
14169 	if( sip->sa.sa_family == AF_INET )
14170 	{
14171 		bitmapPtr	= &me->bitmapV4;
14172 		hasAddr		= item->hasV4;
14173 	}
14174 	else if( sip->sa.sa_family == AF_INET6 )
14175 	{
14176 		bitmapPtr	= &me->bitmapV6;
14177 		hasAddr		= item->hasV6;
14178 	}
14179 	else
14180 	{
14181 		err = kTypeErr;
14182 		goto exit;
14183 	}
14184 
14185 	bitmask = 0;
14186 	if( hasAddr )
14187 	{
14188 		uint32_t		addrOffset;
14189 
14190 		require_noerr_action_quiet( inError, exit, err = inError );
14191 
14192 		if( sip->sa.sa_family == AF_INET )
14193 		{
14194 			const uint32_t		addrV4 = ntohl( sip->v4.sin_addr.s_addr );
14195 
14196 			if( strcasecmp( item->name, "localhost." ) == 0 )
14197 			{
14198 				if( addrV4 == INADDR_LOOPBACK ) bitmask = 1;
14199 			}
14200 			else
14201 			{
14202 				addrOffset = addrV4 - kDNSServerBaseAddrV4;
14203 				if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) )
14204 				{
14205 					bitmask = UINT64_C( 1 ) << ( addrOffset - 1 );
14206 				}
14207 			}
14208 		}
14209 		else
14210 		{
14211 			const uint8_t * const		addrV6 = sip->v6.sin6_addr.s6_addr;
14212 
14213 			if( strcasecmp( item->name, "localhost." ) == 0 )
14214 			{
14215 				if( memcmp( addrV6, in6addr_loopback.s6_addr, 16 ) == 0 ) bitmask = 1;
14216 			}
14217 			else if( memcmp( addrV6, kDNSServerBaseAddrV6, 15 ) == 0 )
14218 			{
14219 				addrOffset = addrV6[ 15 ];
14220 				if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) )
14221 				{
14222 					bitmask = UINT64_C( 1 ) << ( addrOffset - 1 );
14223 				}
14224 			}
14225 		}
14226 	}
14227 	else
14228 	{
14229 		require_action_quiet( inError == kDNSServiceErr_NoSuchRecord, exit, err = inError ? inError : kUnexpectedErr );
14230 		bitmask = 1;
14231 	}
14232 	require_action_quiet( bitmask != 0, exit, err = kValueErr );
14233 	require_action_quiet( *bitmapPtr & bitmask, exit, err = kDuplicateErr );
14234 
14235 	*bitmapPtr &= ~bitmask;
14236 	if( !me->gotFirstResult )
14237 	{
14238 		me->firstTicks		= nowTicks;
14239 		me->gotFirstResult	= true;
14240 	}
14241 	err = kNoErr;
14242 
14243 exit:
14244 	if( err || ( ( me->bitmapV4 == 0 ) && ( me->bitmapV6 == 0 ) ) )
14245 	{
14246 		me->endTicks = nowTicks;
14247 		_GAITesterCompleteCurrentTest( me, err );
14248 	}
14249 }
14250 
14251 //===========================================================================================================================
14252 //	_GAITesterCompleteCurrentTest
14253 //===========================================================================================================================
14254 
14255 static OSStatus
14256 	_GAITesterGetDNSMessageFromPacket(
14257 		const uint8_t *		inPacketPtr,
14258 		size_t				inPacketLen,
14259 		const uint8_t **	outMsgPtr,
14260 		size_t *			outMsgLen );
14261 
_GAITesterCompleteCurrentTest(GAITesterRef me,OSStatus inError)14262 static void	_GAITesterCompleteCurrentTest( GAITesterRef me, OSStatus inError )
14263 {
14264 	OSStatus				err;
14265 	GAITestItem * const		item	= me->currentItem;
14266 	struct timeval			timeStamps[ 4 ];
14267 	struct timeval *		tsPtr;
14268 	struct timeval *		tsQA	= NULL;
14269 	struct timeval *		tsQAAAA	= NULL;
14270 	struct timeval *		tsRA	= NULL;
14271 	struct timeval *		tsRAAAA	= NULL;
14272 	struct timeval *		t1;
14273 	struct timeval *		t2;
14274 	int64_t					idleTimeUs;
14275 	uint8_t					name[ kDomainNameLengthMax ];
14276 
14277 	dispatch_source_forget( &me->timer );
14278 	DNSServiceForget( &me->getAddrInfo );
14279 	DNSServiceForget( &me->connection );
14280 
14281 	item->error = inError;
14282 	if( item->error )
14283 	{
14284 		err = kNoErr;
14285 		goto exit;
14286 	}
14287 
14288 	err = DomainNameFromString( name, item->name, NULL );
14289 	require_noerr( err, exit );
14290 
14291 	tsPtr = &timeStamps[ 0 ];
14292 	for( ;; )
14293 	{
14294 		int							status;
14295 		struct pcap_pkthdr *		pktHdr;
14296 		const uint8_t *				packet;
14297 		const uint8_t *				msgPtr;
14298 		size_t						msgLen;
14299 		const DNSHeader *			hdr;
14300 		unsigned int				flags;
14301 		const uint8_t *				ptr;
14302 		uint16_t					qtype, qclass;
14303 		uint8_t						qname[ kDomainNameLengthMax ];
14304 
14305 		status = pcap_next_ex( me->pcap, &pktHdr, &packet );
14306 		if( status != 1 ) break;
14307 		if( _GAITesterGetDNSMessageFromPacket( packet, pktHdr->caplen, &msgPtr, &msgLen ) != kNoErr ) continue;
14308 		if( msgLen < kDNSHeaderLength ) continue;
14309 
14310 		hdr = (const DNSHeader *) msgPtr;
14311 		flags = DNSHeaderGetFlags( hdr );
14312 		if( DNSFlagsGetOpCode( flags ) != kDNSOpCode_Query ) continue;
14313 		if( DNSHeaderGetQuestionCount( hdr ) < 1 ) continue;
14314 
14315 		ptr = (const uint8_t *) &hdr[ 1 ];
14316 		if( DNSMessageExtractQuestion( msgPtr, msgLen, ptr, qname, &qtype, &qclass, NULL ) != kNoErr ) continue;
14317 		if( qclass != kDNSServiceClass_IN ) continue;
14318 		if( !DomainNameEqual( qname, name ) ) continue;
14319 
14320 		if( item->wantV4 && ( qtype == kDNSServiceType_A ) )
14321 		{
14322 			if( flags & kDNSHeaderFlag_Response )
14323 			{
14324 				if( tsQA && !tsRA )
14325 				{
14326 					tsRA  = tsPtr++;
14327 					*tsRA = pktHdr->ts;
14328 				}
14329 			}
14330 			else if( !tsQA )
14331 			{
14332 				tsQA  = tsPtr++;
14333 				*tsQA = pktHdr->ts;
14334 			}
14335 		}
14336 		else if( item->wantV6 && ( qtype == kDNSServiceType_AAAA ) )
14337 		{
14338 			if( flags & kDNSHeaderFlag_Response )
14339 			{
14340 				if( tsQAAAA && !tsRAAAA )
14341 				{
14342 					tsRAAAA  = tsPtr++;
14343 					*tsRAAAA = pktHdr->ts;
14344 				}
14345 			}
14346 			else if( !tsQAAAA )
14347 			{
14348 				tsQAAAA  = tsPtr++;
14349 				*tsQAAAA = pktHdr->ts;
14350 			}
14351 		}
14352 	}
14353 
14354 	// t1 is the time when the last query was sent.
14355 
14356 	if( tsQA && tsQAAAA )	t1 = TIMEVAL_GT( *tsQA, *tsQAAAA ) ? tsQA : tsQAAAA;
14357 	else					t1 = tsQA ? tsQA : tsQAAAA;
14358 
14359 	// t2 is when the first response was received.
14360 
14361 	if( tsRA && tsRAAAA )	t2 = TIMEVAL_LT( *tsRA, *tsRAAAA ) ? tsRA : tsRAAAA;
14362 	else					t2 = tsRA ? tsRA : tsRAAAA;
14363 
14364 	if( t1 && t2 )
14365 	{
14366 		idleTimeUs = TIMEVAL_USEC64_DIFF( *t2, *t1 );
14367 		if( idleTimeUs < 0 ) idleTimeUs = 0;
14368 	}
14369 	else
14370 	{
14371 		idleTimeUs = 0;
14372 	}
14373 
14374 	item->connectionTimeUs	= UpTicksToMicroseconds( me->connTicks  - me->startTicks );
14375 	item->firstTimeUs		= UpTicksToMicroseconds( me->firstTicks - me->connTicks  ) - (uint64_t) idleTimeUs;
14376 	item->timeUs			= UpTicksToMicroseconds( me->endTicks   - me->connTicks  ) - (uint64_t) idleTimeUs;
14377 
14378 exit:
14379 	ForgetPacketCapture( &me->pcap );
14380 	if( err )	_GAITesterStop( me, err );
14381 	else		_GAITesterStartNextTest( me );
14382 }
14383 
14384 //===========================================================================================================================
14385 //	_GAITesterGetDNSMessageFromPacket
14386 //===========================================================================================================================
14387 
14388 #define kHeaderSizeNullLink		 4
14389 #define kHeaderSizeIPv4Min		20
14390 #define kHeaderSizeIPv6			40
14391 #define kHeaderSizeUDP			 8
14392 
14393 #define kIPProtocolUDP		0x11
14394 
14395 static OSStatus
_GAITesterGetDNSMessageFromPacket(const uint8_t * inPacketPtr,size_t inPacketLen,const uint8_t ** outMsgPtr,size_t * outMsgLen)14396 	_GAITesterGetDNSMessageFromPacket(
14397 		const uint8_t *		inPacketPtr,
14398 		size_t				inPacketLen,
14399 		const uint8_t **	outMsgPtr,
14400 		size_t *			outMsgLen )
14401 {
14402 	OSStatus					err;
14403 	const uint8_t *				nullLink;
14404 	uint32_t					addressFamily;
14405 	const uint8_t *				ip;
14406 	int							ipHeaderLen;
14407 	int							protocol;
14408 	const uint8_t *				msg;
14409 	const uint8_t * const		end = &inPacketPtr[ inPacketLen ];
14410 
14411 	nullLink = &inPacketPtr[ 0 ];
14412 	require_action_quiet( ( end - nullLink ) >= kHeaderSizeNullLink, exit, err = kUnderrunErr );
14413 	addressFamily = ReadHost32( &nullLink[ 0 ] );
14414 
14415 	ip = &nullLink[ kHeaderSizeNullLink ];
14416 	if( addressFamily == AF_INET )
14417 	{
14418 		require_action_quiet( ( end - ip ) >= kHeaderSizeIPv4Min, exit, err = kUnderrunErr );
14419 		ipHeaderLen	= ( ip[ 0 ] & 0x0F ) * 4;
14420 		protocol	=   ip[ 9 ];
14421 	}
14422 	else if( addressFamily == AF_INET6 )
14423 	{
14424 		require_action_quiet( ( end - ip ) >= kHeaderSizeIPv6, exit, err = kUnderrunErr );
14425 		ipHeaderLen	= kHeaderSizeIPv6;
14426 		protocol	= ip[ 6 ];
14427 	}
14428 	else
14429 	{
14430 		err = kTypeErr;
14431 		goto exit;
14432 	}
14433 	require_action_quiet( protocol == kIPProtocolUDP, exit, err = kTypeErr );
14434 	require_action_quiet( ( end - ip ) >= ( ipHeaderLen + kHeaderSizeUDP ), exit, err = kUnderrunErr );
14435 
14436 	msg = &ip[ ipHeaderLen + kHeaderSizeUDP ];
14437 
14438 	*outMsgPtr = msg;
14439 	*outMsgLen = (size_t)( end - msg );
14440 	err = kNoErr;
14441 
14442 exit:
14443 	return( err );
14444 }
14445 
14446 //===========================================================================================================================
14447 //	GAITestCaseCreate
14448 //===========================================================================================================================
14449 
GAITestCaseCreate(const char * inTitle,GAITestCase ** outCase)14450 static OSStatus	GAITestCaseCreate( const char *inTitle, GAITestCase **outCase )
14451 {
14452 	OSStatus			err;
14453 	GAITestCase *		obj;
14454 
14455 	obj = (GAITestCase *) calloc( 1, sizeof( *obj ) );
14456 	require_action( obj, exit, err = kNoMemoryErr );
14457 
14458 	obj->title = strdup( inTitle );
14459 	require_action( obj->title, exit, err = kNoMemoryErr );
14460 
14461 	*outCase = obj;
14462 	obj = NULL;
14463 	err = kNoErr;
14464 
14465 exit:
14466 	if( obj ) GAITestCaseFree( obj );
14467 	return( err );
14468 }
14469 
14470 //===========================================================================================================================
14471 //	GAITestCaseFree
14472 //===========================================================================================================================
14473 
GAITestCaseFree(GAITestCase * inCase)14474 static void	GAITestCaseFree( GAITestCase *inCase )
14475 {
14476 	GAITestItem *		item;
14477 
14478 	while( ( item = inCase->itemList ) != NULL )
14479 	{
14480 		inCase->itemList = item->next;
14481 		GAITestItemFree( item );
14482 	}
14483 	ForgetMem( &inCase->title );
14484 	free( inCase );
14485 }
14486 
14487 //===========================================================================================================================
14488 //	GAITestCaseAddItem
14489 //===========================================================================================================================
14490 
14491 static OSStatus
GAITestCaseAddItem(GAITestCase * inCase,unsigned int inAliasCount,unsigned int inAddressCount,int inTTL,GAITestAddrType inHasAddrs,GAITestAddrType inWantAddrs,unsigned int inTimeLimitMs,unsigned int inItemCount)14492 	GAITestCaseAddItem(
14493 		GAITestCase *	inCase,
14494 		unsigned int	inAliasCount,
14495 		unsigned int	inAddressCount,
14496 		int				inTTL,
14497 		GAITestAddrType	inHasAddrs,
14498 		GAITestAddrType	inWantAddrs,
14499 		unsigned int	inTimeLimitMs,
14500 		unsigned int	inItemCount )
14501 {
14502 	OSStatus			err;
14503 	GAITestItem *		item;
14504 	GAITestItem *		item2;
14505 	GAITestItem *		newItemList = NULL;
14506 	GAITestItem **		itemPtr;
14507 	char *				ptr;
14508 	char *				end;
14509 	unsigned int		i;
14510 	char				name[ 64 ];
14511 	char				tag[ kGAITesterTagStringLen + 1 ];
14512 
14513 	require_action_quiet( inItemCount > 0, exit, err = kNoErr );
14514 
14515 	// Limit address count to 64 because we use 64-bit bitmaps for keeping track of addresses.
14516 
14517 	require_action_quiet( ( inAddressCount >= 1 ) && ( inAddressCount <= 64 ), exit, err = kCountErr );
14518 	require_action_quiet( ( inAliasCount >= 0 ) && ( inAliasCount <= INT32_MAX ), exit, err = kCountErr );
14519 	require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr );
14520 
14521 	ptr = &name[ 0 ];
14522 	end = &name[ countof( name ) ];
14523 
14524 	// Add Alias label.
14525 
14526 	if(      inAliasCount == 1 ) SNPrintF_Add( &ptr, end, "alias." );
14527 	else if( inAliasCount >= 2 ) SNPrintF_Add( &ptr, end, "alias-%u.", inAliasCount );
14528 
14529 	// Add Count label.
14530 
14531 	SNPrintF_Add( &ptr, end, "count-%u.", inAddressCount );
14532 
14533 	// Add TTL label.
14534 
14535 	if( inTTL >= 0 ) SNPrintF_Add( &ptr, end, "ttl-%d.", inTTL );
14536 
14537 	// Add Tag label.
14538 
14539 	SNPrintF_Add( &ptr, end, "tag-%s.",
14540 		_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
14541 
14542 	// Add IPv4 or IPv6 label if necessary.
14543 
14544 	if(      inHasAddrs == kGAITestAddrType_IPv4 ) SNPrintF_Add( &ptr, end, "ipv4." );
14545 	else if( inHasAddrs == kGAITestAddrType_IPv6 ) SNPrintF_Add( &ptr, end, "ipv6." );
14546 
14547 	// Finally, add the d.test. labels.
14548 
14549 	SNPrintF_Add( &ptr, end, "d.test." );
14550 
14551 	// Create item.
14552 
14553 	err = GAITestItemCreate( name, inAddressCount, inHasAddrs, inWantAddrs, inTimeLimitMs, &item );
14554 	require_noerr( err, exit );
14555 
14556 	newItemList	= item;
14557 	itemPtr		= &item->next;
14558 
14559 	// Create repeat items.
14560 
14561 	for( i = 1; i < inItemCount; ++i )
14562 	{
14563 		err = GAITestItemDup( item, &item2 );
14564 		require_noerr( err, exit );
14565 
14566 		*itemPtr	= item2;
14567 		itemPtr		= &item2->next;
14568 	}
14569 
14570 	// Append to test case's item list.
14571 
14572 	for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {}
14573 	*itemPtr	= newItemList;
14574 	newItemList	= NULL;
14575 
14576 exit:
14577 	while( ( item = newItemList ) != NULL )
14578 	{
14579 		newItemList = item->next;
14580 		GAITestItemFree( item );
14581 	}
14582 	return( err );
14583 }
14584 
14585 //===========================================================================================================================
14586 //	GAITestCaseAddLocalHostItem
14587 //===========================================================================================================================
14588 
14589 static OSStatus
GAITestCaseAddLocalHostItem(GAITestCase * inCase,GAITestAddrType inWantAddrs,unsigned int inTimeLimitMs,unsigned int inItemCount)14590 	GAITestCaseAddLocalHostItem(
14591 		GAITestCase *	inCase,
14592 		GAITestAddrType	inWantAddrs,
14593 		unsigned int	inTimeLimitMs,
14594 		unsigned int	inItemCount )
14595 {
14596 	OSStatus			err;
14597 	GAITestItem *		item;
14598 	GAITestItem *		item2;
14599 	GAITestItem *		newItemList = NULL;
14600 	GAITestItem **		itemPtr;
14601 	unsigned int		i;
14602 
14603 	require_action_quiet( inItemCount > 1, exit, err = kNoErr );
14604 
14605 	err = GAITestItemCreate( "localhost.", 1, kGAITestAddrType_Both, inWantAddrs, inTimeLimitMs, &item );
14606 	require_noerr( err, exit );
14607 
14608 	newItemList	= item;
14609 	itemPtr		= &item->next;
14610 
14611 	// Create repeat items.
14612 
14613 	for( i = 1; i < inItemCount; ++i )
14614 	{
14615 		err = GAITestItemDup( item, &item2 );
14616 		require_noerr( err, exit );
14617 
14618 		*itemPtr	= item2;
14619 		itemPtr		= &item2->next;
14620 	}
14621 
14622 	for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {}
14623 	*itemPtr	= newItemList;
14624 	newItemList	= NULL;
14625 
14626 exit:
14627 	while( ( item = newItemList ) != NULL )
14628 	{
14629 		newItemList = item->next;
14630 		GAITestItemFree( item );
14631 	}
14632 	return( err );
14633 }
14634 
14635 //===========================================================================================================================
14636 //	GAITestItemCreate
14637 //===========================================================================================================================
14638 
14639 static OSStatus
GAITestItemCreate(const char * inName,unsigned int inAddressCount,GAITestAddrType inHasAddrs,GAITestAddrType inWantAddrs,unsigned int inTimeLimitMs,GAITestItem ** outItem)14640 	GAITestItemCreate(
14641 		const char *	inName,
14642 		unsigned int	inAddressCount,
14643 		GAITestAddrType	inHasAddrs,
14644 		GAITestAddrType	inWantAddrs,
14645 		unsigned int	inTimeLimitMs,
14646 		GAITestItem **	outItem )
14647 {
14648 	OSStatus			err;
14649 	GAITestItem *		obj = NULL;
14650 
14651 	require_action_quiet( inAddressCount >= 1, exit, err = kCountErr );
14652 	require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr );
14653 	require_action_quiet( GAITestAddrTypeIsValid( inWantAddrs ), exit, err = kValueErr );
14654 
14655 	obj = (GAITestItem *) calloc( 1, sizeof( *obj ) );
14656 	require_action( obj, exit, err = kNoMemoryErr );
14657 
14658 	obj->name = strdup( inName );
14659 	require_action( obj->name, exit, err = kNoMemoryErr );
14660 
14661 	obj->addressCount	= inAddressCount;
14662 	obj->hasV4			= ( inHasAddrs  & kGAITestAddrType_IPv4 ) ? true : false;
14663 	obj->hasV6			= ( inHasAddrs  & kGAITestAddrType_IPv6 ) ? true : false;
14664 	obj->wantV4			= ( inWantAddrs & kGAITestAddrType_IPv4 ) ? true : false;
14665 	obj->wantV6			= ( inWantAddrs & kGAITestAddrType_IPv6 ) ? true : false;
14666 	obj->error			= kInProgressErr;
14667 	obj->timeLimitMs	= inTimeLimitMs;
14668 
14669 	*outItem = obj;
14670 	obj = NULL;
14671 	err = kNoErr;
14672 
14673 exit:
14674 	if( obj ) GAITestItemFree( obj );
14675 	return( err );
14676 }
14677 
14678 //===========================================================================================================================
14679 //	GAITestItemDup
14680 //===========================================================================================================================
14681 
GAITestItemDup(const GAITestItem * inItem,GAITestItem ** outItem)14682 static OSStatus	GAITestItemDup( const GAITestItem *inItem, GAITestItem **outItem )
14683 {
14684 	OSStatus			err;
14685 	GAITestItem *		obj;
14686 
14687 	obj = (GAITestItem *) calloc( 1, sizeof( *obj ) );
14688 	require_action( obj, exit, err = kNoMemoryErr );
14689 
14690 	*obj = *inItem;
14691 	obj->next = NULL;
14692 	if( inItem->name )
14693 	{
14694 		obj->name = strdup( inItem->name );
14695 		require_action( obj->name, exit, err = kNoMemoryErr );
14696 	}
14697 
14698 	*outItem = obj;
14699 	obj = NULL;
14700 	err = kNoErr;
14701 
14702 exit:
14703 	if( obj ) GAITestItemFree( obj );
14704 	return( err );
14705 }
14706 
14707 //===========================================================================================================================
14708 //	GAITestItemFree
14709 //===========================================================================================================================
14710 
GAITestItemFree(GAITestItem * inItem)14711 static void	GAITestItemFree( GAITestItem *inItem )
14712 {
14713 	ForgetMem( &inItem->name );
14714 	free( inItem );
14715 }
14716 
14717 //===========================================================================================================================
14718 //	MDNSDiscoveryTestCmd
14719 //===========================================================================================================================
14720 
14721 #define kMDNSDiscoveryTestFirstQueryTimeoutSecs		4
14722 
14723 typedef struct
14724 {
14725 	DNSServiceRef			query;					// Reference to DNSServiceQueryRecord for replier's "about" TXT record.
14726 	dispatch_source_t		queryTimer;				// Used to time out the "about" TXT record query.
14727 	NanoTime64				startTime;				// When the test started.
14728 	NanoTime64				endTime;				// When the test ended.
14729 	pid_t					replierPID;				// PID of mDNS replier.
14730 	uint32_t				ifIndex;				// Index of interface to run the replier on.
14731 	unsigned int			instanceCount;			// Desired number of service instances.
14732 	unsigned int			txtSize;				// Desired size of each service instance's TXT record data.
14733 	unsigned int			recordCountA;			// Desired number of A records per replier hostname.
14734 	unsigned int			recordCountAAAA;		// Desired number of AAAA records per replier hostname.
14735 	unsigned int			maxDropCount;			// Replier's --maxDropCount option argument.
14736 	double					ucastDropRate;			// Replier's probability of dropping a unicast response.
14737 	double					mcastDropRate;			// Replier's probability of dropping a multicast query or response.
14738 	Boolean					noAdditionals;			// True if the replier is to not include additional records in responses.
14739 	Boolean					useIPv4;				// True if the replier is to use IPv4.
14740 	Boolean					useIPv6;				// True if the replier is to use IPv6.
14741 #if( MDNSRESPONDER_PROJECT )
14742 	Boolean					useNewGAI;				// True if the browser is to use dnssd_getaddrinfo to resolve hostnames.
14743 #endif
14744 	Boolean					flushedCache;			// True if mDNSResponder's record cache was flushed before testing.
14745 	char *					replierCommand;			// Command used to run the replier.
14746 	char *					serviceType;			// Type of services to browse for.
14747 	ServiceBrowserRef		browser;				// Service browser.
14748 	unsigned int			browseTimeSecs;			// Amount of time to spend browsing in seconds.
14749 	const char *			outputFilePath;			// File to write test results to. If NULL, then write to stdout.
14750 	OutputFormatType		outputFormat;			// Format of test results output.
14751 	Boolean					outputAppendNewline;	// True if a newline character should be appended to JSON output.
14752 	char					hostname[ 16 + 1 ];		// Base hostname that the replier is to use for instance and host names.
14753 	char					tag[ 4 + 1 ];			// Tag that the replier is to use in its service types.
14754 
14755 }	MDNSDiscoveryTestContext;
14756 
14757 static void		_MDNSDiscoveryTestFirstQueryTimeout( void *inContext );
14758 static void DNSSD_API
14759 	_MDNSDiscoveryTestAboutQueryCallback(
14760 		DNSServiceRef			inSDRef,
14761 		DNSServiceFlags			inFlags,
14762 		uint32_t				inInterfaceIndex,
14763 		DNSServiceErrorType		inError,
14764 		const char *			inFullName,
14765 		uint16_t				inType,
14766 		uint16_t				inClass,
14767 		uint16_t				inRDataLen,
14768 		const void *			inRDataPtr,
14769 		uint32_t				inTTL,
14770 		void *					inContext );
14771 static void
14772 	_MDNSDiscoveryTestServiceBrowserCallback(
14773 		ServiceBrowserResults *	inResults,
14774 		OSStatus				inError,
14775 		void *					inContext );
14776 static Boolean	_MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName, const uint8_t *inTXTPtr, size_t inTXTLen );
14777 
MDNSDiscoveryTestCmd(void)14778 static void	MDNSDiscoveryTestCmd( void )
14779 {
14780 	OSStatus						err;
14781 	MDNSDiscoveryTestContext *		context;
14782 	char							queryName[ sizeof_field( MDNSDiscoveryTestContext, hostname ) + 15 ];
14783 
14784 	context = (MDNSDiscoveryTestContext *) calloc( 1, sizeof( *context ) );
14785 	require_action( context, exit, err = kNoMemoryErr );
14786 
14787 	err = CheckIntegerArgument( gMDNSDiscoveryTest_InstanceCount, "instance count", 1, UINT16_MAX );
14788 	require_noerr_quiet( err, exit );
14789 
14790 	err = CheckIntegerArgument( gMDNSDiscoveryTest_TXTSize, "TXT size", 1, UINT16_MAX );
14791 	require_noerr_quiet( err, exit );
14792 
14793 	err = CheckIntegerArgument( gMDNSDiscoveryTest_BrowseTimeSecs, "browse time (seconds)", 1, INT_MAX );
14794 	require_noerr_quiet( err, exit );
14795 
14796 	err = CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountA, "A record count", 0, 64 );
14797 	require_noerr_quiet( err, exit );
14798 
14799 	err = CheckIntegerArgument( gMDNSDiscoveryTest_RecordCountAAAA, "AAAA record count", 0, 64 );
14800 	require_noerr_quiet( err, exit );
14801 
14802 	err = CheckDoubleArgument( gMDNSDiscoveryTest_UnicastDropRate, "unicast drop rate", 0.0, 1.0 );
14803 	require_noerr_quiet( err, exit );
14804 
14805 	err = CheckDoubleArgument( gMDNSDiscoveryTest_MulticastDropRate, "multicast drop rate", 0.0, 1.0 );
14806 	require_noerr_quiet( err, exit );
14807 
14808 	err = CheckIntegerArgument( gMDNSDiscoveryTest_MaxDropCount, "drop count", 0, 255 );
14809 	require_noerr_quiet( err, exit );
14810 
14811 	context->replierPID				= -1;
14812 	context->instanceCount			= (unsigned int) gMDNSDiscoveryTest_InstanceCount;
14813 	context->txtSize				= (unsigned int) gMDNSDiscoveryTest_TXTSize;
14814 	context->browseTimeSecs			= (unsigned int) gMDNSDiscoveryTest_BrowseTimeSecs;
14815 	context->recordCountA			= (unsigned int) gMDNSDiscoveryTest_RecordCountA;
14816 	context->recordCountAAAA		= (unsigned int) gMDNSDiscoveryTest_RecordCountAAAA;
14817 	context->ucastDropRate			= gMDNSDiscoveryTest_UnicastDropRate;
14818 	context->mcastDropRate			= gMDNSDiscoveryTest_MulticastDropRate;
14819 	context->maxDropCount			= (unsigned int) gMDNSDiscoveryTest_MaxDropCount;
14820 	context->outputFilePath			= gMDNSDiscoveryTest_OutputFilePath;
14821 	context->outputAppendNewline	= gMDNSDiscoveryTest_OutputAppendNewline	? true : false;
14822 	context->noAdditionals			= gMDNSDiscoveryTest_NoAdditionals			? true : false;
14823 	context->useIPv4				= ( gMDNSDiscoveryTest_UseIPv4 || !gMDNSDiscoveryTest_UseIPv6 ) ? true : false;
14824 	context->useIPv6				= ( gMDNSDiscoveryTest_UseIPv6 || !gMDNSDiscoveryTest_UseIPv4 ) ? true : false;
14825 #if( MDNSRESPONDER_PROJECT )
14826 	context->useNewGAI				= gMDNSDiscoveryTest_UseNewGAI				? true : false;
14827 #endif
14828 
14829 	if( gMDNSDiscoveryTest_Interface )
14830 	{
14831 		err = InterfaceIndexFromArgString( gMDNSDiscoveryTest_Interface, &context->ifIndex );
14832 		require_noerr_quiet( err, exit );
14833 	}
14834 	else
14835 	{
14836 		err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
14837 		require_noerr_quiet( err, exit );
14838 	}
14839 
14840 	err = OutputFormatFromArgString( gMDNSDiscoveryTest_OutputFormat, &context->outputFormat );
14841 	require_noerr_quiet( err, exit );
14842 
14843 	if( gMDNSDiscoveryTest_FlushCache )
14844 	{
14845 		err = CheckRootUser();
14846 		require_noerr_quiet( err, exit );
14847 
14848 		err = systemf( NULL, "killall -HUP mDNSResponder" );
14849 		require_noerr( err, exit );
14850 		sleep( 1 );
14851 		context->flushedCache = true;
14852 	}
14853 
14854 	_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( context->hostname ) - 1,
14855 		context->hostname );
14856 	_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( context->tag ) - 1, context->tag );
14857 
14858 	ASPrintF( &context->serviceType, "_t-%s-%u-%u._tcp", context->tag, context->txtSize, context->instanceCount );
14859 	require_action( context->serviceType, exit, err = kUnknownErr );
14860 
14861 	ASPrintF( &context->replierCommand,
14862 		"dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount %u "
14863 		"--countA %u --countAAAA %u --udrop %.1f --mdrop %.1f --maxDropCount %u %?s%?s%?s",
14864 		(int64_t) getpid(),
14865 		context->ifIndex,
14866 		context->hostname,
14867 		context->tag,
14868 		context->instanceCount,
14869 		context->recordCountA,
14870 		context->recordCountAAAA,
14871 		context->ucastDropRate,
14872 		context->mcastDropRate,
14873 		context->maxDropCount,
14874 		context->noAdditionals,	" --noAdditionals",
14875 		context->useIPv4,		" --ipv4",
14876 		context->useIPv6,		" --ipv6" );
14877 	require_action_quiet( context->replierCommand, exit, err = kUnknownErr );
14878 
14879 	err = _SpawnCommand( &context->replierPID, NULL, NULL, "%s", context->replierCommand );
14880 	require_noerr_quiet( err, exit );
14881 
14882 	// Query for the replier's about TXT record. A response means that it's fully up and running.
14883 
14884 	SNPrintF( queryName, sizeof( queryName ), "about.%s.local.", context->hostname );
14885 	err = DNSServiceQueryRecord( &context->query, kDNSServiceFlagsForceMulticast, context->ifIndex, queryName,
14886 		kDNSServiceType_TXT, kDNSServiceClass_IN, _MDNSDiscoveryTestAboutQueryCallback, context );
14887 	require_noerr( err, exit );
14888 
14889 	err = DNSServiceSetDispatchQueue( context->query, dispatch_get_main_queue() );
14890 	require_noerr( err, exit );
14891 
14892 	err = DispatchTimerCreate( dispatch_time_seconds( kMDNSDiscoveryTestFirstQueryTimeoutSecs ),
14893 		DISPATCH_TIME_FOREVER, UINT64_C_safe( kMDNSDiscoveryTestFirstQueryTimeoutSecs ) * kNanosecondsPerSecond / 10, NULL,
14894 		_MDNSDiscoveryTestFirstQueryTimeout, NULL, context, &context->queryTimer );
14895 	require_noerr( err, exit );
14896 	dispatch_resume( context->queryTimer );
14897 
14898 	context->startTime = NanoTimeGetCurrent();
14899 	dispatch_main();
14900 
14901 exit:
14902 	exit( 1 );
14903 }
14904 
14905 //===========================================================================================================================
14906 //	_MDNSDiscoveryTestFirstQueryTimeout
14907 //===========================================================================================================================
14908 
_MDNSDiscoveryTestFirstQueryTimeout(void * inContext)14909 static void	_MDNSDiscoveryTestFirstQueryTimeout( void *inContext )
14910 {
14911 	MDNSDiscoveryTestContext * const		context = (MDNSDiscoveryTestContext *) inContext;
14912 
14913 	dispatch_source_forget( &context->queryTimer );
14914 
14915 	FPrintF( stderr, "error: Query for mdnsreplier's \"about\" TXT record timed out.\n" );
14916 	exit( 1 );
14917 }
14918 
14919 //===========================================================================================================================
14920 //	_MDNSDiscoveryTestAboutQueryCallback
14921 //===========================================================================================================================
14922 
14923 static void DNSSD_API
_MDNSDiscoveryTestAboutQueryCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inFullName,uint16_t inType,uint16_t inClass,uint16_t inRDataLen,const void * inRDataPtr,uint32_t inTTL,void * inContext)14924 	_MDNSDiscoveryTestAboutQueryCallback(
14925 		DNSServiceRef			inSDRef,
14926 		DNSServiceFlags			inFlags,
14927 		uint32_t				inInterfaceIndex,
14928 		DNSServiceErrorType		inError,
14929 		const char *			inFullName,
14930 		uint16_t				inType,
14931 		uint16_t				inClass,
14932 		uint16_t				inRDataLen,
14933 		const void *			inRDataPtr,
14934 		uint32_t				inTTL,
14935 		void *					inContext )
14936 {
14937 	OSStatus								err;
14938 	MDNSDiscoveryTestContext * const		context = (MDNSDiscoveryTestContext *) inContext;
14939 
14940 	Unused( inSDRef );
14941 	Unused( inInterfaceIndex );
14942 	Unused( inFullName );
14943 	Unused( inType );
14944 	Unused( inClass );
14945 	Unused( inRDataLen );
14946 	Unused( inRDataPtr );
14947 	Unused( inTTL );
14948 
14949 	err = inError;
14950 	require_noerr( err, exit );
14951 	require_quiet( inFlags & kDNSServiceFlagsAdd, exit );
14952 
14953 	DNSServiceForget( &context->query );
14954 	dispatch_source_forget( &context->queryTimer );
14955 
14956 	err = ServiceBrowserCreate( dispatch_get_main_queue(), 0, "local.", context->browseTimeSecs, false, &context->browser );
14957 	require_noerr( err, exit );
14958 
14959 	err = ServiceBrowserAddServiceType( context->browser, context->serviceType );
14960 	require_noerr( err, exit );
14961 
14962 #if( MDNSRESPONDER_PROJECT )
14963 	ServiceBrowserSetUseNewGAI( context->browser, context->useNewGAI );
14964 #endif
14965 	ServiceBrowserSetCallback( context->browser, _MDNSDiscoveryTestServiceBrowserCallback, context );
14966 	ServiceBrowserStart( context->browser );
14967 
14968 exit:
14969 	if( err ) exit( 1 );
14970 }
14971 
14972 //===========================================================================================================================
14973 //	_MDNSDiscoveryTestServiceBrowserCallback
14974 //===========================================================================================================================
14975 
14976 #define kMDNSDiscoveryTestResultsKey_ReplierInfo					CFSTR( "replierInfo" )
14977 #define kMDNSDiscoveryTestResultsKey_StartTime						CFSTR( "startTime" )
14978 #define kMDNSDiscoveryTestResultsKey_EndTime						CFSTR( "endTime" )
14979 #define kMDNSDiscoveryTestResultsKey_BrowseTimeSecs					CFSTR( "browseTimeSecs" )
14980 #define kMDNSDiscoveryTestResultsKey_ServiceType					CFSTR( "serviceType" )
14981 #define kMDNSDiscoveryTestResultsKey_FlushedCache					CFSTR( "flushedCache" )
14982 #define kMDNSDiscoveryTestResultsKey_UsedNewGAI						CFSTR( "usedNewGAI" )
14983 #define kMDNSDiscoveryTestResultsKey_UnexpectedInstances			CFSTR( "unexpectedInstances" )
14984 #define kMDNSDiscoveryTestResultsKey_MissingInstances				CFSTR( "missingInstances" )
14985 #define kMDNSDiscoveryTestResultsKey_IncorrectInstances				CFSTR( "incorrectInstances" )
14986 #define kMDNSDiscoveryTestResultsKey_Success						CFSTR( "success" )
14987 #define kMDNSDiscoveryTestResultsKey_TotalResolveTime				CFSTR( "totalResolveTimeUs" )
14988 
14989 #define kMDNSDiscoveryTestReplierInfoKey_Command					CFSTR( "command" )
14990 #define kMDNSDiscoveryTestReplierInfoKey_InstanceCount				CFSTR( "instanceCount" )
14991 #define kMDNSDiscoveryTestReplierInfoKey_TXTSize					CFSTR( "txtSize" )
14992 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountA				CFSTR( "recordCountA" )
14993 #define kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA			CFSTR( "recordCountAAAA" )
14994 #define kMDNSDiscoveryTestReplierInfoKey_Hostname					CFSTR( "hostname" )
14995 #define kMDNSDiscoveryTestReplierInfoKey_NoAdditionals				CFSTR( "noAdditionals" )
14996 #define kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate			CFSTR( "ucastDropRate" )
14997 #define kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate			CFSTR( "mcastDropRate" )
14998 #define kMDNSDiscoveryTestReplierInfoKey_MaxDropCount				CFSTR( "maxDropCount" )
14999 
15000 #define kMDNSDiscoveryTestUnexpectedInstanceKey_Name				CFSTR( "name" )
15001 #define kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex		CFSTR( "interfaceIndex" )
15002 
15003 #define kMDNSDiscoveryTestIncorrectInstanceKey_Name					CFSTR( "name" )
15004 #define kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve			CFSTR( "didResolve" )
15005 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname			CFSTR( "badHostname" )
15006 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadPort				CFSTR( "badPort" )
15007 #define kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT				CFSTR( "badTXT" )
15008 #define kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs		CFSTR( "unexpectedAddrs" )
15009 #define kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs			CFSTR( "missingAddrs" )
15010 
_MDNSDiscoveryTestServiceBrowserCallback(ServiceBrowserResults * inResults,OSStatus inError,void * inContext)15011 static void	_MDNSDiscoveryTestServiceBrowserCallback( ServiceBrowserResults *inResults, OSStatus inError, void *inContext )
15012 {
15013 	OSStatus								err;
15014 	MDNSDiscoveryTestContext * const		context			= (MDNSDiscoveryTestContext *) inContext;
15015 	const SBRDomain *						domain;
15016 	const SBRServiceType *					type;
15017 	const SBRServiceInstance *				instance;
15018 	const SBRServiceInstance **				instanceArray	= NULL;
15019 	const SBRIPAddress *					ipaddr;
15020 	size_t									hostnameLen;
15021 	const char *							ptr;
15022 	const char *							end;
15023 	unsigned int							i;
15024 	uint32_t								u32;
15025 	CFMutableArrayRef						unexpectedInstances;
15026 	CFMutableArrayRef						missingInstances;
15027 	CFMutableArrayRef						incorrectInstances;
15028 	CFMutableDictionaryRef					plist			= NULL;
15029 	CFMutableDictionaryRef					badDict			= NULL;
15030 	CFMutableArrayRef						unexpectedAddrs	= NULL;
15031 	CFMutableArrayRef						missingAddrs	= NULL;
15032 	uint64_t								maxResolveTimeUs;
15033 	int										success			= false;
15034 	char									startTime[ 32 ];
15035 	char									endTime[ 32 ];
15036 
15037 	context->endTime = NanoTimeGetCurrent();
15038 
15039 	err = inError;
15040 	require_noerr( err, exit );
15041 
15042 	_NanoTime64ToTimestamp( context->startTime, startTime, sizeof( startTime ) );
15043 	_NanoTime64ToTimestamp( context->endTime, endTime, sizeof( endTime ) );
15044 	err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
15045 		"{"
15046 			"%kO="
15047 			"{"
15048 				"%kO=%s"	// replierCommand
15049 				"%kO=%lli"	// txtSize
15050 				"%kO=%lli"	// instanceCount
15051 				"%kO=%lli"	// recordCountA
15052 				"%kO=%lli"	// recordCountAAAA
15053 				"%kO=%s"	// hostname
15054 				"%kO=%b"	// noAdditionals
15055 				"%kO=%f"	// ucastDropRate
15056 				"%kO=%f"	// mcastDropRate
15057 				"%kO=%i"	// maxDropCount
15058 			"}"
15059 			"%kO=%s"	// startTime
15060 			"%kO=%s"	// endTime
15061 			"%kO=%lli"	// browseTimeSecs
15062 			"%kO=%s"	// serviceType
15063 			"%kO=%b"	// flushedCache
15064 		#if( MDNSRESPONDER_PROJECT )
15065 			"%kO=%b"	// usedNewGAI
15066 		#endif
15067 			"%kO=[%@]"	// unexpectedInstances
15068 			"%kO=[%@]"	// missingInstances
15069 			"%kO=[%@]"	// incorrectInstances
15070 		"}",
15071 		kMDNSDiscoveryTestResultsKey_ReplierInfo,
15072 		kMDNSDiscoveryTestReplierInfoKey_Command,			context->replierCommand,
15073 		kMDNSDiscoveryTestReplierInfoKey_InstanceCount,		(int64_t) context->instanceCount,
15074 		kMDNSDiscoveryTestReplierInfoKey_TXTSize,			(int64_t) context->txtSize,
15075 		kMDNSDiscoveryTestReplierInfoKey_RecordCountA,		(int64_t) context->recordCountA,
15076 		kMDNSDiscoveryTestReplierInfoKey_RecordCountAAAA,	(int64_t) context->recordCountAAAA,
15077 		kMDNSDiscoveryTestReplierInfoKey_Hostname,			context->hostname,
15078 		kMDNSDiscoveryTestReplierInfoKey_NoAdditionals,		context->noAdditionals,
15079 		kMDNSDiscoveryTestReplierInfoKey_UnicastDropRate,	context->ucastDropRate,
15080 		kMDNSDiscoveryTestReplierInfoKey_MulticastDropRate,	context->mcastDropRate,
15081 		kMDNSDiscoveryTestReplierInfoKey_MaxDropCount,		context->maxDropCount,
15082 		kMDNSDiscoveryTestResultsKey_StartTime,				startTime,
15083 		kMDNSDiscoveryTestResultsKey_EndTime,				endTime,
15084 		kMDNSDiscoveryTestResultsKey_BrowseTimeSecs,		(int64_t) context->browseTimeSecs,
15085 		kMDNSDiscoveryTestResultsKey_ServiceType,			context->serviceType,
15086 		kMDNSDiscoveryTestResultsKey_FlushedCache,			context->flushedCache,
15087 	#if( MDNSRESPONDER_PROJECT )
15088 		kMDNSDiscoveryTestResultsKey_UsedNewGAI,			context->useNewGAI,
15089 	#endif
15090 		kMDNSDiscoveryTestResultsKey_UnexpectedInstances,	&unexpectedInstances,
15091 		kMDNSDiscoveryTestResultsKey_MissingInstances,		&missingInstances,
15092 		kMDNSDiscoveryTestResultsKey_IncorrectInstances,	&incorrectInstances );
15093 	require_noerr( err, exit );
15094 
15095 	for( domain = inResults->domainList; domain && ( strcasecmp( domain->name, "local." ) != 0 ); domain = domain->next ) {}
15096 	require_action( domain, exit, err = kInternalErr );
15097 
15098 	for( type = domain->typeList; type && ( strcasecmp( type->name, context->serviceType ) != 0 ); type = type->next ) {}
15099 	require_action( type, exit, err = kInternalErr );
15100 
15101 	instanceArray = (const SBRServiceInstance **) calloc( context->instanceCount, sizeof( *instanceArray ) );
15102 	require_action( instanceArray, exit, err = kNoMemoryErr );
15103 
15104 	hostnameLen = strlen( context->hostname );
15105 	for( instance = type->instanceList; instance; instance = instance->next )
15106 	{
15107 		unsigned int		instanceNumber = 0;
15108 
15109 		if( strcmp_prefix( instance->name, context->hostname ) == 0 )
15110 		{
15111 			ptr = &instance->name[ hostnameLen ];
15112 			if( ( ptr[ 0 ] == ' ' ) && ( ptr[ 1 ] == '(' ) )
15113 			{
15114 				ptr += 2;
15115 				for( end = ptr; isdigit_safe( *end ); ++end ) {}
15116 				if( DecimalTextToUInt32( ptr, end, &u32, &ptr ) == kNoErr )
15117 				{
15118 					if( ( u32 >= 2 ) && ( u32 <= context->instanceCount ) && ( ptr[ 0 ] == ')' ) && ( ptr[ 1 ] == '\0' ) )
15119 					{
15120 						instanceNumber = u32;
15121 					}
15122 				}
15123 			}
15124 			else if( *ptr == '\0' )
15125 			{
15126 				instanceNumber = 1;
15127 			}
15128 		}
15129 		if( ( instanceNumber != 0 ) && ( instance->ifIndex == context->ifIndex ) )
15130 		{
15131 			check( !instanceArray[ instanceNumber - 1 ] );
15132 			instanceArray[ instanceNumber - 1 ] = instance;
15133 		}
15134 		else
15135 		{
15136 			err = CFPropertyListAppendFormatted( kCFAllocatorDefault, unexpectedInstances,
15137 				"{"
15138 					"%kO=%s"
15139 					"%kO=%lli"
15140 				"}",
15141 				kMDNSDiscoveryTestUnexpectedInstanceKey_Name,			instance->name,
15142 				kMDNSDiscoveryTestUnexpectedInstanceKey_InterfaceIndex,	(int64_t) instance->ifIndex );
15143 			require_noerr( err, exit );
15144 		}
15145 	}
15146 
15147 	maxResolveTimeUs = 0;
15148 	for( i = 1; i <= context->instanceCount; ++i )
15149 	{
15150 		int		isHostnameValid;
15151 		int		isTXTValid;
15152 
15153 		instance = instanceArray[ i - 1 ];
15154 		if( !instance )
15155 		{
15156 			if( i == 1 )
15157 			{
15158 				err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingInstances, "%s", context->hostname );
15159 				require_noerr( err, exit );
15160 			}
15161 			else
15162 			{
15163 				char *		instanceName = NULL;
15164 
15165 				ASPrintF( &instanceName, "%s (%u)", context->hostname, i );
15166 				require_action( instanceName, exit, err = kUnknownErr );
15167 
15168 				err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingInstances, "%s", instanceName );
15169 				free( instanceName );
15170 				require_noerr( err, exit );
15171 			}
15172 			continue;
15173 		}
15174 
15175 		if( !instance->hostname )
15176 		{
15177 			err = CFPropertyListAppendFormatted( kCFAllocatorDefault, incorrectInstances,
15178 				"{"
15179 					"%kO=%s"
15180 					"%kO=%b"
15181 				"}",
15182 				kMDNSDiscoveryTestIncorrectInstanceKey_Name,		instance->name,
15183 				kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve,	false );
15184 			require_noerr( err, exit );
15185 			continue;
15186 		}
15187 
15188 		badDict = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
15189 		require_action( badDict, exit, err = kNoMemoryErr );
15190 
15191 		isHostnameValid = false;
15192 		if( strcmp_prefix( instance->hostname, context->hostname ) == 0 )
15193 		{
15194 			ptr = &instance->hostname[ hostnameLen ];
15195 			if( i == 1 )
15196 			{
15197 				if( strcmp( ptr, ".local." ) == 0 ) isHostnameValid = true;
15198 			}
15199 			else if( *ptr == '-' )
15200 			{
15201 				++ptr;
15202 				for( end = ptr; isdigit_safe( *end ); ++end ) {}
15203 				if( DecimalTextToUInt32( ptr, end, &u32, &ptr ) == kNoErr )
15204 				{
15205 					if( ( u32 == i ) && ( strcmp( ptr, ".local." ) == 0 ) ) isHostnameValid = true;
15206 				}
15207 			}
15208 		}
15209 		if( !isHostnameValid )
15210 		{
15211 			err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadHostname, instance->hostname,
15212 				kSizeCString );
15213 			require_noerr( err, exit );
15214 		}
15215 
15216 		if( instance->port != (uint16_t)( kMDNSReplierPortBase + context->txtSize ) )
15217 		{
15218 			err = CFDictionarySetInt64( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadPort, instance->port );
15219 			require_noerr( err, exit );
15220 		}
15221 
15222 		isTXTValid = false;
15223 		if( instance->txtLen == context->txtSize )
15224 		{
15225 			uint8_t		name[ kDomainNameLengthMax ];
15226 
15227 			err = DomainNameFromString( name, instance->name, NULL );
15228 			require_noerr( err, exit );
15229 
15230 			err = DomainNameAppendString( name, type->name, NULL );
15231 			require_noerr( err, exit );
15232 
15233 			err = DomainNameAppendString( name, "local", NULL );
15234 			require_noerr( err, exit );
15235 
15236 			if( _MDNSDiscoveryTestTXTRecordIsValid( name, instance->txtPtr, instance->txtLen ) ) isTXTValid = true;
15237 		}
15238 		if( !isTXTValid )
15239 		{
15240 			char *		hexStr = NULL;
15241 
15242 			ASPrintF( &hexStr, "%.4H", instance->txtPtr, (int) instance->txtLen, (int) instance->txtLen );
15243 			require_action( hexStr, exit, err = kUnknownErr );
15244 
15245 			err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_BadTXT, hexStr, kSizeCString );
15246 			free( hexStr );
15247 			require_noerr( err, exit );
15248 		}
15249 
15250 		if( isHostnameValid )
15251 		{
15252 			uint64_t			addrV4Bitmap, addrV6Bitmap, bitmask, resolveTimeUs;
15253 			unsigned int		j;
15254 			uint8_t				addrV4[ 4 ];
15255 			uint8_t				addrV6[ 16 ];
15256 			uint8_t				addrV6LL[ 16 ];
15257 
15258 			if( context->recordCountA < 64 )	addrV4Bitmap = ( UINT64_C( 1 ) << context->recordCountA ) - 1;
15259 			else								addrV4Bitmap =  ~UINT64_C( 0 );
15260 
15261 			if( context->recordCountAAAA < 64 ) addrV6Bitmap = ( UINT64_C( 1 ) << context->recordCountAAAA ) - 1;
15262 			else								addrV6Bitmap =  ~UINT64_C( 0 );
15263 
15264 			addrV4[ 0 ] = 0;
15265 			WriteBig16( &addrV4[ 1 ], i );
15266 			addrV4[ 3 ] = 0;
15267 
15268 			memcpy( addrV6, kMDNSReplierBaseAddrV6, 16 );
15269 			WriteBig16( &addrV6[ 12 ], i );
15270 
15271 			memcpy( addrV6LL, kMDNSReplierLinkLocalBaseAddrV6, 16 );
15272 			WriteBig16( &addrV6LL[ 12 ], i );
15273 
15274 			unexpectedAddrs = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15275 			require_action( unexpectedAddrs, exit, err = kNoMemoryErr );
15276 
15277 			resolveTimeUs = 0;
15278 			for( ipaddr = instance->ipaddrList; ipaddr; ipaddr = ipaddr->next )
15279 			{
15280 				const uint8_t *		addrPtr;
15281 				unsigned int		lsb;
15282 				int					isAddrValid = false;
15283 
15284 				if( ipaddr->sip.sa.sa_family == AF_INET )
15285 				{
15286 					addrPtr	= (const uint8_t *) &ipaddr->sip.v4.sin_addr.s_addr;
15287 					lsb		= addrPtr[ 3 ];
15288 					if( ( memcmp( addrPtr, addrV4, 3 ) == 0 ) && ( lsb >= 1 ) && ( lsb <= context->recordCountA ) )
15289 					{
15290 						bitmask = UINT64_C( 1 ) << ( lsb - 1 );
15291 						addrV4Bitmap &= ~bitmask;
15292 						isAddrValid = true;
15293 					}
15294 				}
15295 				else if( ipaddr->sip.sa.sa_family == AF_INET6 )
15296 				{
15297 					const struct sockaddr_in6 * const		sin6 = &ipaddr->sip.v6;
15298 
15299 					addrPtr	= sin6->sin6_addr.s6_addr;
15300 					lsb		= addrPtr[ 15 ];
15301 					if( ( lsb >= 1 ) && ( lsb <= context->recordCountAAAA ) )
15302 					{
15303 						const uint32_t		scopeID = ( lsb == 1 ) ? context->ifIndex : 0;
15304 
15305 						if( ( memcmp( addrPtr, ( lsb == 1 ) ? addrV6LL : addrV6, 15 ) == 0 ) &&
15306 							( sin6->sin6_scope_id == scopeID ) )
15307 						{
15308 							bitmask = UINT64_C( 1 ) << ( lsb - 1 );
15309 							addrV6Bitmap &= ~bitmask;
15310 							isAddrValid = true;
15311 						}
15312 					}
15313 				}
15314 				if( isAddrValid )
15315 				{
15316 					if( ipaddr->resolveTimeUs > resolveTimeUs ) resolveTimeUs = ipaddr->resolveTimeUs;
15317 				}
15318 				else
15319 				{
15320 					err = CFPropertyListAppendFormatted( kCFAllocatorDefault, unexpectedAddrs, "%##a", &ipaddr->sip );
15321 					require_noerr( err, exit );
15322 				}
15323 			}
15324 
15325 			resolveTimeUs += ( instance->discoverTimeUs + instance->resolveTimeUs );
15326 			if( resolveTimeUs > maxResolveTimeUs ) maxResolveTimeUs = resolveTimeUs;
15327 
15328 			if( CFArrayGetCount( unexpectedAddrs ) > 0 )
15329 			{
15330 				CFDictionarySetValue( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_UnexpectedAddrs, unexpectedAddrs );
15331 			}
15332 			ForgetCF( &unexpectedAddrs );
15333 
15334 			missingAddrs = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15335 			require_action( missingAddrs, exit, err = kNoMemoryErr );
15336 
15337 			for( j = 1; addrV4Bitmap != 0; ++j )
15338 			{
15339 				bitmask = UINT64_C( 1 ) << ( j - 1 );
15340 				if( addrV4Bitmap & bitmask )
15341 				{
15342 					addrV4Bitmap &= ~bitmask;
15343 					addrV4[ 3 ] = (uint8_t) j;
15344 					err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingAddrs, "%.4a", addrV4 );
15345 					require_noerr( err, exit );
15346 				}
15347 			}
15348 			for( j = 1; addrV6Bitmap != 0; ++j )
15349 			{
15350 				bitmask = UINT64_C( 1 ) << ( j - 1 );
15351 				if( addrV6Bitmap & bitmask )
15352 				{
15353 					struct sockaddr_in6		sin6;
15354 					uint8_t					missingIPv6[ 16 ];
15355 
15356 					addrV6Bitmap &= ~bitmask;
15357 					memcpy( missingIPv6, ( j == 1 ) ? addrV6LL : addrV6, 16 );
15358 					missingIPv6[ 15 ] = (uint8_t) j;
15359 					_SockAddrInitIPv6( &sin6, missingIPv6, ( j == 1 ) ? context->ifIndex : 0, 0 );
15360 					err = CFPropertyListAppendFormatted( kCFAllocatorDefault, missingAddrs, "%##a", &sin6 );
15361 					require_noerr( err, exit );
15362 				}
15363 			}
15364 
15365 			if( CFArrayGetCount( missingAddrs ) > 0 )
15366 			{
15367 				CFDictionarySetValue( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_MissingAddrs, missingAddrs );
15368 			}
15369 			ForgetCF( &missingAddrs );
15370 		}
15371 
15372 		if( CFDictionaryGetCount( badDict ) > 0 )
15373 		{
15374 			err = CFDictionarySetCString( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_Name, instance->name,
15375 				kSizeCString );
15376 			require_noerr( err, exit );
15377 
15378 			CFDictionarySetBoolean( badDict, kMDNSDiscoveryTestIncorrectInstanceKey_DidResolve, true );
15379 			CFArrayAppendValue( incorrectInstances, badDict );
15380 		}
15381 		ForgetCF( &badDict );
15382 	}
15383 
15384 	if( ( CFArrayGetCount( unexpectedInstances ) == 0 ) &&
15385 		( CFArrayGetCount( missingInstances )    == 0 ) &&
15386 		( CFArrayGetCount( incorrectInstances )  == 0 ) )
15387 	{
15388 		err = CFDictionarySetInt64( plist, kMDNSDiscoveryTestResultsKey_TotalResolveTime, (int64_t) maxResolveTimeUs );
15389 		require_noerr( err, exit );
15390 		success = true;
15391 	}
15392 	else
15393 	{
15394 		success = false;
15395 	}
15396 	CFDictionarySetBoolean( plist, kMDNSDiscoveryTestResultsKey_Success, success );
15397 
15398 	err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
15399 	require_noerr_quiet( err, exit );
15400 
15401 exit:
15402 	ForgetCF( &context->browser );
15403 	if( context->replierPID != -1 )
15404 	{
15405 		kill( context->replierPID, SIGTERM );
15406 		context->replierPID = -1;
15407 	}
15408 	FreeNullSafe( instanceArray );
15409 	CFReleaseNullSafe( plist );
15410 	CFReleaseNullSafe( badDict );
15411 	CFReleaseNullSafe( unexpectedAddrs );
15412 	CFReleaseNullSafe( missingAddrs );
15413 	exit( err ? 1 : ( success ? 0 : 2 ) );
15414 }
15415 
15416 //===========================================================================================================================
15417 //	_MDNSDiscoveryTestTXTRecordIsValid
15418 //===========================================================================================================================
15419 
_MDNSDiscoveryTestTXTRecordIsValid(const uint8_t * inRecordName,const uint8_t * inTXTPtr,size_t inTXTLen)15420 static Boolean	_MDNSDiscoveryTestTXTRecordIsValid( const uint8_t *inRecordName, const uint8_t *inTXTPtr, size_t inTXTLen )
15421 {
15422 	uint32_t			hash;
15423 	int					n;
15424 	const uint8_t *		ptr;
15425 	size_t				i, wholeCount, remCount;
15426 	uint8_t				txtStr[ 16 ];
15427 
15428 	if( inTXTLen == 0 ) return( false );
15429 
15430 	hash = _FNV1( inRecordName, DomainNameLength( inRecordName ) );
15431 
15432 	txtStr[ 0 ] = 15;
15433 	n = MemPrintF( &txtStr[ 1 ], 15, "hash=0x%08X", hash );
15434 	check( n == 15 );
15435 
15436 	ptr = inTXTPtr;
15437 	wholeCount = inTXTLen / 16;
15438 	for( i = 0; i < wholeCount; ++i )
15439 	{
15440 		if( memcmp( ptr, txtStr, 16 ) != 0 ) return( false );
15441 		ptr += 16;
15442 	}
15443 
15444 	remCount = inTXTLen % 16;
15445 	if( remCount > 0 )
15446 	{
15447 		txtStr[ 0 ] = (uint8_t)( remCount - 1 );
15448 		if( memcmp( ptr, txtStr, remCount ) != 0 ) return( false );
15449 		ptr += remCount;
15450 	}
15451 	check( ptr == &inTXTPtr[ inTXTLen ] );
15452 	return( true );
15453 }
15454 
15455 //===========================================================================================================================
15456 //	DotLocalTestCmd
15457 //===========================================================================================================================
15458 
15459 #define kDotLocalTestPreparationTimeLimitSecs		5
15460 #define kDotLocalTestSubtestDurationSecs			5
15461 
15462 // Constants for SRV record query subtest.
15463 
15464 #define kDotLocalTestSRV_Priority		1
15465 #define kDotLocalTestSRV_Weight			0
15466 #define kDotLocalTestSRV_Port			80
15467 #define kDotLocalTestSRV_TargetName		( (const uint8_t *) "\x03" "www" "\x07" "example" "\x03" "com" )
15468 #define kDotLocalTestSRV_TargetStr		"www.example.com."
15469 #define kDotLocalTestSRV_ResultStr		"1 0 80 " kDotLocalTestSRV_TargetStr
15470 
15471 typedef enum
15472 {
15473 	kDotLocalTestState_Unset				= 0,
15474 	kDotLocalTestState_Preparing			= 1,
15475 	kDotLocalTestState_GAIMDNSOnly			= 2,
15476 	kDotLocalTestState_GAIDNSOnly			= 3,
15477 	kDotLocalTestState_GAIBoth				= 4,
15478 	kDotLocalTestState_GAINeither			= 5,
15479 	kDotLocalTestState_GAINoSuchRecord		= 6,
15480 	kDotLocalTestState_QuerySRV				= 7,
15481 	kDotLocalTestState_Done					= 8
15482 
15483 }	DotLocalTestState;
15484 
15485 typedef struct
15486 {
15487 	const char *			testDesc;			// Description of the current subtest.
15488 	char *					queryName;			// Query name for GetAddrInfo or QueryRecord operation.
15489 	dispatch_source_t		timer;				// Timer used for limiting the time for each subtest.
15490 	NanoTime64				startTime;			// Timestamp of when the subtest started.
15491 	NanoTime64				endTime;			// Timestamp of when the subtest ended.
15492 	CFMutableArrayRef		correctResults;		// Operation results that were expected.
15493 	CFMutableArrayRef		duplicateResults;	// Operation results that were expected, but were already received.
15494 	CFMutableArrayRef		unexpectedResults;	// Operation results that were unexpected.
15495 	OSStatus				error;				// Subtest's error code.
15496 	uint32_t				addrDNSv4;			// If hasDNSv4 is true, the expected DNS IPv4 address for queryName.
15497 	uint32_t				addrMDNSv4;			// If hasMDNSv4 is true, the expected MDNS IPv4 address for queryName.
15498 	uint8_t					addrDNSv6[ 16 ];	// If hasDNSv6 is true, the expected DNS IPv6 address for queryName.
15499 	uint8_t					addrMDNSv6[ 16 ];	// If hasMDNSv6 is true, the expected MDNS IPv6 address for queryName.
15500 	Boolean					hasDNSv4;			// True if queryName has a DNS IPv4 address.
15501 	Boolean					hasDNSv6;			// True if queryName has a DNS IPv6 address.
15502 	Boolean					hasMDNSv4;			// True if queryName has an MDNS IPv4 address.
15503 	Boolean					hasMDNSv6;			// True if queryName has an MDNS IPv6 address.
15504 	Boolean					needDNSv4;			// True if operation is expecting, but hasn't received a DNS IPv4 result.
15505 	Boolean					needDNSv6;			// True if operation is expecting, but hasn't received a DNS IPv6 result.
15506 	Boolean					needMDNSv4;			// True if operation is expecting, but hasn't received an MDNS IPv4 result.
15507 	Boolean					needMDNSv6;			// True if operation is expecting, but hasn't received an MDNS IPv6 result.
15508 	Boolean					needSRV;			// True if operation is expecting, but hasn't received an SRV result.
15509 
15510 }	DotLocalSubtest;
15511 
15512 typedef struct
15513 {
15514 	dispatch_source_t		timer;				// Timer used for limiting the time for each state/subtest.
15515 	DotLocalSubtest *		subtest;			// Current subtest's state.
15516 	DNSServiceRef			connection;			// Shared connection for DNS-SD operations.
15517 	DNSServiceRef			op;					// Reference for the current DNS-SD operation.
15518 	DNSServiceRef			op2;				// Reference for mdnsreplier probe query used during preparing state.
15519 	DNSRecordRef			localSOARef;		// Reference returned by DNSServiceRegisterRecord() for local. SOA record.
15520 	char *					replierCmd;			// Command used to invoke the mdnsreplier.
15521 	char *					serverCmd;			// Command used to invoke the test DNS server.
15522 	CFMutableArrayRef		reportsGAI;			// Reports for subtests that use DNSServiceGetAddrInfo.
15523 	CFMutableArrayRef		reportsQuerySRV;	// Reports for subtests that use DNSServiceQueryRecord for SRV records.
15524 	NanoTime64				startTime;			// Timestamp for when the test started.
15525 	NanoTime64				endTime;			// Timestamp for when the test ended.
15526 	DotLocalTestState		state;				// The test's current state.
15527 	pid_t					replierPID;			// PID of spawned mdnsreplier.
15528 	pid_t					serverPID;			// PID of spawned test DNS server.
15529 	uint32_t				ifIndex;			// Interface index used for mdnsreplier.
15530 	char *					outputFilePath;		// File to write test results to. If NULL, then write to stdout.
15531 	OutputFormatType		outputFormat;		// Format of test results output.
15532 	Boolean					registeredSOA;		// True if the dummy local. SOA record was successfully registered.
15533 	Boolean					serverIsReady;		// True if response was received for test DNS server probe query.
15534 	Boolean					replierIsReady;		// True if response was received for mdnsreplier probe query.
15535 	Boolean					testFailed;			// True if at least one subtest failed.
15536 	char					labelStr[ 20 + 1 ];	// Unique label string used for for making the query names used by subtests.
15537 												// The format of this string is "dotlocal-test-<six random chars>".
15538 }	DotLocalTestContext;
15539 
15540 static void	_DotLocalTestStateMachine( DotLocalTestContext *inContext );
15541 static void DNSSD_API
15542 	_DotLocalTestProbeQueryRecordCallback(
15543 		DNSServiceRef			inSDRef,
15544 		DNSServiceFlags			inFlags,
15545 		uint32_t				inInterfaceIndex,
15546 		DNSServiceErrorType		inError,
15547 		const char *			inFullName,
15548 		uint16_t				inType,
15549 		uint16_t				inClass,
15550 		uint16_t				inRDataLen,
15551 		const void *			inRDataPtr,
15552 		uint32_t				inTTL,
15553 		void *					inContext );
15554 static void DNSSD_API
15555 	_DotLocalTestRegisterRecordCallback(
15556 		DNSServiceRef		inSDRef,
15557 		DNSRecordRef		inRecordRef,
15558 		DNSServiceFlags		inFlags,
15559 		DNSServiceErrorType	inError,
15560 		void *				inContext );
15561 static void	_DotLocalTestTimerHandler( void *inContext );
15562 static void DNSSD_API
15563 	_DotLocalTestGAICallback(
15564 		DNSServiceRef			inSDRef,
15565 		DNSServiceFlags			inFlags,
15566 		uint32_t				inInterfaceIndex,
15567 		DNSServiceErrorType		inError,
15568 		const char *			inHostname,
15569 		const struct sockaddr *	inSockAddr,
15570 		uint32_t				inTTL,
15571 		void *					inContext );
15572 static void DNSSD_API
15573 	_DotLocalTestQueryRecordCallback(
15574 		DNSServiceRef			inSDRef,
15575 		DNSServiceFlags			inFlags,
15576 		uint32_t				inInterfaceIndex,
15577 		DNSServiceErrorType		inError,
15578 		const char *			inFullName,
15579 		uint16_t				inType,
15580 		uint16_t				inClass,
15581 		uint16_t				inRDataLen,
15582 		const void *			inRDataPtr,
15583 		uint32_t				inTTL,
15584 		void *					inContext );
15585 
DotLocalTestCmd(void)15586 static void	DotLocalTestCmd( void )
15587 {
15588 	OSStatus					err;
15589 	DotLocalTestContext *		context;
15590 	uint8_t *					rdataPtr;
15591 	size_t						rdataLen;
15592 	DNSServiceFlags				flags;
15593 	char						queryName[ 64 ];
15594 	char						randBuf[ 6 + 1 ];	// Large enough for four and six character random strings below.
15595 
15596 	context = (DotLocalTestContext *) calloc( 1, sizeof( *context ) );
15597 	require_action( context, exit, err = kNoMemoryErr );
15598 
15599 	context->startTime	= NanoTimeGetCurrent();
15600 	context->endTime	= kNanoTime_Invalid;
15601 
15602 	context->state = kDotLocalTestState_Preparing;
15603 
15604 	if( gDotLocalTest_Interface )
15605 	{
15606 		err = InterfaceIndexFromArgString( gDotLocalTest_Interface, &context->ifIndex );
15607 		require_noerr_quiet( err, exit );
15608 	}
15609 	else
15610 	{
15611 		err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
15612 		require_noerr_quiet( err, exit );
15613 	}
15614 
15615 	if( gDotLocalTest_OutputFilePath )
15616 	{
15617 		context->outputFilePath = strdup( gDotLocalTest_OutputFilePath );
15618 		require_action( context->outputFilePath, exit, err = kNoMemoryErr );
15619 	}
15620 
15621 	err = OutputFormatFromArgString( gDotLocalTest_OutputFormat, &context->outputFormat );
15622 	require_noerr_quiet( err, exit );
15623 
15624 	context->reportsGAI = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15625 	require_action( context->reportsGAI, exit, err = kNoMemoryErr );
15626 
15627 	context->reportsQuerySRV = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15628 	require_action( context->reportsQuerySRV, exit, err = kNoMemoryErr );
15629 
15630 	SNPrintF( context->labelStr, sizeof( context->labelStr ), "dotlocal-test-%s",
15631 		_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, 6, randBuf ) );
15632 
15633 	// Spawn an mdnsreplier.
15634 
15635 	ASPrintF( &context->replierCmd,
15636 		"dnssdutil mdnsreplier --follow %lld --interface %u --hostname %s --tag %s --maxInstanceCount 2 --countA 1"
15637 		" --countAAAA 1",
15638 		(int64_t) getpid(), context->ifIndex, context->labelStr,
15639 		_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, 4, randBuf ) );
15640 	require_action_quiet( context->replierCmd, exit, err = kUnknownErr );
15641 
15642 	err = _SpawnCommand( &context->replierPID, NULL, NULL, "%s", context->replierCmd );
15643 	require_noerr( err, exit );
15644 
15645 	// Spawn a test DNS server
15646 
15647 	ASPrintF( &context->serverCmd,
15648 		"dnssdutil server --loopback --follow %lld --port 0 --defaultTTL 300 --domain %s.local.",
15649 		(int64_t) getpid(), context->labelStr );
15650 	require_action_quiet( context->serverCmd, exit, err = kUnknownErr );
15651 
15652 	err = _SpawnCommand( &context->serverPID, NULL, NULL, "%s", context->serverCmd );
15653 	require_noerr( err, exit );
15654 
15655 	// Create a shared DNS-SD connection.
15656 
15657 	err = DNSServiceCreateConnection( &context->connection );
15658 	require_noerr( err, exit );
15659 
15660 	err = DNSServiceSetDispatchQueue( context->connection, dispatch_get_main_queue() );
15661 	require_noerr( err, exit );
15662 
15663 	// Create probe query for DNS server, i.e., query for any name that has an A record.
15664 
15665 	SNPrintF( queryName, sizeof( queryName ), "tag-dotlocal-test-probe.ipv4.%s.local.", context->labelStr );
15666 
15667 	flags = kDNSServiceFlagsShareConnection;
15668 #if( TARGET_OS_WATCH )
15669 	flags |= kDNSServiceFlagsPathEvaluationDone;
15670 #endif
15671 
15672 	context->op = context->connection;
15673 	err = DNSServiceQueryRecord( &context->op, flags, kDNSServiceInterfaceIndexAny, queryName, kDNSServiceType_A,
15674 		kDNSServiceClass_IN, _DotLocalTestProbeQueryRecordCallback, context );
15675 	require_noerr( err, exit );
15676 
15677 	// Create probe query for mdnsreplier's "about" TXT record.
15678 
15679 	SNPrintF( queryName, sizeof( queryName ), "about.%s.local.", context->labelStr );
15680 
15681 	flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsForceMulticast;
15682 #if( TARGET_OS_WATCH )
15683 	flags |= kDNSServiceFlagsPathEvaluationDone;
15684 #endif
15685 
15686 	context->op2 = context->connection;
15687 	err = DNSServiceQueryRecord( &context->op2, flags, context->ifIndex, queryName, kDNSServiceType_TXT, kDNSServiceClass_IN,
15688 		_DotLocalTestProbeQueryRecordCallback, context );
15689 	require_noerr( err, exit );
15690 
15691 	// Register a dummy local. SOA record.
15692 
15693 	err = CreateSOARecordData( kRootLabel, kRootLabel, 1976040101, 1 * kSecondsPerDay, 2 * kSecondsPerHour,
15694 		1000 * kSecondsPerHour, 2 * kSecondsPerDay, &rdataPtr, &rdataLen );
15695 	require_noerr( err, exit );
15696 
15697 	err = DNSServiceRegisterRecord( context->connection, &context->localSOARef, kDNSServiceFlagsUnique,
15698 		kDNSServiceInterfaceIndexLocalOnly, "local.", kDNSServiceType_SOA, kDNSServiceClass_IN,
15699 		(uint16_t) rdataLen, rdataPtr, 1 * kSecondsPerHour, _DotLocalTestRegisterRecordCallback, context );
15700 	require_noerr( err, exit );
15701 
15702 	// Start timer for probe responses and SOA record registration.
15703 
15704 	err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestPreparationTimeLimitSecs ),
15705 		INT64_C_safe( kDotLocalTestPreparationTimeLimitSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
15706 		_DotLocalTestTimerHandler, context, &context->timer );
15707 	require_noerr( err, exit );
15708 	dispatch_resume( context->timer );
15709 
15710 	dispatch_main();
15711 
15712 exit:
15713 	if( err ) ErrQuit( 1, "error: %#m\n", err );
15714 }
15715 
15716 //===========================================================================================================================
15717 //	_DotLocalTestStateMachine
15718 //===========================================================================================================================
15719 
15720 static OSStatus	_DotLocalSubtestCreate( DotLocalSubtest **outSubtest );
15721 static void		_DotLocalSubtestFree( DotLocalSubtest *inSubtest );
15722 static OSStatus	_DotLocalTestStartSubtest( DotLocalTestContext *inContext );
15723 static OSStatus	_DotLocalTestFinalizeSubtest( DotLocalTestContext *inContext );
15724 static void		_DotLocalTestFinalizeAndExit( DotLocalTestContext *inContext ) ATTRIBUTE_NORETURN;
15725 
_DotLocalTestStateMachine(DotLocalTestContext * inContext)15726 static void	_DotLocalTestStateMachine( DotLocalTestContext *inContext )
15727 {
15728 	OSStatus				err;
15729 	DotLocalTestState		nextState;
15730 
15731 	DNSServiceForget( &inContext->op );
15732 	DNSServiceForget( &inContext->op2 );
15733 	dispatch_source_forget( &inContext->timer );
15734 
15735 	switch( inContext->state )
15736 	{
15737 		case kDotLocalTestState_Preparing:			nextState = kDotLocalTestState_GAIMDNSOnly;		break;
15738 		case kDotLocalTestState_GAIMDNSOnly:		nextState = kDotLocalTestState_GAIDNSOnly;		break;
15739 		case kDotLocalTestState_GAIDNSOnly:			nextState = kDotLocalTestState_GAIBoth;			break;
15740 		case kDotLocalTestState_GAIBoth:			nextState = kDotLocalTestState_GAINeither;		break;
15741 		case kDotLocalTestState_GAINeither:			nextState = kDotLocalTestState_GAINoSuchRecord;	break;
15742 		case kDotLocalTestState_GAINoSuchRecord:	nextState = kDotLocalTestState_QuerySRV;		break;
15743 		case kDotLocalTestState_QuerySRV:			nextState = kDotLocalTestState_Done;			break;
15744 		default:									err = kStateErr;								goto exit;
15745 	}
15746 
15747 	if( inContext->state == kDotLocalTestState_Preparing )
15748 	{
15749 		if( !inContext->registeredSOA || !inContext->serverIsReady || !inContext->replierIsReady )
15750 		{
15751 			FPrintF( stderr, "Preparation timed out: Registered SOA? %s. Server ready? %s. mdnsreplier ready? %s.\n",
15752 				YesNoStr( inContext->registeredSOA ),
15753 				YesNoStr( inContext->serverIsReady ),
15754 				YesNoStr( inContext->replierIsReady ) );
15755 			err = kNotPreparedErr;
15756 			goto exit;
15757 		}
15758 	}
15759 	else
15760 	{
15761 		err = _DotLocalTestFinalizeSubtest( inContext );
15762 		require_noerr( err, exit );
15763 	}
15764 
15765 	inContext->state = nextState;
15766 	if( inContext->state == kDotLocalTestState_Done ) _DotLocalTestFinalizeAndExit( inContext );
15767 	err = _DotLocalTestStartSubtest( inContext );
15768 
15769 exit:
15770 	if( err ) ErrQuit( 1, "error: %#m\n", err );
15771 }
15772 
15773 //===========================================================================================================================
15774 //	_DotLocalSubtestCreate
15775 //===========================================================================================================================
15776 
_DotLocalSubtestCreate(DotLocalSubtest ** outSubtest)15777 static OSStatus	_DotLocalSubtestCreate( DotLocalSubtest **outSubtest )
15778 {
15779 	OSStatus				err;
15780 	DotLocalSubtest *		obj;
15781 
15782 	obj = (DotLocalSubtest *) calloc( 1, sizeof( *obj ) );
15783 	require_action( obj, exit, err = kNoMemoryErr );
15784 
15785 	obj->correctResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15786 	require_action( obj->correctResults, exit, err = kNoMemoryErr );
15787 
15788 	obj->duplicateResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15789 	require_action( obj->duplicateResults, exit, err = kNoMemoryErr );
15790 
15791 	obj->unexpectedResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
15792 	require_action( obj->unexpectedResults, exit, err = kNoMemoryErr );
15793 
15794 	*outSubtest = obj;
15795 	obj = NULL;
15796 	err = kNoErr;
15797 
15798 exit:
15799 	if( obj ) _DotLocalSubtestFree( obj );
15800 	return( err );
15801 }
15802 
15803 //===========================================================================================================================
15804 //	_DotLocalSubtestFree
15805 //===========================================================================================================================
15806 
_DotLocalSubtestFree(DotLocalSubtest * inSubtest)15807 static void	_DotLocalSubtestFree( DotLocalSubtest *inSubtest )
15808 {
15809 	ForgetMem( &inSubtest->queryName );
15810 	ForgetCF( &inSubtest->correctResults );
15811 	ForgetCF( &inSubtest->duplicateResults );
15812 	ForgetCF( &inSubtest->unexpectedResults );
15813 	free( inSubtest );
15814 }
15815 
15816 //===========================================================================================================================
15817 //	_DotLocalTestStartSubtest
15818 //===========================================================================================================================
15819 
_DotLocalTestStartSubtest(DotLocalTestContext * inContext)15820 static OSStatus	_DotLocalTestStartSubtest( DotLocalTestContext *inContext )
15821 {
15822 	OSStatus				err;
15823 	DotLocalSubtest *		subtest	= NULL;
15824 	DNSServiceRef			op		= NULL;
15825 	DNSServiceFlags			flags;
15826 
15827 	err = _DotLocalSubtestCreate( &subtest );
15828 	require_noerr( err, exit );
15829 
15830 	if( inContext->state == kDotLocalTestState_GAIMDNSOnly )
15831 	{
15832 		ASPrintF( &subtest->queryName, "%s-2.local.", inContext->labelStr );
15833 		require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
15834 
15835 		subtest->hasMDNSv4 = subtest->needMDNSv4 = true;
15836 		subtest->hasMDNSv6 = subtest->needMDNSv6 = true;
15837 
15838 		subtest->addrMDNSv4 = htonl( 0x00000201 );							// 0.0.2.1
15839 		memcpy( subtest->addrMDNSv6, kMDNSReplierLinkLocalBaseAddrV6, 16 );	// fe80::2:1
15840 		subtest->addrMDNSv6[ 13 ] = 2;
15841 		subtest->addrMDNSv6[ 15 ] = 1;
15842 
15843 		subtest->testDesc = kDotLocalTestSubtestDesc_GAIMDNSOnly;
15844 	}
15845 
15846 	else if( inContext->state == kDotLocalTestState_GAIDNSOnly )
15847 	{
15848 		ASPrintF( &subtest->queryName, "tag-dns-only.%s.local.", inContext->labelStr );
15849 		require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
15850 
15851 		subtest->hasDNSv4 = subtest->needDNSv4 = true;
15852 		subtest->hasDNSv6 = subtest->needDNSv6 = true;
15853 
15854 		subtest->addrDNSv4 = htonl( kDNSServerBaseAddrV4 + 1 );				// 203.0.113.1
15855 		memcpy( subtest->addrDNSv6, kDNSServerBaseAddrV6, 16 );				// 2001:db8:1::1
15856 		subtest->addrDNSv6[ 15 ] = 1;
15857 
15858 		subtest->testDesc = kDotLocalTestSubtestDesc_GAIDNSOnly;
15859 	}
15860 
15861 	else if( inContext->state == kDotLocalTestState_GAIBoth )
15862 	{
15863 		ASPrintF( &subtest->queryName, "%s.local.", inContext->labelStr );
15864 		require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
15865 
15866 		subtest->hasDNSv4	= subtest->needDNSv4	= true;
15867 		subtest->hasDNSv6	= subtest->needDNSv6	= true;
15868 		subtest->hasMDNSv4	= subtest->needMDNSv4	= true;
15869 		subtest->hasMDNSv6	= subtest->needMDNSv6	= true;
15870 
15871 		subtest->addrDNSv4 = htonl( kDNSServerBaseAddrV4 + 1 );				// 203.0.113.1
15872 		memcpy( subtest->addrDNSv6, kDNSServerBaseAddrV6, 16 );				// 2001:db8:1::1
15873 		subtest->addrDNSv6[ 15 ] = 1;
15874 
15875 		subtest->addrMDNSv4 = htonl( 0x00000101 );							// 0.0.1.1
15876 		memcpy( subtest->addrMDNSv6, kMDNSReplierLinkLocalBaseAddrV6, 16 );	// fe80::1:1
15877 		subtest->addrMDNSv6[ 13 ] = 1;
15878 		subtest->addrMDNSv6[ 15 ] = 1;
15879 
15880 		subtest->testDesc = kDotLocalTestSubtestDesc_GAIBoth;
15881 	}
15882 
15883 	else if( inContext->state == kDotLocalTestState_GAINeither )
15884 	{
15885 		ASPrintF( &subtest->queryName, "doesnotexit-%s.local.", inContext->labelStr );
15886 		require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
15887 
15888 		subtest->testDesc = kDotLocalTestSubtestDesc_GAINeither;
15889 	}
15890 
15891 	else if( inContext->state == kDotLocalTestState_GAINoSuchRecord )
15892 	{
15893 		ASPrintF( &subtest->queryName, "doesnotexit-dns.%s.local.", inContext->labelStr );
15894 		require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
15895 
15896 		subtest->hasDNSv4 = subtest->needDNSv4 = true;
15897 		subtest->hasDNSv6 = subtest->needDNSv6 = true;
15898 		subtest->testDesc = kDotLocalTestSubtestDesc_GAINoSuchRecord;
15899 	}
15900 
15901 	else if( inContext->state == kDotLocalTestState_QuerySRV )
15902 	{
15903 		ASPrintF( &subtest->queryName, "_http._tcp.srv-%u-%u-%u.%s%s.local.",
15904 			kDotLocalTestSRV_Priority, kDotLocalTestSRV_Weight, kDotLocalTestSRV_Port, kDotLocalTestSRV_TargetStr,
15905 			inContext->labelStr );
15906 		require_action_quiet( subtest->queryName, exit, err = kNoMemoryErr );
15907 
15908 		subtest->needSRV	= true;
15909 		subtest->testDesc	= kDotLocalTestSubtestDesc_QuerySRV;
15910 	}
15911 
15912 	else
15913 	{
15914 		err = kStateErr;
15915 		goto exit;
15916 	}
15917 
15918 	// Start new operation.
15919 
15920 	flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates;
15921 #if( TARGET_OS_WATCH )
15922 	flags |= kDNSServiceFlagsPathEvaluationDone;
15923 #endif
15924 
15925 	subtest->startTime	= NanoTimeGetCurrent();
15926 	subtest->endTime	= kNanoTime_Invalid;
15927 
15928 	if( inContext->state == kDotLocalTestState_QuerySRV )
15929 	{
15930 		op = inContext->connection;
15931 		err = DNSServiceQueryRecord( &op, flags, kDNSServiceInterfaceIndexAny, subtest->queryName,
15932 			kDNSServiceType_SRV, kDNSServiceClass_IN, _DotLocalTestQueryRecordCallback, inContext );
15933 		require_noerr( err, exit );
15934 	}
15935 	else
15936 	{
15937 		op = inContext->connection;
15938 		err = DNSServiceGetAddrInfo( &op, flags, kDNSServiceInterfaceIndexAny,
15939 			kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, subtest->queryName, _DotLocalTestGAICallback, inContext );
15940 		require_noerr( err, exit );
15941 	}
15942 
15943 	// Start timer.
15944 
15945 	check( !inContext->timer );
15946 	err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDotLocalTestSubtestDurationSecs ),
15947 		INT64_C_safe( kDotLocalTestSubtestDurationSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
15948 		_DotLocalTestTimerHandler, inContext, &inContext->timer );
15949 	require_noerr( err, exit );
15950 	dispatch_resume( inContext->timer );
15951 
15952 	check( !inContext->op );
15953 	inContext->op = op;
15954 	op = NULL;
15955 
15956 	check( !inContext->subtest );
15957 	inContext->subtest = subtest;
15958 	subtest = NULL;
15959 
15960 exit:
15961 	if( subtest )	_DotLocalSubtestFree( subtest );
15962 	if( op )		DNSServiceRefDeallocate( op );
15963 	return( err );
15964 }
15965 
15966 //===========================================================================================================================
15967 //	_DotLocalTestFinalizeSubtest
15968 //===========================================================================================================================
15969 
15970 #define kDotLocalTestReportKey_StartTime				CFSTR( "startTime" )		// String.
15971 #define kDotLocalTestReportKey_EndTime					CFSTR( "endTime" )			// String.
15972 #define kDotLocalTestReportKey_Success					CFSTR( "success" )			// Boolean.
15973 #define kDotLocalTestReportKey_MDNSReplierCmd			CFSTR( "replierCmd" )		// String.
15974 #define kDotLocalTestReportKey_DNSServerCmd				CFSTR( "serverCmd" )		// String.
15975 #define kDotLocalTestReportKey_GetAddrInfoTests			CFSTR( "testsGAI" )			// Array of Dictionaries.
15976 #define kDotLocalTestReportKey_QuerySRVTests			CFSTR( "testsQuerySRV" )	// Array of Dictionaries.
15977 #define kDotLocalTestReportKey_Description				CFSTR( "description" )		// String.
15978 #define kDotLocalTestReportKey_QueryName				CFSTR( "queryName" )		// String.
15979 #define kDotLocalTestReportKey_Error					CFSTR( "error" )			// Integer.
15980 #define kDotLocalTestReportKey_Results					CFSTR( "results" )			// Dictionary of Arrays.
15981 #define kDotLocalTestReportKey_CorrectResults			CFSTR( "correct" )			// Array of Strings
15982 #define kDotLocalTestReportKey_DuplicateResults			CFSTR( "duplicates" )		// Array of Strings.
15983 #define kDotLocalTestReportKey_UnexpectedResults		CFSTR( "unexpected" )		// Array of Strings.
15984 #define kDotLocalTestReportKey_MissingResults			CFSTR( "missing" )			// Array of Strings.
15985 
_DotLocalTestFinalizeSubtest(DotLocalTestContext * inContext)15986 static OSStatus	_DotLocalTestFinalizeSubtest( DotLocalTestContext *inContext )
15987 {
15988 	OSStatus					err;
15989 	DotLocalSubtest *			subtest;
15990 	CFMutableDictionaryRef		reportDict;
15991 	CFMutableDictionaryRef		resultsDict;
15992 	CFMutableArrayRef			missingResults, reportArray;
15993 	char						startTime[ 32 ];
15994 	char						endTime[ 32 ];
15995 
15996 	subtest = inContext->subtest;
15997 	inContext->subtest = NULL;
15998 
15999 	subtest->endTime = NanoTimeGetCurrent();
16000 	_NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
16001 	_NanoTime64ToTimestamp( subtest->endTime, endTime, sizeof( endTime ) );
16002 
16003 	reportDict = NULL;
16004 	err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &reportDict,
16005 		"{"
16006 			"%kO=%s"	// startTime
16007 			"%kO=%s"	// endTime
16008 			"%kO=%s"	// queryName
16009 			"%kO=%s"	// description
16010 			"%kO={%@}"	// results
16011 		"}",
16012 		kDotLocalTestReportKey_StartTime,	startTime,
16013 		kDotLocalTestReportKey_EndTime,		endTime,
16014 		kDotLocalTestReportKey_QueryName,	subtest->queryName,
16015 		kDotLocalTestReportKey_Description,	subtest->testDesc,
16016 		kDotLocalTestReportKey_Results,		&resultsDict );
16017 	require_noerr( err, exit );
16018 
16019 	missingResults = NULL;
16020 	switch( inContext->state )
16021 	{
16022 		case kDotLocalTestState_GAIMDNSOnly:
16023 		case kDotLocalTestState_GAIDNSOnly:
16024 		case kDotLocalTestState_GAIBoth:
16025 		case kDotLocalTestState_GAINeither:
16026 			if( subtest->needDNSv4 || subtest->needDNSv6 || subtest->needMDNSv4 || subtest->needMDNSv6 )
16027 			{
16028 				err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
16029 					"["
16030 						"%.4a"	// Expected DNS IPv4 address
16031 						"%.16a"	// Expected DNS IPv6 address
16032 						"%.4a"	// Expected MDNS IPv4 address
16033 						"%.16a"	// Expected MDNS IPv6 address
16034 					"]",
16035 					subtest->needDNSv4  ? &subtest->addrDNSv4  : NULL,
16036 					subtest->needDNSv6  ?  subtest->addrDNSv6  : NULL,
16037 					subtest->needMDNSv4 ? &subtest->addrMDNSv4 : NULL,
16038 					subtest->needMDNSv6 ?  subtest->addrMDNSv6 : NULL );
16039 				require_noerr( err, exit );
16040 			}
16041 			break;
16042 
16043 		case kDotLocalTestState_QuerySRV:
16044 			if( subtest->needSRV )
16045 			{
16046 				err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
16047 					"["
16048 						"%s"	// Expected SRV record data as a string.
16049 					"]",
16050 					kDotLocalTestSRV_ResultStr );
16051 				require_noerr( err, exit );
16052 			}
16053 			break;
16054 
16055 		case kDotLocalTestState_GAINoSuchRecord:
16056 			if( subtest->needDNSv4 || subtest->needDNSv6 )
16057 			{
16058 				err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &missingResults,
16059 					"["
16060 						"%s" // No Such Record (A)
16061 						"%s" // No Such Record (AAAA)
16062 					"]",
16063 					subtest->needDNSv4 ? kNoSuchRecordAStr    : NULL,
16064 					subtest->needDNSv6 ? kNoSuchRecordAAAAStr : NULL );
16065 				require_noerr( err, exit );
16066 			}
16067 			break;
16068 
16069 		default:
16070 			err = kStateErr;
16071 			goto exit;
16072 	}
16073 
16074 	CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_CorrectResults, subtest->correctResults );
16075 
16076 	if( missingResults )
16077 	{
16078 		CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_MissingResults, missingResults );
16079 		ForgetCF( &missingResults );
16080 		if( !subtest->error ) subtest->error = kNotFoundErr;
16081 	}
16082 
16083 	if( CFArrayGetCount( subtest->unexpectedResults ) > 0 )
16084 	{
16085 		CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_UnexpectedResults, subtest->unexpectedResults );
16086 		if( !subtest->error ) subtest->error = kUnexpectedErr;
16087 	}
16088 
16089 	if( CFArrayGetCount( subtest->duplicateResults ) > 0 )
16090 	{
16091 		CFDictionarySetValue( resultsDict, kDotLocalTestReportKey_DuplicateResults, subtest->duplicateResults );
16092 		if( !subtest->error ) subtest->error = kDuplicateErr;
16093 	}
16094 
16095 	if( subtest->error ) inContext->testFailed = true;
16096 	err = CFDictionarySetInt64( reportDict, kDotLocalTestReportKey_Error, subtest->error );
16097 	require_noerr( err, exit );
16098 
16099 	reportArray = ( inContext->state == kDotLocalTestState_QuerySRV ) ? inContext->reportsQuerySRV : inContext->reportsGAI;
16100 	CFArrayAppendValue( reportArray, reportDict );
16101 
16102 exit:
16103 	_DotLocalSubtestFree( subtest );
16104 	CFReleaseNullSafe( reportDict );
16105 	return( err );
16106 }
16107 
16108 //===========================================================================================================================
16109 //	_DotLocalTestFinalizeAndExit
16110 //===========================================================================================================================
16111 
_DotLocalTestFinalizeAndExit(DotLocalTestContext * inContext)16112 static void	_DotLocalTestFinalizeAndExit( DotLocalTestContext *inContext )
16113 {
16114 	OSStatus				err;
16115 	CFPropertyListRef		plist;
16116 	char					timestampStart[ 32 ];
16117 	char					timestampEnd[ 32 ];
16118 
16119 	check( !inContext->subtest );
16120 	inContext->endTime = NanoTimeGetCurrent();
16121 
16122 	if( inContext->replierPID != -1 )
16123 	{
16124 		kill( inContext->replierPID, SIGTERM );
16125 		inContext->replierPID = -1;
16126 	}
16127 	if( inContext->serverPID != -1 )
16128 	{
16129 		kill( inContext->serverPID, SIGTERM );
16130 		inContext->serverPID = -1;
16131 	}
16132 	err = DNSServiceRemoveRecord( inContext->connection, inContext->localSOARef, 0 );
16133 	require_noerr( err, exit );
16134 
16135 	_NanoTime64ToTimestamp( inContext->startTime, timestampStart, sizeof( timestampStart ) );
16136 	_NanoTime64ToTimestamp( inContext->endTime, timestampEnd, sizeof( timestampEnd ) );
16137 
16138 	err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
16139 		"{"
16140 			"%kO=%s"	// startTime
16141 			"%kO=%s"	// endTime
16142 			"%kO=%O"	// testsGAI
16143 			"%kO=%O"	// testsQuerySRV
16144 			"%kO=%b"	// success
16145 			"%kO=%s"	// replierCmd
16146 			"%kO=%s"	// serverCmd
16147 		"}",
16148 		kDotLocalTestReportKey_StartTime,			timestampStart,
16149 		kDotLocalTestReportKey_EndTime,				timestampEnd,
16150 		kDotLocalTestReportKey_GetAddrInfoTests,	inContext->reportsGAI,
16151 		kDotLocalTestReportKey_QuerySRVTests,		inContext->reportsQuerySRV,
16152 		kDotLocalTestReportKey_Success,				inContext->testFailed ? false : true,
16153 		kDotLocalTestReportKey_MDNSReplierCmd,		inContext->replierCmd,
16154 		kDotLocalTestReportKey_DNSServerCmd,		inContext->serverCmd );
16155 	require_noerr( err, exit );
16156 
16157 	ForgetCF( &inContext->reportsGAI );
16158 	ForgetCF( &inContext->reportsQuerySRV );
16159 
16160 	err = OutputPropertyList( plist, inContext->outputFormat, inContext->outputFilePath );
16161 	CFRelease( plist );
16162 	require_noerr( err, exit );
16163 
16164 	exit( inContext->testFailed ? 2 : 0 );
16165 
16166 exit:
16167 	ErrQuit( 1, "error: %#m\n", err );
16168 }
16169 
16170 //===========================================================================================================================
16171 //	_DotLocalTestProbeQueryRecordCallback
16172 //===========================================================================================================================
16173 
16174 static void DNSSD_API
_DotLocalTestProbeQueryRecordCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inFullName,uint16_t inType,uint16_t inClass,uint16_t inRDataLen,const void * inRDataPtr,uint32_t inTTL,void * inContext)16175 	_DotLocalTestProbeQueryRecordCallback(
16176 		DNSServiceRef			inSDRef,
16177 		DNSServiceFlags			inFlags,
16178 		uint32_t				inInterfaceIndex,
16179 		DNSServiceErrorType		inError,
16180 		const char *			inFullName,
16181 		uint16_t				inType,
16182 		uint16_t				inClass,
16183 		uint16_t				inRDataLen,
16184 		const void *			inRDataPtr,
16185 		uint32_t				inTTL,
16186 		void *					inContext )
16187 {
16188 	DotLocalTestContext * const		context = (DotLocalTestContext *) inContext;
16189 
16190 	Unused( inInterfaceIndex );
16191 	Unused( inFullName );
16192 	Unused( inType );
16193 	Unused( inClass );
16194 	Unused( inRDataLen );
16195 	Unused( inRDataPtr );
16196 	Unused( inTTL );
16197 
16198 	check( context->state == kDotLocalTestState_Preparing );
16199 
16200 	require_quiet( ( inFlags & kDNSServiceFlagsAdd ) && !inError, exit );
16201 
16202 	if( inSDRef == context->op )
16203 	{
16204 		DNSServiceForget( &context->op );
16205 		context->serverIsReady = true;
16206 	}
16207 	else if( inSDRef == context->op2 )
16208 	{
16209 		DNSServiceForget( &context->op2 );
16210 		context->replierIsReady = true;
16211 	}
16212 
16213 	if( context->registeredSOA && context->serverIsReady && context->replierIsReady )
16214 	{
16215 		_DotLocalTestStateMachine( context );
16216 	}
16217 
16218 exit:
16219 	return;
16220 }
16221 
16222 //===========================================================================================================================
16223 //	_DotLocalTestRegisterRecordCallback
16224 //===========================================================================================================================
16225 
16226 static void DNSSD_API
_DotLocalTestRegisterRecordCallback(DNSServiceRef inSDRef,DNSRecordRef inRecordRef,DNSServiceFlags inFlags,DNSServiceErrorType inError,void * inContext)16227 	_DotLocalTestRegisterRecordCallback(
16228 		DNSServiceRef		inSDRef,
16229 		DNSRecordRef		inRecordRef,
16230 		DNSServiceFlags		inFlags,
16231 		DNSServiceErrorType	inError,
16232 		void *				inContext )
16233 {
16234 	DotLocalTestContext * const		context = (DotLocalTestContext *) inContext;
16235 
16236 	Unused( inSDRef );
16237 	Unused( inRecordRef );
16238 	Unused( inFlags );
16239 
16240 	if( inError ) ErrQuit( 1, "error: local. SOA record registration failed: %#m\n", inError );
16241 
16242 	if( !context->registeredSOA )
16243 	{
16244 		context->registeredSOA = true;
16245 		if( context->serverIsReady && context->replierIsReady ) _DotLocalTestStateMachine( context );
16246 	}
16247 }
16248 
16249 //===========================================================================================================================
16250 //	_DotLocalTestTimerHandler
16251 //===========================================================================================================================
16252 
_DotLocalTestTimerHandler(void * inContext)16253 static void	_DotLocalTestTimerHandler( void *inContext )
16254 {
16255 	_DotLocalTestStateMachine( (DotLocalTestContext *) inContext );
16256 }
16257 
16258 //===========================================================================================================================
16259 //	_DotLocalTestGAICallback
16260 //===========================================================================================================================
16261 
16262 static void DNSSD_API
_DotLocalTestGAICallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inHostname,const struct sockaddr * inSockAddr,uint32_t inTTL,void * inContext)16263 	_DotLocalTestGAICallback(
16264 		DNSServiceRef			inSDRef,
16265 		DNSServiceFlags			inFlags,
16266 		uint32_t				inInterfaceIndex,
16267 		DNSServiceErrorType		inError,
16268 		const char *			inHostname,
16269 		const struct sockaddr *	inSockAddr,
16270 		uint32_t				inTTL,
16271 		void *					inContext )
16272 {
16273 	OSStatus						err;
16274 	DotLocalTestContext * const		context = (DotLocalTestContext *) inContext;
16275 	DotLocalSubtest * const			subtest	= context->subtest;
16276 	const sockaddr_ip * const		sip		= (const sockaddr_ip *) inSockAddr;
16277 
16278 	Unused( inSDRef );
16279 	Unused( inInterfaceIndex );
16280 	Unused( inHostname );
16281 	Unused( inTTL );
16282 
16283 	require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
16284 	require_action_quiet( ( sip->sa.sa_family == AF_INET ) || ( sip->sa.sa_family == AF_INET6 ), exit, err = kTypeErr );
16285 
16286 	if( context->state == kDotLocalTestState_GAINoSuchRecord )
16287 	{
16288 		if( inError == kDNSServiceErr_NoSuchRecord )
16289 		{
16290 			CFMutableArrayRef		array = NULL;
16291 			const char *			noSuchRecordStr;
16292 
16293 			if( sip->sa.sa_family == AF_INET )
16294 			{
16295 				array = subtest->needDNSv4 ? subtest->correctResults : subtest->duplicateResults;
16296 				subtest->needDNSv4 = false;
16297 
16298 				noSuchRecordStr = kNoSuchRecordAStr;
16299 			}
16300 			else
16301 			{
16302 				array = subtest->needDNSv6 ? subtest->correctResults : subtest->duplicateResults;
16303 				subtest->needDNSv6 = false;
16304 
16305 				noSuchRecordStr = kNoSuchRecordAAAAStr;
16306 			}
16307 			err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%s", noSuchRecordStr );
16308 			require_noerr( err, fatal );
16309 		}
16310 		else if( !inError )
16311 		{
16312 			err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpectedResults, "%##a", sip );
16313 			require_noerr( err, fatal );
16314 		}
16315 		else
16316 		{
16317 			err = inError;
16318 			goto exit;
16319 		}
16320 	}
16321 	else
16322 	{
16323 		if( !inError )
16324 		{
16325 			CFMutableArrayRef		array = NULL;
16326 
16327 			if( sip->sa.sa_family == AF_INET )
16328 			{
16329 				const uint32_t		addrV4 = sip->v4.sin_addr.s_addr;
16330 
16331 				if( subtest->hasDNSv4 && ( addrV4 == subtest->addrDNSv4 ) )
16332 				{
16333 					array = subtest->needDNSv4 ? subtest->correctResults : subtest->duplicateResults;
16334 					subtest->needDNSv4 = false;
16335 				}
16336 				else if( subtest->hasMDNSv4 && ( addrV4 == subtest->addrMDNSv4 ) )
16337 				{
16338 					array = subtest->needMDNSv4 ? subtest->correctResults : subtest->duplicateResults;
16339 					subtest->needMDNSv4 = false;
16340 				}
16341 			}
16342 			else
16343 			{
16344 				const uint8_t * const		addrV6 = sip->v6.sin6_addr.s6_addr;
16345 
16346 				if( subtest->hasDNSv6 && ( memcmp( addrV6, subtest->addrDNSv6, 16 ) == 0 ) )
16347 				{
16348 					array = subtest->needDNSv6 ? subtest->correctResults : subtest->duplicateResults;
16349 					subtest->needDNSv6 = false;
16350 				}
16351 				else if( subtest->hasMDNSv6 && ( memcmp( addrV6, subtest->addrMDNSv6, 16 ) == 0 ) )
16352 				{
16353 					array = subtest->needMDNSv6 ? subtest->correctResults : subtest->duplicateResults;
16354 					subtest->needMDNSv6 = false;
16355 				}
16356 			}
16357 			if( !array ) array = subtest->unexpectedResults;
16358 			err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%##a", sip );
16359 			require_noerr( err, fatal );
16360 		}
16361 		else if( inError == kDNSServiceErr_NoSuchRecord )
16362 		{
16363 			err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpectedResults, "%s",
16364 				( sip->sa.sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr );
16365 			require_noerr( err, fatal );
16366 		}
16367 		else
16368 		{
16369 			err = inError;
16370 			goto exit;
16371 		}
16372 	}
16373 
16374 exit:
16375 	if( err )
16376 	{
16377 		subtest->error = err;
16378 		_DotLocalTestStateMachine( context );
16379 	}
16380 	return;
16381 
16382 fatal:
16383 	ErrQuit( 1, "error: %#m\n", err );
16384 }
16385 
16386 //===========================================================================================================================
16387 //	_DotLocalTestQueryRecordCallback
16388 //===========================================================================================================================
16389 
16390 static void DNSSD_API
_DotLocalTestQueryRecordCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inFullName,uint16_t inType,uint16_t inClass,uint16_t inRDataLen,const void * inRDataPtr,uint32_t inTTL,void * inContext)16391 	_DotLocalTestQueryRecordCallback(
16392 		DNSServiceRef			inSDRef,
16393 		DNSServiceFlags			inFlags,
16394 		uint32_t				inInterfaceIndex,
16395 		DNSServiceErrorType		inError,
16396 		const char *			inFullName,
16397 		uint16_t				inType,
16398 		uint16_t				inClass,
16399 		uint16_t				inRDataLen,
16400 		const void *			inRDataPtr,
16401 		uint32_t				inTTL,
16402 		void *					inContext )
16403 {
16404 	OSStatus							err;
16405 	DotLocalTestContext * const			context = (DotLocalTestContext *) inContext;
16406 	DotLocalSubtest * const				subtest = context->subtest;
16407 	const dns_fixed_fields_srv *		fields;
16408 	const uint8_t *						target;
16409 	const uint8_t *						ptr;
16410 	const uint8_t *						end;
16411 	char *								rdataStr;
16412 	unsigned int						priority, weight, port;
16413 	CFMutableArrayRef					array;
16414 
16415 	Unused( inSDRef );
16416 	Unused( inInterfaceIndex );
16417 	Unused( inFullName );
16418 	Unused( inTTL );
16419 
16420 	check( context->state == kDotLocalTestState_QuerySRV );
16421 
16422 	err = inError;
16423 	require_noerr_quiet( err, exit );
16424 	require_action_quiet( inFlags & kDNSServiceFlagsAdd, exit, err = kFlagErr );
16425 	require_action_quiet( ( inType == kDNSServiceType_SRV ) && ( inClass == kDNSServiceClass_IN ), exit, err = kTypeErr );
16426 	require_action_quiet( inRDataLen > sizeof( dns_fixed_fields_srv ), exit, err = kSizeErr );
16427 
16428 	fields		= (const dns_fixed_fields_srv *) inRDataPtr;
16429 	priority	= dns_fixed_fields_srv_get_priority( fields );
16430 	weight		= dns_fixed_fields_srv_get_weight( fields );
16431 	port		= dns_fixed_fields_srv_get_port( fields );
16432 	target		= (const uint8_t *) &fields[ 1 ];
16433 	end			= ( (const uint8_t *) inRDataPtr ) + inRDataLen;
16434 	for( ptr = target; ( ptr < end ) && ( *ptr != 0 ); ptr += ( 1 + *ptr ) ) {}
16435 
16436 	if( ( priority == kDotLocalTestSRV_Priority ) &&
16437 		( weight   == kDotLocalTestSRV_Weight )   &&
16438 		( port     == kDotLocalTestSRV_Port )     &&
16439 		( ptr < end ) && DomainNameEqual( target, kDotLocalTestSRV_TargetName ) )
16440 	{
16441 		array = subtest->needSRV ? subtest->correctResults : subtest->duplicateResults;
16442 		subtest->needSRV = false;
16443 	}
16444 	else
16445 	{
16446 		array = subtest->unexpectedResults;
16447 	}
16448 
16449 	rdataStr = NULL;
16450 	DNSRecordDataToString( inRDataPtr, inRDataLen, kDNSServiceType_SRV, &rdataStr );
16451 	if( !rdataStr )
16452 	{
16453 		ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, inRDataLen );
16454 		require_action( rdataStr, fatal, err = kNoMemoryErr );
16455 	}
16456 
16457 	err = CFPropertyListAppendFormatted( kCFAllocatorDefault, array, "%s", rdataStr );
16458 	free( rdataStr );
16459 	require_noerr( err, fatal );
16460 
16461 exit:
16462 	if( err )
16463 	{
16464 		subtest->error = err;
16465 		_DotLocalTestStateMachine( context );
16466 	}
16467 	return;
16468 
16469 fatal:
16470 	ErrQuit( 1, "error: %#m\n", err );
16471 }
16472 
16473 //===========================================================================================================================
16474 //	ProbeConflictTestCmd
16475 //===========================================================================================================================
16476 
16477 #define kProbeConflictTestService_DefaultName		"pctest-name"
16478 #define kProbeConflictTestService_Port				60000
16479 
16480 #define kProbeConflictTestTXTPtr		"\x13" "PROBE-CONFLICT-TEST"
16481 #define kProbeConflictTestTXTLen		sizeof_string( kProbeConflictTestTXTPtr )
16482 
16483 typedef struct
16484 {
16485 	const char *		description;
16486 	const char *		program;
16487 	Boolean				expectsRename;
16488 
16489 }	ProbeConflictTestCase;
16490 
16491 #define kPCTProgPreWait			"wait 1000;"	// Wait 1 second before sending gratuitous response.
16492 #define kPCTProgPostWait		"wait 10000;"	// Wait 10 seconds after sending gratuitous response.
16493 												// This allows ~2.75 seconds for probing, ~5 seconds for a rename, and
16494 												// a 2 second fudge factor for unexpected system delays.
16495 
16496 static const ProbeConflictTestCase		kProbeConflictTestCases[] =
16497 {
16498 	// No conflicts
16499 
16500 	{ "No probe conflicts.",                       kPCTProgPreWait "probes n-n-n;"       "send;" kPCTProgPostWait, false },
16501 
16502 	// One multicast probe conflict
16503 
16504 	{ "One multicast probe conflict (1).",         kPCTProgPreWait "probes m;"           "send;" kPCTProgPostWait, false },
16505 	{ "One multicast probe conflict (2).",         kPCTProgPreWait "probes n-m;"         "send;" kPCTProgPostWait, false },
16506 	{ "One multicast probe conflict (3).",         kPCTProgPreWait "probes n-n-m;"       "send;" kPCTProgPostWait, false },
16507 
16508 	// One unicast probe conflict
16509 
16510 	{ "One unicast probe conflict (1).",           kPCTProgPreWait "probes u;"           "send;" kPCTProgPostWait, true },
16511 	{ "One unicast probe conflict (2).",           kPCTProgPreWait "probes n-u;"         "send;" kPCTProgPostWait, true },
16512 	{ "One unicast probe conflict (3).",           kPCTProgPreWait "probes n-n-u;"       "send;" kPCTProgPostWait, true },
16513 
16514 	// One multicast and one unicast probe conflict
16515 
16516 	{ "Multicast and unicast probe conflict (1).", kPCTProgPreWait "probes m-u;"         "send;" kPCTProgPostWait, true },
16517 	{ "Multicast and unicast probe conflict (2).", kPCTProgPreWait "probes m-n-u;"       "send;" kPCTProgPostWait, true },
16518 	{ "Multicast and unicast probe conflict (3).", kPCTProgPreWait "probes m-n-n-u;"     "send;" kPCTProgPostWait, true },
16519 	{ "Multicast and unicast probe conflict (4).", kPCTProgPreWait "probes n-m-u;"       "send;" kPCTProgPostWait, true },
16520 	{ "Multicast and unicast probe conflict (5).", kPCTProgPreWait "probes n-m-n-u;"     "send;" kPCTProgPostWait, true },
16521 	{ "Multicast and unicast probe conflict (6).", kPCTProgPreWait "probes n-m-n-n-u;"   "send;" kPCTProgPostWait, true },
16522 	{ "Multicast and unicast probe conflict (7).", kPCTProgPreWait "probes n-n-m-u;"     "send;" kPCTProgPostWait, true },
16523 	{ "Multicast and unicast probe conflict (8).", kPCTProgPreWait "probes n-n-m-n-u;"   "send;" kPCTProgPostWait, true },
16524 	{ "Multicast and unicast probe conflict (9).", kPCTProgPreWait "probes n-n-m-n-n-u;" "send;" kPCTProgPostWait, true },
16525 
16526 	// Two multicast probe conflicts
16527 
16528 	{ "Two multicast probe conflicts (1).",        kPCTProgPreWait "probes m-m;"         "send;" kPCTProgPostWait, true },
16529 	{ "Two multicast probe conflicts (2).",        kPCTProgPreWait "probes m-n-m;"       "send;" kPCTProgPostWait, true },
16530 	{ "Two multicast probe conflicts (3).",        kPCTProgPreWait "probes m-n-n-m;"     "send;" kPCTProgPostWait, true },
16531 	{ "Two multicast probe conflicts (4).",        kPCTProgPreWait "probes n-m-m;"       "send;" kPCTProgPostWait, true },
16532 	{ "Two multicast probe conflicts (5).",        kPCTProgPreWait "probes n-m-n-m-n;"   "send;" kPCTProgPostWait, true },
16533 	{ "Two multicast probe conflicts (6).",        kPCTProgPreWait "probes n-m-n-n-m;"   "send;" kPCTProgPostWait, true },
16534 	{ "Two multicast probe conflicts (7).",        kPCTProgPreWait "probes n-n-m-m;"     "send;" kPCTProgPostWait, true },
16535 	{ "Two multicast probe conflicts (8).",        kPCTProgPreWait "probes n-n-m-n-m;"   "send;" kPCTProgPostWait, true },
16536 	{ "Two multicast probe conflicts (9).",        kPCTProgPreWait "probes n-n-m-n-n-m;" "send;" kPCTProgPostWait, true },
16537 };
16538 
16539 #define kProbeConflictTestCaseCount		countof( kProbeConflictTestCases )
16540 
16541 typedef struct
16542 {
16543 	DNSServiceRef				registration;	// Test service registration.
16544 	NanoTime64					testStartTime;	// Test's start time.
16545 	NanoTime64					startTime;		// Current test case's start time.
16546 	MDNSColliderRef				collider;		// mDNS collider object.
16547 	CFMutableArrayRef			results;		// Array of test case results.
16548 	char *						serviceName;	// Test service's instance name as a string. (malloced)
16549 	char *						serviceType;	// Test service's service type as a string. (malloced)
16550 	uint8_t *					recordName;		// FQDN of collider's record (same as test service's records). (malloced)
16551 	dispatch_source_t			sigSourceINT;	// SIGINT signal handler.
16552 	dispatch_source_t			sigSourceTERM;	// SIGTERM signal handler.
16553 	CFStringRef					exComputerName;	// Previous ComputerName.
16554 	CFStringRef					exLocalHostName;// Previous LocalHostName.
16555 	CFStringEncoding			exCompNameEnc;	// Previous ComputerName's encoding.
16556 	unsigned int				testCaseIndex;	// Index of the current test case.
16557 	uint32_t					ifIndex;		// Index of the interface that the collider is to operate on.
16558 	MDNSColliderProtocols		protocol;		// mDNS collider's IP protocol.
16559 	char *						outputFilePath;	// File to write test results to. If NULL, then write to stdout. (malloced)
16560 	OutputFormatType			outputFormat;	// Format of test report output.
16561 	Boolean						registered;		// True if the test service instance is currently registered.
16562 	Boolean						testFailed;		// True if at least one test case failed.
16563 
16564 }	ProbeConflictTestContext;
16565 
16566 static void DNSSD_API
16567 	_ProbeConflictTestRegisterCallback(
16568 		DNSServiceRef		inSDRef,
16569 		DNSServiceFlags		inFlags,
16570 		DNSServiceErrorType	inError,
16571 		const char *		inName,
16572 		const char *		inType,
16573 		const char *		inDomain,
16574 		void *				inContext );
16575 static void		_ProbeConflictTestColliderStopHandler( void *inContext, OSStatus inError );
16576 static OSStatus	_ProbeConflictTestStartNextTest( ProbeConflictTestContext *inContext );
16577 static OSStatus	_ProbeConflictTestStopCurrentTest( ProbeConflictTestContext *inContext, Boolean inRenamed );
16578 static void		_ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext *inContext ) ATTRIBUTE_NORETURN;
16579 static void		_ProbeConflictTestRestoreSystemNames( ProbeConflictTestContext *inContext );
16580 static void		_ProbeConflictTestSignalHandler( void *inContext );
16581 
ProbeConflictTestCmd(void)16582 static void	ProbeConflictTestCmd( void )
16583 {
16584 	OSStatus						err;
16585 	ProbeConflictTestContext *		context;
16586 	const char *					serviceName;
16587 	CFStringRef						computerName	= NULL;
16588 	CFStringRef						localHostName	= NULL;
16589 	char *							uniqueName;
16590 	char							tag[ 6 + 1 ];
16591 
16592 	context = (ProbeConflictTestContext *) calloc( 1, sizeof( *context ) );
16593 	require_action( context, exit, err = kNoMemoryErr );
16594 
16595 	if( gProbeConflictTest_Interface )
16596 	{
16597 		err = InterfaceIndexFromArgString( gProbeConflictTest_Interface, &context->ifIndex );
16598 		require_noerr_quiet( err, exit );
16599 	}
16600 	else
16601 	{
16602 		err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_All, NULL, &context->ifIndex );
16603 		require_noerr_quiet( err, exit );
16604 	}
16605 
16606 	if( gProbeConflictTest_UseIPv6 )
16607 	{
16608 		if( gProbeConflictTest_UseIPv4 )
16609 		{
16610 			FPrintF( stderr, "error: --ipv4 and --ipv6 are mutually exclusive options.\n" );
16611 			goto exit;
16612 		}
16613 		context->protocol = kMDNSColliderProtocol_IPv6;
16614 	}
16615 	else
16616 	{
16617 		context->protocol = kMDNSColliderProtocol_IPv4;
16618 	}
16619 
16620 	if( gProbeConflictTest_OutputFilePath )
16621 	{
16622 		context->outputFilePath = strdup( gProbeConflictTest_OutputFilePath );
16623 		require_action( context->outputFilePath, exit, err = kNoMemoryErr );
16624 	}
16625 
16626 	err = OutputFormatFromArgString( gProbeConflictTest_OutputFormat, &context->outputFormat );
16627 	require_noerr_quiet( err, exit );
16628 
16629 	context->results = CFArrayCreateMutable( NULL, kProbeConflictTestCaseCount, &kCFTypeArrayCallBacks );
16630 	require_action( context->results, exit, err = kNoMemoryErr );
16631 
16632 	context->testStartTime = NanoTimeGetCurrent();
16633 
16634 	// Set a unique ComputerName.
16635 
16636 	computerName = SCDynamicStoreCopyComputerName( NULL, &context->exCompNameEnc );
16637 	err = map_scerror( computerName );
16638 	require_noerr( err, exit );
16639 
16640 	_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag );
16641 	ASPrintF( &uniqueName, "dnssdutil-pctest-computer-name-%s", tag );
16642 	require_action( uniqueName, exit, err = kNoMemoryErr );
16643 
16644 	err = _SetComputerNameWithUTF8CString( uniqueName );
16645 	ForgetMem( &uniqueName );
16646 	require_noerr( err, exit );
16647 	context->exComputerName = computerName;
16648 	computerName = NULL;
16649 
16650 	// Set a unique LocalHostName.
16651 
16652 	localHostName = SCDynamicStoreCopyLocalHostName( NULL );
16653 	err = map_scerror( localHostName );
16654 	require_noerr( err, exit );
16655 
16656 	ASPrintF( &uniqueName, "dnssdutil-pctest-local-hostname-%s", tag );
16657 	require_action( uniqueName, exit, err = kNoMemoryErr );
16658 
16659 	err = _SetLocalHostNameWithUTF8CString( uniqueName );
16660 	ForgetMem( &uniqueName );
16661 	require_noerr( err, exit );
16662 	context->exLocalHostName = localHostName;
16663 	localHostName = NULL;
16664 
16665 	// Set up SIGINT signal handler.
16666 
16667 	signal( SIGINT, SIG_IGN );
16668 	err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), _ProbeConflictTestSignalHandler, context,
16669 		&context->sigSourceINT );
16670 	require_noerr( err, exit );
16671 	dispatch_resume( context->sigSourceINT );
16672 
16673 	// Set up SIGTERM signal handler.
16674 
16675 	signal( SIGTERM, SIG_IGN );
16676 	err = DispatchSignalSourceCreate( SIGTERM, dispatch_get_main_queue(), _ProbeConflictTestSignalHandler, context,
16677 		&context->sigSourceTERM );
16678 	require_noerr( err, exit );
16679 	dispatch_resume( context->sigSourceTERM );
16680 
16681 	// Register the test service instance.
16682 
16683 	serviceName = gProbeConflictTest_UseComputerName ? NULL : kProbeConflictTestService_DefaultName;
16684 
16685 	ASPrintF( &context->serviceType, "_pctest-%s._udp", tag );
16686 	require_action( context->serviceType, exit, err = kNoMemoryErr );
16687 
16688 	err = DNSServiceRegister( &context->registration, 0, context->ifIndex, serviceName, context->serviceType, "local.",
16689 		NULL, htons( kProbeConflictTestService_Port ), 0, NULL, _ProbeConflictTestRegisterCallback, context );
16690 	require_noerr( err, exit );
16691 
16692 	err = DNSServiceSetDispatchQueue( context->registration, dispatch_get_main_queue() );
16693 	require_noerr( err, exit );
16694 
16695 	dispatch_main();
16696 
16697 exit:
16698 	CFReleaseNullSafe( computerName );
16699 	CFReleaseNullSafe( localHostName );
16700 	if( context ) _ProbeConflictTestRestoreSystemNames( context );
16701 	ErrQuit( 1, "error: %#m\n", err );
16702 }
16703 
16704 //===========================================================================================================================
16705 //	_ProbeConflictTestRegisterCallback
16706 //===========================================================================================================================
16707 
16708 static void DNSSD_API
_ProbeConflictTestRegisterCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,DNSServiceErrorType inError,const char * inName,const char * inType,const char * inDomain,void * inContext)16709 	_ProbeConflictTestRegisterCallback(
16710 		DNSServiceRef		inSDRef,
16711 		DNSServiceFlags		inFlags,
16712 		DNSServiceErrorType	inError,
16713 		const char *		inName,
16714 		const char *		inType,
16715 		const char *		inDomain,
16716 		void *				inContext )
16717 {
16718 	OSStatus								err;
16719 	ProbeConflictTestContext * const		context = (ProbeConflictTestContext *) inContext;
16720 
16721 	Unused( inSDRef );
16722 	Unused( inType );
16723 	Unused( inDomain );
16724 
16725 	err = inError;
16726 	require_noerr( err, exit );
16727 
16728 	if( !context->registered )
16729 	{
16730 		if( inFlags & kDNSServiceFlagsAdd )
16731 		{
16732 			uint8_t *			ptr;
16733 			size_t				recordNameLen;
16734 			unsigned int		len;
16735 			uint8_t				name[ kDomainNameLengthMax ];
16736 
16737 			context->registered = true;
16738 
16739 			FreeNullSafe( context->serviceName );
16740 			context->serviceName = strdup( inName );
16741 			require_action( context->serviceName, exit, err = kNoMemoryErr );
16742 
16743 			err = DomainNameFromString( name, context->serviceName, NULL );
16744 			require_noerr( err, exit );
16745 
16746 			err = DomainNameAppendString( name, context->serviceType, NULL );
16747 			require_noerr( err, exit );
16748 
16749 			err = DomainNameAppendString( name, "local", NULL );
16750 			require_noerr( err, exit );
16751 
16752 			ForgetMem( &context->recordName );
16753 			err = DomainNameDup( name, &context->recordName, &recordNameLen );
16754 			require_noerr( err, exit );
16755 			require_fatal( recordNameLen > 0, "Record name length is zero." );	// Prevents dubious static analyzer warning.
16756 
16757 			// Make the first label all caps so that it's easier to spot in system logs.
16758 
16759 			ptr = context->recordName;
16760 			for( len = *ptr++; len > 0; --len, ++ptr ) *ptr = (uint8_t) toupper_safe( *ptr );
16761 
16762 			err = _ProbeConflictTestStartNextTest( context );
16763 			require_noerr( err, exit );
16764 		}
16765 	}
16766 	else
16767 	{
16768 		if( !( inFlags & kDNSServiceFlagsAdd ) )
16769 		{
16770 			context->registered = false;
16771 			err = _ProbeConflictTestStopCurrentTest( context, true );
16772 			require_noerr( err, exit );
16773 		}
16774 	}
16775 	err = kNoErr;
16776 
16777 exit:
16778 	if( err ) exit( 1 );
16779 }
16780 
16781 //===========================================================================================================================
16782 //	_ProbeConflictTestColliderStopHandler
16783 //===========================================================================================================================
16784 
_ProbeConflictTestColliderStopHandler(void * inContext,OSStatus inError)16785 static void	_ProbeConflictTestColliderStopHandler( void *inContext, OSStatus inError )
16786 {
16787 	OSStatus								err;
16788 	ProbeConflictTestContext * const		context = (ProbeConflictTestContext *) inContext;
16789 
16790 	err = inError;
16791 	require_noerr_quiet( err, exit );
16792 
16793 	ForgetCF( &context->collider );
16794 
16795 	err = _ProbeConflictTestStopCurrentTest( context, false );
16796 	require_noerr( err, exit );
16797 
16798 	err = _ProbeConflictTestStartNextTest( context );
16799 	require_noerr( err, exit );
16800 
16801 exit:
16802 	if( err ) exit( 1 );
16803 }
16804 
16805 //===========================================================================================================================
16806 //	_ProbeConflictTestStartNextTest
16807 //===========================================================================================================================
16808 
_ProbeConflictTestStartNextTest(ProbeConflictTestContext * inContext)16809 static OSStatus	_ProbeConflictTestStartNextTest( ProbeConflictTestContext *inContext )
16810 {
16811 	OSStatus							err;
16812 	const ProbeConflictTestCase *		testCase;
16813 
16814 	check( !inContext->collider );
16815 
16816 	if( inContext->testCaseIndex < kProbeConflictTestCaseCount )
16817 	{
16818 		testCase = &kProbeConflictTestCases[ inContext->testCaseIndex ];
16819 	}
16820 	else
16821 	{
16822 		_ProbeConflictTestFinalizeAndExit( inContext );
16823 	}
16824 
16825 	err = MDNSColliderCreate( dispatch_get_main_queue(), &inContext->collider );
16826 	require_noerr( err, exit );
16827 
16828 	err = MDNSColliderSetProgram( inContext->collider, testCase->program );
16829 	require_noerr( err, exit );
16830 
16831 	err = MDNSColliderSetRecord( inContext->collider, inContext->recordName, kDNSServiceType_TXT,
16832 		kProbeConflictTestTXTPtr, kProbeConflictTestTXTLen );
16833 	require_noerr( err, exit );
16834 
16835 	MDNSColliderSetProtocols( inContext->collider, inContext->protocol );
16836 	MDNSColliderSetInterfaceIndex( inContext->collider, inContext->ifIndex );
16837 	MDNSColliderSetStopHandler( inContext->collider, _ProbeConflictTestColliderStopHandler, inContext );
16838 
16839 	inContext->startTime = NanoTimeGetCurrent();
16840 	err = MDNSColliderStart( inContext->collider );
16841 	require_noerr( err, exit );
16842 
16843 exit:
16844 	return( err );
16845 }
16846 
16847 //===========================================================================================================================
16848 //	_ProbeConflictTestStopCurrentTest
16849 //===========================================================================================================================
16850 
16851 #define kProbeConflictTestCaseResultKey_Description			CFSTR( "description" )
16852 #define kProbeConflictTestCaseResultKey_StartTime			CFSTR( "startTime" )
16853 #define kProbeConflictTestCaseResultKey_EndTime				CFSTR( "endTime" )
16854 #define kProbeConflictTestCaseResultKey_ExpectedRename		CFSTR( "expectedRename" )
16855 #define kProbeConflictTestCaseResultKey_ServiceName			CFSTR( "serviceName" )
16856 #define kProbeConflictTestCaseResultKey_Passed				CFSTR( "passed" )
16857 
_ProbeConflictTestStopCurrentTest(ProbeConflictTestContext * inContext,Boolean inRenamed)16858 static OSStatus	_ProbeConflictTestStopCurrentTest( ProbeConflictTestContext *inContext, Boolean inRenamed )
16859 {
16860 	OSStatus							err;
16861 	const ProbeConflictTestCase *		testCase;
16862 	NanoTime64							now;
16863 	Boolean								passed;
16864 	char								startTime[ 32 ];
16865 	char								endTime[ 32 ];
16866 
16867 	now = NanoTimeGetCurrent();
16868 
16869 	if( inContext->collider )
16870 	{
16871 		MDNSColliderSetStopHandler( inContext->collider, NULL, NULL );
16872 		MDNSColliderStop( inContext->collider );
16873 		CFRelease( inContext->collider );
16874 		inContext->collider = NULL;
16875 	}
16876 
16877 	testCase = &kProbeConflictTestCases[ inContext->testCaseIndex ];
16878 	passed = ( ( testCase->expectsRename && inRenamed ) || ( !testCase->expectsRename && !inRenamed ) ) ? true : false;
16879 	if( !passed ) inContext->testFailed = true;
16880 
16881 	_NanoTime64ToTimestamp( inContext->startTime, startTime, sizeof( startTime ) );
16882 	_NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
16883 
16884 	err = CFPropertyListAppendFormatted( kCFAllocatorDefault, inContext->results,
16885 		"{"
16886 			"%kO=%s"	// description
16887 			"%kO=%b"	// expectedRename
16888 			"%kO=%s"	// startTime
16889 			"%kO=%s"	// endTime
16890 			"%kO=%s"	// serviceName
16891 			"%kO=%b"	// passed
16892 		"}",
16893 		kProbeConflictTestCaseResultKey_Description,	testCase->description,
16894 		kProbeConflictTestCaseResultKey_ExpectedRename,	testCase->expectsRename,
16895 		kProbeConflictTestCaseResultKey_StartTime,		startTime,
16896 		kProbeConflictTestCaseResultKey_EndTime,		endTime,
16897 		kProbeConflictTestCaseResultKey_ServiceName,	inContext->serviceName,
16898 		kProbeConflictTestCaseResultKey_Passed,			passed );
16899 	require_noerr( err, exit );
16900 
16901 	++inContext->testCaseIndex;
16902 
16903 exit:
16904 	return( err );
16905 }
16906 
16907 //===========================================================================================================================
16908 //	_ProbeConflictTestFinalizeAndExit
16909 //===========================================================================================================================
16910 
16911 #define kProbeConflictTestReportKey_StartTime		CFSTR( "startTime" )
16912 #define kProbeConflictTestReportKey_EndTime			CFSTR( "endTime" )
16913 #define kProbeConflictTestReportKey_ServiceType		CFSTR( "serviceType" )
16914 #define kProbeConflictTestReportKey_Results			CFSTR( "results" )
16915 #define kProbeConflictTestReportKey_Passed			CFSTR( "passed" )
16916 
_ProbeConflictTestFinalizeAndExit(ProbeConflictTestContext * inContext)16917 static void	_ProbeConflictTestFinalizeAndExit( ProbeConflictTestContext *inContext )
16918 {
16919 	OSStatus				err;
16920 	CFPropertyListRef		plist;
16921 	NanoTime64				now;
16922 	int						exitCode;
16923 	char					startTime[ 32 ];
16924 	char					endTime[ 32 ];
16925 
16926 	now = NanoTimeGetCurrent();
16927 
16928 	check( !inContext->collider );
16929 
16930 	_NanoTime64ToTimestamp( inContext->testStartTime, startTime, sizeof( startTime ) );
16931 	_NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
16932 
16933 	err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
16934 		"{"
16935 			"%kO=%s"	// startTime
16936 			"%kO=%s"	// endTime
16937 			"%kO=%s"	// serviceType
16938 			"%kO=%O"	// results
16939 			"%kO=%b"	// passed
16940 		"}",
16941 		kProbeConflictTestReportKey_StartTime,		startTime,
16942 		kProbeConflictTestReportKey_EndTime,		endTime,
16943 		kProbeConflictTestReportKey_ServiceType,	inContext->serviceType,
16944 		kProbeConflictTestReportKey_Results,		inContext->results,
16945 		kProbeConflictTestReportKey_Passed,			inContext->testFailed ? false : true );
16946 	require_noerr( err, exit );
16947 	ForgetCF( &inContext->results );
16948 
16949 	err = OutputPropertyList( plist, inContext->outputFormat, inContext->outputFilePath );
16950 	CFRelease( plist );
16951 	require_noerr( err, exit );
16952 
16953 exit:
16954 	_ProbeConflictTestRestoreSystemNames( inContext );
16955 	if( err )
16956 	{
16957 		FPrintF( stderr, "error: %#m\n", err );
16958 		exitCode = 1;
16959 	}
16960 	else
16961 	{
16962 		exitCode = inContext->testFailed ? 2 : 0;
16963 	}
16964 	exit( exitCode );
16965 }
16966 
16967 //===========================================================================================================================
16968 //	_ProbeConflictTestRestoreSystemNames
16969 //===========================================================================================================================
16970 
_ProbeConflictTestRestoreSystemNames(ProbeConflictTestContext * inContext)16971 static void	_ProbeConflictTestRestoreSystemNames( ProbeConflictTestContext *inContext )
16972 {
16973 	OSStatus		err;
16974 
16975 	if( inContext->exComputerName )
16976 	{
16977 		err = _SetComputerName( inContext->exComputerName, inContext->exCompNameEnc );
16978 		check_noerr( err );
16979 		ForgetCF( &inContext->exComputerName );
16980 	}
16981 	if( inContext->exLocalHostName )
16982 	{
16983 		err = _SetLocalHostName( inContext->exLocalHostName );
16984 		check_noerr( err );
16985 		ForgetCF( &inContext->exLocalHostName );
16986 	}
16987 }
16988 
16989 //===========================================================================================================================
16990 //	_ProbeConflictTestSignalHandler
16991 //===========================================================================================================================
16992 
_ProbeConflictTestSignalHandler(void * inContext)16993 static void	_ProbeConflictTestSignalHandler( void *inContext )
16994 {
16995 	_ProbeConflictTestRestoreSystemNames( inContext );
16996 	FPrintF( stderr, "Probe conflict test got a SIGINT or SIGTERM signal, exiting...\n" );
16997 	exit( 1 );
16998 }
16999 
17000 #if( MDNSRESPONDER_PROJECT )
17001 //===========================================================================================================================
17002 //    FallbackTestCmd
17003 //===========================================================================================================================
17004 
17005 typedef struct
17006 {
17007 	unsigned int		serverIndex;		// Index of server that is soley capable of answering query.
17008 
17009 }	FallbackSubtestParams;
17010 
17011 #define kFallbackTestSubtestCount		8
17012 
17013 const FallbackSubtestParams		kFallbackSubtestParams[] = { { 2 }, { 4 }, { 1 }, { 3 }, { 2 }, { 1 }, { 4 }, { 3 } };
17014 check_compile_time( countof( kFallbackSubtestParams ) == kFallbackTestSubtestCount );
17015 
17016 typedef struct
17017 {
17018 	char *			hostname;	// Hostname to resolve.
17019 	NanoTime64		startTime;	// Subtest's start time.
17020 	NanoTime64		endTime;	// Subtest's end time.
17021 	OSStatus		error;		// Subtest's current error.
17022 
17023 }	FallbackSubtest;
17024 
17025 typedef struct
17026 {
17027 	dispatch_queue_t			queue;			// Serial queue for test events.
17028 	dispatch_semaphore_t		doneSem;		// Semaphore to signal when the test is done.
17029 	DNSServiceRef				gai;			// Current DNSServiceGetAddrInfo request.
17030 	dispatch_source_t			timer;			// Timer for enforcing time limit on current DNSServiceGetAddrInfo request.
17031 	size_t						subtestIndex;	// Index of current subtest.
17032 	pid_t						serverPID;		// PID of spawned test DNS server.
17033 	OSStatus					error;			// Current test error.
17034 	NanoTime64					startTime;		// Test's start time.
17035 	NanoTime64					endTime;		// Test's end time.
17036 	char *						serverCmd;		// Command used to invoke the test DNS server.
17037 	char *						probeHostname;	// Hostname queried to verify that server is up and running.
17038 	FallbackSubtest				subtests[ kFallbackTestSubtestCount ];
17039 	Boolean						useRefused;		// True if server uses Refused RCODE for queries it's not allowed to answer.
17040 
17041 }	FallbackTest;
17042 
17043 static OSStatus	_FallbackTestCreate( FallbackTest **outTest );
17044 static OSStatus	_FallbackTestRun( FallbackTest *inTest );
17045 static void		_FallbackTestFree( FallbackTest *inTest );
17046 
17047 ulog_define_ex( kDNSSDUtilIdentifier, FallbackTest, kLogLevelInfo, kLogFlags_None, "FallbackTest", NULL );
17048 #define ft_ulog( LEVEL, ... )		ulog( &log_category_from_name( FallbackTest ), (LEVEL), __VA_ARGS__ )
17049 
FallbackTestCmd(void)17050 static void	FallbackTestCmd( void )
17051 {
17052 	OSStatus				err;
17053 	FallbackTest *			test		= NULL;
17054 	CFPropertyListRef		plist		= NULL;
17055 	OutputFormatType		outputFormat;
17056 	size_t					i;
17057 	CFMutableArrayRef		results;
17058 	Boolean					testPassed	= false;
17059 	Boolean					subtestFailed;
17060 	char					startTime[ 32 ];
17061 	char					endTime[ 32 ];
17062 
17063 	err = CheckRootUser();
17064 	require_noerr_quiet( err, exit );
17065 
17066 	err = OutputFormatFromArgString( gFallbackTest_OutputFormat, &outputFormat );
17067 	require_noerr_quiet( err, exit );
17068 
17069 	err = _FallbackTestCreate( &test );
17070 	require_noerr( err, exit );
17071 
17072 	if( gFallbackTest_UseRefused ) test->useRefused = true;
17073 	err = _FallbackTestRun( test );
17074 	require_noerr( err, exit );
17075 
17076 	_NanoTime64ToTimestamp( test->startTime, startTime, sizeof( startTime ) );
17077 	_NanoTime64ToTimestamp( test->endTime, endTime, sizeof( endTime ) );
17078 	err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
17079 		"{"
17080 			"%kO=%s"	// startTime
17081 			"%kO=%s"	// endTime
17082 			"%kO=%s"	// serverCmd
17083 			"%kO=[%@]"	// results
17084 		"}",
17085 		CFSTR( "startTime" ),	startTime,
17086 		CFSTR( "endTime" ),		endTime,
17087 		CFSTR( "serverCmd" ),	test->serverCmd,
17088 		CFSTR( "results" ),		&results );
17089 	require_noerr( err, exit );
17090 
17091 	subtestFailed = false;
17092 	check( test->subtestIndex == kFallbackTestSubtestCount );
17093 	for( i = 0; i < kFallbackTestSubtestCount; ++i )
17094 	{
17095 		CFMutableDictionaryRef		resultDict;
17096 		FallbackSubtest * const		subtest = &test->subtests[ i ];
17097 		char						errorDesc[ 128 ];
17098 
17099 		err = CFPropertyListAppendFormatted( kCFAllocatorDefault, results, "{%@}", &resultDict );
17100 		require_noerr( err, exit );
17101 
17102 		err = CFDictionarySetCString( resultDict, CFSTR( "name" ), subtest->hostname, kSizeCString );
17103 		require_noerr( err, exit );
17104 
17105 		_NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
17106 		err = CFDictionarySetCString( resultDict, CFSTR( "startTime" ), startTime, kSizeCString );
17107 		require_noerr( err, exit );
17108 
17109 		_NanoTime64ToTimestamp( subtest->endTime, endTime, sizeof( endTime ) );
17110 		err = CFDictionarySetCString( resultDict, CFSTR( "endTime" ), endTime, kSizeCString );
17111 		require_noerr( err, exit );
17112 
17113 		SNPrintF( errorDesc, sizeof( errorDesc ), "%m", subtest->error );
17114 		err = CFPropertyListAppendFormatted( kCFAllocatorDefault, resultDict,
17115 			"%kO="
17116 			"{"
17117 				"%kO=%lli"	// code
17118 				"%kO=%s"	// description
17119 			"}",
17120 			CFSTR( "error" ),
17121 			CFSTR( "code" ),		(int64_t) subtest->error,
17122 			CFSTR( "description" ),	errorDesc );
17123 		require_noerr( err, exit );
17124 
17125 		if( subtest->error ) subtestFailed = true;
17126 	}
17127 	if( !subtestFailed ) testPassed = true;
17128 	CFPropertyListAppendFormatted( kCFAllocatorDefault, plist, "%kO=%b", CFSTR( "pass" ), testPassed );
17129 
17130 	err = OutputPropertyList( plist, outputFormat, gFallbackTest_OutputFilePath );
17131 	require_noerr( err, exit );
17132 
17133 exit:
17134 	if( test ) _FallbackTestFree( test );
17135 	CFReleaseNullSafe( plist );
17136 	gExitCode = err ? 1 : ( testPassed ? 0 : 2 );
17137 }
17138 
17139 //===========================================================================================================================
17140 
17141 static void		_FallbackTestStart( void *inContext );
17142 static void		_FallbackTestStop( FallbackTest *inTest, OSStatus inError );
17143 static void DNSSD_API
17144 	_FallbackTestProbeGAICallback(
17145 		DNSServiceRef			inSDRef,
17146 		DNSServiceFlags			inFlags,
17147 		uint32_t				inInterfaceIndex,
17148 		DNSServiceErrorType		inError,
17149 		const char *			inHostname,
17150 		const struct sockaddr *	inSockAddr,
17151 		uint32_t				inTTL,
17152 		void *					inContext );
17153 static void		_FallbackTestProbeTimerHandler( void *inContext );
17154 static void DNSSD_API
17155 	_FallbackTestGetAddrInfoCallback(
17156 		DNSServiceRef			inSDRef,
17157 		DNSServiceFlags			inFlags,
17158 		uint32_t				inInterfaceIndex,
17159 		DNSServiceErrorType		inError,
17160 		const char *			inHostname,
17161 		const struct sockaddr *	inSockAddr,
17162 		uint32_t				inTTL,
17163 		void *					inContext );
17164 static void		_FallbackTestGAITimerHandler( void *inContext );
17165 static OSStatus	_FallbackTestStartSubtest( FallbackTest *inTest );
17166 static void		_FallbackTestForgetSources( FallbackTest *inTest );
17167 
17168 #define kFallbackTestProbeTimeLimitSecs		 5
17169 #define kFallbackTestGAITimeLimitSecs		75
17170 
_FallbackTestCreate(FallbackTest ** outTest)17171 static OSStatus	_FallbackTestCreate( FallbackTest **outTest )
17172 {
17173 	OSStatus			err;
17174 	FallbackTest *		test;
17175 
17176 	test = (FallbackTest *) calloc( 1, sizeof( *test ) );
17177 	require_action( test, exit, err = kNoMemoryErr );
17178 
17179 	test->error		= kInProgressErr;
17180 	test->serverPID	= -1;
17181 
17182 	test->queue = dispatch_queue_create( "com.apple.dnssdutil.fallback-test", DISPATCH_QUEUE_SERIAL );
17183 	require_action( test->queue, exit, err = kNoResourcesErr );
17184 
17185 	test->doneSem = dispatch_semaphore_create( 0 );
17186 	require_action( test->doneSem, exit, err = kNoResourcesErr );
17187 
17188 	*outTest = test;
17189 	test = NULL;
17190 	err = kNoErr;
17191 
17192 exit:
17193 	if( test ) _FallbackTestFree( test );
17194 	return( err );
17195 }
17196 
17197 //===========================================================================================================================
17198 
_FallbackTestRun(FallbackTest * inTest)17199 static OSStatus	_FallbackTestRun( FallbackTest *inTest )
17200 {
17201 	dispatch_async_f( inTest->queue, inTest, _FallbackTestStart );
17202 	dispatch_semaphore_wait( inTest->doneSem, DISPATCH_TIME_FOREVER );
17203 	return( inTest->error );
17204 }
17205 
17206 //===========================================================================================================================
17207 
_FallbackTestFree(FallbackTest * inTest)17208 static void	_FallbackTestFree( FallbackTest *inTest )
17209 {
17210 	size_t		i;
17211 
17212 	check( !inTest->gai );
17213 	check( !inTest->timer );
17214 	check( inTest->serverPID < 0 );
17215 
17216 	ForgetMem( &inTest->serverCmd );
17217 	ForgetMem( &inTest->probeHostname );
17218 	dispatch_forget( &inTest->queue );
17219 	dispatch_forget( &inTest->doneSem );
17220 	for( i = 0; i < kFallbackTestSubtestCount; ++i )
17221 	{
17222 		FallbackSubtest * const		subtest = &inTest->subtests[ i ];
17223 
17224 		ForgetMem( &subtest->hostname );
17225 	}
17226 	free( inTest );
17227 }
17228 
17229 //===========================================================================================================================
17230 
_FallbackTestStart(void * inContext)17231 static void	_FallbackTestStart( void *inContext )
17232 {
17233 	OSStatus					err;
17234 	FallbackTest * const		test = (FallbackTest *) inContext;
17235 	char						tag[ 6 + 1 ];
17236 
17237 	test->startTime = NanoTimeGetCurrent();
17238 
17239 	// The "dnssdutil server" command will create a resolver entry for the server's "d.test." domain containing an array
17240 	// of the server's IP addresses. Because configd favors IPv6 addresses, when there's a mix of IPv4 and IPv6
17241 	// addresses, configd may rearrange the array in order to ensure that IPv6 addresses come before the IPv4 addresses.
17242 	// To preserve the original address order, the server is specified to run in IPv6-only mode. This way,
17243 	// mDNSResponder's view of the address will be such that address with index value 1 is first, address with index
17244 	// value 2 is second, etc.
17245 
17246 	ASPrintF( &test->serverCmd, "dnssdutil server --loopback --follow %lld --ipv6 --extraIPv6 3%s",
17247 		(int64_t) getpid(), test->useRefused ? " --useRefused" : "" );
17248 	require_action_quiet( test->serverCmd, exit, err = kUnknownErr );
17249 
17250 	err = _SpawnCommand( &test->serverPID, "/dev/null", "/dev/null", "%s", test->serverCmd );
17251 	require_noerr( err, exit );
17252 
17253 	ASPrintF( &test->probeHostname, "tag-fallback-test-probe-%s.count-1.ipv4.ttl-900.d.test.",
17254 		_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
17255 	require_action( test->probeHostname, exit, err = kNoMemoryErr );
17256 
17257 	ft_ulog( kLogLevelInfo, "Starting GetAddrInfo request for %s\n", test->probeHostname );
17258 
17259 	err = DNSServiceGetAddrInfo( &test->gai, 0, kDNSServiceInterfaceIndexAny, kDNSServiceProtocol_IPv4,
17260 		test->probeHostname, _FallbackTestProbeGAICallback, test );
17261 	require_noerr( err, exit );
17262 
17263 	err = DNSServiceSetDispatchQueue( test->gai, test->queue );
17264 	require_noerr( err, exit );
17265 
17266 	err = DispatchTimerOneShotCreate( dispatch_time_seconds( kFallbackTestProbeTimeLimitSecs ),
17267 		kFallbackTestProbeTimeLimitSecs * ( UINT64_C_safe( kNanosecondsPerSecond ) / 10 ), test->queue,
17268 		_FallbackTestProbeTimerHandler, test, &test->timer );
17269 	require_noerr( err, exit );
17270 	dispatch_resume( test->timer );
17271 
17272 exit:
17273 	if( err ) _FallbackTestStop( test, err );
17274 }
17275 
17276 //===========================================================================================================================
17277 
_FallbackTestStop(FallbackTest * inTest,OSStatus inError)17278 static void	_FallbackTestStop( FallbackTest *inTest, OSStatus inError )
17279 {
17280 	inTest->error	= inError;
17281 	inTest->endTime	= NanoTimeGetCurrent();
17282 	_FallbackTestForgetSources( inTest );
17283 	if( inTest->serverPID >= 0 )
17284 	{
17285 		OSStatus		err;
17286 
17287 		err = kill( inTest->serverPID, SIGTERM );
17288 		err = map_global_noerr_errno( err );
17289 		check_noerr( err );
17290 		inTest->serverPID = -1;
17291 	}
17292 	dispatch_semaphore_signal( inTest->doneSem );
17293 }
17294 
17295 //===========================================================================================================================
17296 
17297 static void DNSSD_API
_FallbackTestProbeGAICallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inHostname,const struct sockaddr * inSockAddr,uint32_t inTTL,void * inContext)17298 	_FallbackTestProbeGAICallback(
17299 		DNSServiceRef			inSDRef,
17300 		DNSServiceFlags			inFlags,
17301 		uint32_t				inInterfaceIndex,
17302 		DNSServiceErrorType		inError,
17303 		const char *			inHostname,
17304 		const struct sockaddr *	inSockAddr,
17305 		uint32_t				inTTL,
17306 		void *					inContext )
17307 {
17308 	OSStatus					err;
17309 	FallbackTest * const		test = (FallbackTest *) inContext;
17310 
17311 	Unused( inSDRef );
17312 	Unused( inInterfaceIndex );
17313 	Unused( inHostname );
17314 	Unused( inTTL );
17315 
17316 	if( ( inFlags & kDNSServiceFlagsAdd ) && !inError )
17317 	{
17318 		_FallbackTestForgetSources( test );
17319 
17320 		ft_ulog( kLogLevelInfo, "Probe: Got GAI address %##a for %s\n", inSockAddr, test->probeHostname );
17321 
17322 		check( test->subtestIndex == 0 );
17323 		err = _FallbackTestStartSubtest( test );
17324 		require_noerr( err, exit );
17325 	}
17326 	err = kNoErr;
17327 
17328 exit:
17329 	if( err ) _FallbackTestStop( test, err );
17330 }
17331 
17332 //===========================================================================================================================
17333 
_FallbackTestProbeTimerHandler(void * inContext)17334 static void	_FallbackTestProbeTimerHandler( void *inContext )
17335 {
17336 	FallbackTest * const		test = (FallbackTest *) inContext;
17337 
17338 	ft_ulog( kLogLevelInfo, "GetAddrInfo probe request for \"%s\" timed out.\n", test->probeHostname );
17339 	_FallbackTestStop( test, kNotPreparedErr );
17340 }
17341 
17342 //===========================================================================================================================
17343 
17344 static void DNSSD_API
_FallbackTestGetAddrInfoCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inHostname,const struct sockaddr * inSockAddr,uint32_t inTTL,void * inContext)17345 	_FallbackTestGetAddrInfoCallback(
17346 		DNSServiceRef			inSDRef,
17347 		DNSServiceFlags			inFlags,
17348 		uint32_t				inInterfaceIndex,
17349 		DNSServiceErrorType		inError,
17350 		const char *			inHostname,
17351 		const struct sockaddr *	inSockAddr,
17352 		uint32_t				inTTL,
17353 		void *					inContext )
17354 {
17355 	OSStatus					err;
17356 	struct sockaddr_in			sin;
17357 	FallbackTest * const		test		= (FallbackTest *) inContext;
17358 	FallbackSubtest *const		subtest		= &test->subtests[ test->subtestIndex ];
17359 	Boolean						complete	= false;
17360 
17361 	Unused( inSDRef );
17362 	Unused( inInterfaceIndex );
17363 	Unused( inTTL );
17364 
17365 	_FallbackTestForgetSources( test );
17366 
17367 	if( strcasecmp( inHostname, subtest->hostname ) != 0 )
17368 	{
17369 		ft_ulog( kLogLevelError, "GetAddrInfo(%s) result: Got unexpected hostname \"%s\".\n",
17370 			subtest->hostname, inHostname );
17371 		err = kUnexpectedErr;
17372 		goto done;
17373 	}
17374 	if( inError )
17375 	{
17376 		ft_ulog( kLogLevelError, "GetAddrInfo(%s) result: Got unexpected error %#m.\n", subtest->hostname, inError );
17377 		err = inError;
17378 		goto done;
17379 	}
17380 	if( ( inFlags & kDNSServiceFlagsAdd ) == 0 )
17381 	{
17382 		ft_ulog( kLogLevelError, "GetAddrInfo(%s) result: Missing Add flag.\n", subtest->hostname );
17383 		err = kUnexpectedErr;
17384 		goto done;
17385 	}
17386 	_SockAddrInitIPv4( &sin, kDNSServerBaseAddrV4 + 1, 0 );
17387 	if( SockAddrCompareAddr( inSockAddr, &sin ) != 0 )
17388 	{
17389 		ft_ulog( kLogLevelError, "GetAddrInfo(%s) result: Got unexpected address %##a (expected %##a).\n",
17390 			subtest->hostname, inSockAddr, &sin );
17391 		err = kUnexpectedErr;
17392 		goto done;
17393 	}
17394 	ft_ulog( kLogLevelInfo, "Subtest %zu/%d: Got expected GAI address %##a for %s\n",
17395 		test->subtestIndex + 1, kFallbackTestSubtestCount, inSockAddr, subtest->hostname );
17396 	err = kNoErr;
17397 
17398 done:
17399 	subtest->endTime	= NanoTimeGetCurrent();
17400 	subtest->error		= err;
17401 	err = kNoErr;
17402 	if( ++test->subtestIndex < kFallbackTestSubtestCount )
17403 	{
17404 		err = _FallbackTestStartSubtest( test );
17405 		require_noerr( err, exit );
17406 	}
17407 	else
17408 	{
17409 		complete = true;
17410 	}
17411 
17412 exit:
17413 	if( err || complete ) _FallbackTestStop( test, err );
17414 }
17415 
17416 //===========================================================================================================================
17417 
_FallbackTestGAITimerHandler(void * inContext)17418 static void	_FallbackTestGAITimerHandler( void *inContext )
17419 {
17420 	OSStatus					err;
17421 	FallbackTest * const		test		= (FallbackTest *) inContext;
17422 	FallbackSubtest * const		subtest		= &test->subtests[ test->subtestIndex ];
17423 	Boolean						complete	= false;
17424 
17425 	_FallbackTestForgetSources( test );
17426 
17427 	ft_ulog( kLogLevelInfo, "GetAddrInfo request for \"%s\" timed out.\n", subtest->hostname );
17428 
17429 	subtest->endTime	= NanoTimeGetCurrent();
17430 	subtest->error		= kTimeoutErr;
17431 	if( ++test->subtestIndex < kFallbackTestSubtestCount )
17432 	{
17433 		err = _FallbackTestStartSubtest( test );
17434 		require_noerr( err, exit );
17435 	}
17436 	else
17437 	{
17438 		complete = true;
17439 		err = kNoErr;
17440 	}
17441 
17442 exit:
17443 	if( err || complete ) _FallbackTestStop( test, err );
17444 }
17445 
17446 //===========================================================================================================================
17447 
_FallbackTestStartSubtest(FallbackTest * inTest)17448 static OSStatus	_FallbackTestStartSubtest( FallbackTest *inTest )
17449 {
17450 	OSStatus					err;
17451 	FallbackSubtest * const		subtest = &inTest->subtests[ inTest->subtestIndex ];
17452 	char						tag[ 6 + 1 ];
17453 
17454 	subtest->error		= kInProgressErr;
17455 	subtest->startTime	= NanoTimeGetCurrent();
17456 
17457 	ForgetMem( &subtest->hostname );
17458 	ASPrintF( &subtest->hostname, "index-%u.tag-fallback-test-%s.count-1.ipv4.ttl-900.d.test.",
17459 		kFallbackSubtestParams[ inTest->subtestIndex ].serverIndex,
17460 		_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
17461 	require_action( subtest->hostname, exit, err = kNoMemoryErr );
17462 
17463 	ft_ulog( kLogLevelInfo, "Starting GetAddrInfo request for %s\n", subtest->hostname );
17464 
17465 	check( !inTest->gai );
17466 	err = DNSServiceGetAddrInfo( &inTest->gai, 0, kDNSServiceInterfaceIndexAny, kDNSServiceProtocol_IPv4,
17467 		subtest->hostname, _FallbackTestGetAddrInfoCallback, inTest );
17468 	require_noerr( err, exit );
17469 
17470 	err = DNSServiceSetDispatchQueue( inTest->gai, inTest->queue );
17471 	require_noerr( err, exit );
17472 
17473 	check( !inTest->timer );
17474 	err = DispatchTimerOneShotCreate( dispatch_time_seconds( kFallbackTestGAITimeLimitSecs ),
17475 		kFallbackTestGAITimeLimitSecs * ( UINT64_C_safe( kNanosecondsPerSecond ) / 10 ), inTest->queue,
17476 		_FallbackTestGAITimerHandler, inTest, &inTest->timer );
17477 	require_noerr( err, exit );
17478 	dispatch_resume( inTest->timer );
17479 
17480 exit:
17481 	return( err );
17482 }
17483 
17484 //===========================================================================================================================
17485 
_FallbackTestForgetSources(FallbackTest * inTest)17486 static void	_FallbackTestForgetSources( FallbackTest *inTest )
17487 {
17488 	DNSServiceForget( &inTest->gai );
17489 	dispatch_source_forget( &inTest->timer );
17490 }
17491 
17492 //===========================================================================================================================
17493 //    ExpensiveConstrainedsTestCmd
17494 //===========================================================================================================================
17495 
17496 #define NOTIFICATION_TIME_THRESHOLD 1500    // The maximum wating time allowed before notification happens
17497 #define TEST_REPETITION             2       // the number of repetition that one test has to passed
17498 #define LOOPBACK_INTERFACE_NAME     "lo0"
17499 #define WIFI_TEST_QUESTION_NAME     "www.example.com"
17500 #define EXPENSIVE_CONSTRAINED_MAX_RETRIES 1
17501 #define EXPENSIVE_CONSTRAINED_TEST_INTERVAL 5
17502 // Use "-n tag-expensive-test.ttl-86400.d.test." to run the test locally
17503 // #define LOOPBACK_TEST_QUESTION_NAME "tag-expensive-test.ttl-86400.d.test."
17504 
17505 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME                CFSTR( "Start Time" )
17506 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME                  CFSTR( "End Time" )
17507 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED                CFSTR( "All Tests Passed" )
17508 #define EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT            CFSTR( "Subtest Results" )
17509 
17510 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME             CFSTR( "Start Time" )
17511 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME               CFSTR( "End Time" )
17512 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME                  CFSTR( "Question Name" )
17513 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS                  CFSTR( "DNS Service Flags" )
17514 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS              CFSTR( "Protocols" )
17515 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX        CFSTR( "Interface Index" )
17516 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME         CFSTR( "Interface Name" )
17517 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT                 CFSTR( "Result" )
17518 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR                  CFSTR( "Error Description" )
17519 #define EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS          CFSTR( "Test Progress" )
17520 
17521 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME           CFSTR( "Start Time" )
17522 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME             CFSTR( "End Time" )
17523 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE                CFSTR( "State" )
17524 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT        CFSTR( "Expected Result" )
17525 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT        CFSTR( "Actual Result" )
17526 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW   CFSTR( "Expensive Prev->Now" )
17527 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW CFSTR( "Constrained Prev->Now" )
17528 #define EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK            CFSTR( "Call Back" )
17529 
17530 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP       CFSTR( "Timestamp" )
17531 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME            CFSTR( "Answer Name" )
17532 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS           CFSTR( "Add or Remove" )
17533 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE       CFSTR( "Interface Index" )
17534 #define EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS         CFSTR( "Address" )
17535 
17536 // All the states that ends with _PREPARE represents the state where the test state is reset and initialized.
17537 enum ExpensiveConstrainedTestState
17538 {
17539     TEST_BEGIN,
17540     TEST_EXPENSIVE_PREPARE,
17541     TEST_EXPENSIVE,                     // Test if mDNSResponder can handle "expensive" status change of the corresponding interface
17542     TEST_CONSTRAINED_PREPARE,
17543     TEST_CONSTRAINED,                   // Test if mDNSResponder can handle "constrained" status change of the corresponding interface
17544     TEST_EXPENSIVE_CONSTRAINED_PREPARE,
17545     TEST_EXPENSIVE_CONSTRAINED,          // Test if mDNSResponder can handle "expensive" and "constrained" status change of the corresponding interface at the same time
17546     TEST_FAILED,
17547     TEST_SUCCEEDED
17548 };
17549 enum ExpensiveConstrainedTestOperation
17550 {
17551     RESULT_ADD, // received response for the given query, which means mDNSResponder is able to send out the query over the interface, because the interface status is changed.
17552     RESULT_RMV, // received negative response for the given query, which means mDNSResponder is not able to send out the query over the interface, because the interface status is changed.
17553     NO_UPDATE   // no status update notification
17554 };
17555 
17556 typedef struct
17557 {
17558     uint32_t                    subtestIndex;           // The index of parameter for the subtest
17559     DNSServiceRef               opRef;                  // sdRef for the DNSServiceGetAddrInfo operation.
17560     const char *                name;                   // Hostname to resolve.
17561     DNSServiceFlags             flags;                  // Flags argument for DNSServiceGetAddrInfo().
17562     DNSServiceProtocol          protocols;              // Protocols argument for DNSServiceGetAddrInfo().
17563     uint32_t                    ifIndex;                // Interface index argument for DNSServiceGetAddrInfo().
17564     char                        ifName[IFNAMSIZ];       // Interface name for the given interface index.
17565     dispatch_source_t           timer;                  // The test will check if the current behavior is valid, which is called by
17566                                                         // the timer per 2s.
17567     pid_t                       serverPID;
17568     Boolean                     isExpensivePrev;        // If the interface is expensive in the previous test step.
17569     Boolean                     isExpensiveNow;         // If the interface is expensive now.
17570     Boolean                     isConstrainedPrev;      // If the interface is constrained in the previous test step.
17571     Boolean                     isConstrainedNow;       // If the interface is constrained now.
17572     Boolean                     startFromExpensive;     // All the test will start from expensive/constrained interface, so there won's be an answer until the interface is changed.
17573     uint8_t                     numOfRetries;           // the number of retries we can have if the test fail
17574     struct timeval              updateTime;             // The time when interface status(expensive or constrained) is changed.
17575     struct timeval              notificationTime;       // The time when callback function, which is passed to DNSServiceGetAddrInfo, gets called.
17576     uint32_t                    counter;                // To record how many times the test has repeated.
17577     enum ExpensiveConstrainedTestState          state;              // The current test state.
17578     enum ExpensiveConstrainedTestOperation      expectedOperation;  // the test expects this kind of notification
17579     enum ExpensiveConstrainedTestOperation      operation;          // represents what notification the callback function gets.
17580 
17581     NanoTime64                  testReport_startTime;       // when the entire test starts
17582     CFMutableArrayRef           subtestReport;              // stores the log message for every subtest
17583     NanoTime64                  subtestReport_startTime;    // when the subtest starts
17584     CFMutableArrayRef           subtestProgress;            // one test iteration
17585     NanoTime64                  subtestProgress_startTime;  // when the test iteration starts
17586     CFMutableArrayRef           subtestProgress_callBack;   // array of ADD/REMOVE events
17587     char *                      outputFilePath;             // File to write test results to. If NULL, then write to stdout. (malloced)
17588     OutputFormatType            outputFormat;               // Format of test report output.
17589 } ExpensiveConstrainedContext;
17590 
17591 // structure that controls how the subtest is run
17592 typedef struct
17593 {
17594     const char  *qname;                 // the name of the query, when the ends with ".d.test.", test will send query to local DNS server
17595     Boolean     deny_expensive;         // if the query should avoid using expensive interface
17596     Boolean     deny_constrained;       // if the query should avoid using constrained interface
17597     Boolean     start_from_expensive;   // if the query should starts from using an expensive interface
17598     Boolean     ipv4_query;             // only allow IPv4 query
17599     Boolean     ipv6_query;             // only allow IPv6 query
17600     int8_t      test_passed;            // if the subtest passes
17601 } ExpensiveConstrainedTestParams;
17602 
17603 static ExpensiveConstrainedTestParams   ExpensiveConstrainedSubtestParams[] =
17604 {
17605 //  qname                                                   deny_expensive  deny_constrained    start_from_expensive    ipv4_query  ipv6_query
17606     {"tag-expensive_constrained-test.ttl-86400.d.test.",    true,           false,              false,                  true,       true,       -1},
17607     {"tag-expensive_constrained-test.ttl-86400.d.test.",    true,           false,              true,                   true,       true,       -1},
17608     {"tag-expensive_constrained-test.ttl-86400.d.test.",    false,          true,               false,                  true,       true,       -1},
17609     {"tag-expensive_constrained-test.ttl-86400.d.test.",    false,          true,               true,                   true,       true,       -1},
17610     {"tag-expensive_constrained-test.ttl-86400.d.test.",    true,           true,               false,                  true,       true,       -1},
17611     {"tag-expensive_constrained-test.ttl-86400.d.test.",    true,           true,               true,                   true,       true,       -1},
17612 // IPv4 Only
17613     {"tag-expensive_constrained-test.ttl-86400.d.test.",    true,           false,              false,                  true,       false,      -1},
17614     {"tag-expensive_constrained-test.ttl-86400.d.test.",    true,           false,              true,                   true,       false,      -1},
17615     {"tag-expensive_constrained-test.ttl-86400.d.test.",    false,          true,               false,                  true,       false,      -1},
17616     {"tag-expensive_constrained-test.ttl-86400.d.test.",    false,          true,               true,                   true,       false,      -1},
17617     {"tag-expensive_constrained-test.ttl-86400.d.test.",    true,           true,               false,                  true,       false,      -1},
17618     {"tag-expensive_constrained-test.ttl-86400.d.test.",    true,           true,               true,                   true,       false,      -1},
17619 // IPv6 Only
17620     {"tag-expensive_constrained-test.ttl-86400.d.test.",    true,           false,              false,                  false,      true,       -1},
17621     {"tag-expensive_constrained-test.ttl-86400.d.test.",    true,           false,              true,                   false,      true,       -1},
17622     {"tag-expensive_constrained-test.ttl-86400.d.test.",    false,          true,               false,                  false,      true,       -1},
17623     {"tag-expensive_constrained-test.ttl-86400.d.test.",    false,          true,               true,                   false,      true,       -1},
17624     {"tag-expensive_constrained-test.ttl-86400.d.test.",    true,           true,               false,                  false,      true,       -1},
17625     {"tag-expensive_constrained-test.ttl-86400.d.test.",    true,           true,               true,                   false,      true,       -1}
17626 };
17627 
17628 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext *context );
17629 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext *context );
17630 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext *context );
17631 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext *context, uint32_t second );
17632 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext *context );
17633 static void DNSSD_API
17634     ExpensiveConstrainedCallback(
17635         DNSServiceRef           inSDRef,
17636         DNSServiceFlags         inFlags,
17637         uint32_t                inInterfaceIndex,
17638         DNSServiceErrorType     inError,
17639         const char *            inHostname,
17640         const struct sockaddr * inSockAddr,
17641         uint32_t                inTTL,
17642         void *                  inContext );
17643 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext *context );
17644 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext *context );
17645 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext *context );
17646 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext *context, const char *error_description );
17647 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext *context, Boolean allPassed );
17648 static const char *ExpensiveConstrainedProtocolString(DNSServiceProtocol protocol);
17649 static const char *ExpensiveConstrainedStateString(enum ExpensiveConstrainedTestState state);
17650 static const char *ExpensiveConstrainedOperationString(enum ExpensiveConstrainedTestOperation operation);
17651 static Boolean expensiveConstrainedEndsWith( const char *str, const char *suffix );
17652 
17653 //===========================================================================================================================
17654 //    ExpensiveConstrainedTestCmd
17655 //===========================================================================================================================
17656 
ExpensiveConstrainedTestCmd(void)17657 static void ExpensiveConstrainedTestCmd( void )
17658 {
17659     OSStatus                        err;
17660     dispatch_source_t               signalSource   = NULL;
17661     ExpensiveConstrainedContext *   context        = NULL;
17662 
17663     // Set up SIGINT handler.
17664     signal( SIGINT, SIG_IGN );
17665     err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
17666     require_noerr( err, exit );
17667     dispatch_resume( signalSource );
17668 
17669     // create the test context
17670     context = (ExpensiveConstrainedContext *) calloc( 1, sizeof(*context) );
17671     require_action( context, exit, err = kNoMemoryErr );
17672 
17673     // get the command line option
17674     err = OutputFormatFromArgString( gExpensiveConstrainedTest_OutputFormat, &context->outputFormat );
17675     require_noerr_quiet( err, exit );
17676     if ( gExpensiveConstrainedTest_OutputFilePath )
17677     {
17678         context->outputFilePath = strdup( gExpensiveConstrainedTest_OutputFilePath );
17679         require_noerr_quiet( context->outputFilePath, exit );
17680     }
17681 
17682     // initialize context
17683     context->subtestIndex = 0;
17684     context->numOfRetries = EXPENSIVE_CONSTRAINED_MAX_RETRIES;
17685 
17686     // initialize the CFArray used to store the log
17687     context->subtestReport = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
17688     context->testReport_startTime = NanoTimeGetCurrent();
17689 
17690     // setup local DNS server
17691     ExpensiveConstrainedSetupLocalDNSServer( context );
17692 
17693     ExpensiveConstrainedStartTestHandler( context );
17694 
17695     dispatch_main();
17696 
17697 exit:
17698     exit( 1 );
17699 }
17700 
17701 //===========================================================================================================================
17702 //    ExpensiveConstrainedSetupLocalDNSServer
17703 //===========================================================================================================================
17704 
ExpensiveConstrainedSetupLocalDNSServer(ExpensiveConstrainedContext * context)17705 static void ExpensiveConstrainedSetupLocalDNSServer( ExpensiveConstrainedContext *context )
17706 {
17707     pid_t current_pid = getpid();
17708     OSStatus err = _SpawnCommand( &context->serverPID, NULL, NULL, "dnssdutil server -l --port 0 --follow %d", current_pid );
17709     if (err != 0)
17710     {
17711         FPrintF( stdout, "dnssdutil server -l --port 0 --follow <PID> failed, error: %d\n", err );
17712         exit( 1 );
17713     }
17714     sleep(2);
17715 }
17716 
17717 //===========================================================================================================================
17718 //    ExpensiveConstrainedStartTestHandler
17719 //===========================================================================================================================
17720 
ExpensiveConstrainedStartTestHandler(ExpensiveConstrainedContext * context)17721 static void ExpensiveConstrainedStartTestHandler( ExpensiveConstrainedContext *context )
17722 {
17723     // setup 3s timer
17724     ExpensiveConstrainedSetupTimer( context, EXPENSIVE_CONSTRAINED_TEST_INTERVAL );
17725 
17726     // set the event handler for the 3s timer
17727     dispatch_source_set_event_handler( context->timer, ^{
17728         ExpensiveConstrainedTestTimerEventHandler( context );
17729     } );
17730 
17731     dispatch_resume( context->timer );
17732 }
17733 
17734 //===========================================================================================================================
17735 //    ExpensiveConstrainedStartTestHandler
17736 //===========================================================================================================================
17737 
ExpensiveConstrainedStopTestHandler(ExpensiveConstrainedContext * context)17738 static void ExpensiveConstrainedStopTestHandler( ExpensiveConstrainedContext *context )
17739 {
17740     dispatch_cancel( context->timer );
17741     dispatch_release( context->timer );
17742     context->timer = NULL;
17743 }
17744 
17745 //===========================================================================================================================
17746 //    ExpensiveConstrainedSetupTimer
17747 //===========================================================================================================================
17748 
ExpensiveConstrainedSetupTimer(ExpensiveConstrainedContext * context,uint32_t second)17749 static void ExpensiveConstrainedSetupTimer( ExpensiveConstrainedContext *context, uint32_t second )
17750 {
17751     // set the timer source, the event handler will be called for every "second" seconds
17752     context->timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue() );
17753     if ( context->timer == NULL )
17754     {
17755         FPrintF( stdout, "dispatch_source_create:DISPATCH_SOURCE_TYPE_TIMER failed\n" );
17756         exit( 1 );
17757     }
17758     // the first block will be put into the queue "second"s after calling dispatch_resume
17759     dispatch_source_set_timer( context->timer, dispatch_time( DISPATCH_TIME_NOW, second * NSEC_PER_SEC ),
17760                                (unsigned long long)(second) * NSEC_PER_SEC, 100ull * NSEC_PER_MSEC );
17761 }
17762 
17763 //===========================================================================================================================
17764 //    ExpensiveConstrainedTestTimerEventHandler
17765 //===========================================================================================================================
17766 
ExpensiveConstrainedTestTimerEventHandler(ExpensiveConstrainedContext * context)17767 static void ExpensiveConstrainedTestTimerEventHandler( ExpensiveConstrainedContext *context )
17768 {
17769     OSStatus    err;
17770     char        buffer[ 1024 ];
17771     const char *errorDescription = NULL;
17772 
17773     // do not log the state if we are in transition state
17774     if (context->state != TEST_BEGIN
17775         && context->state != TEST_SUCCEEDED
17776         && context->state != TEST_CONSTRAINED_PREPARE
17777         && context->state != TEST_EXPENSIVE_CONSTRAINED_PREPARE)
17778         ExpensiveConstrainedSubtestProgressReport( context );
17779 
17780     switch ( context->state ) {
17781         case TEST_BEGIN:
17782         {
17783             ExpensiveConstrainedStopTestHandler( context );
17784 
17785             // clear mDNSResponder cache
17786             err = systemf( NULL, "killall -HUP mDNSResponder" );
17787             require_noerr_action( err, test_failed, errorDescription = "systemf failed");
17788 
17789             // initialize the global parameters
17790             ExpensiveConstrainedInitializeContext( context );
17791 
17792             // The local DNS server is set up on the local only interface.
17793             gExpensiveConstrainedTest_Interface = LOOPBACK_INTERFACE_NAME;
17794             strncpy( context->ifName, gExpensiveConstrainedTest_Interface, sizeof( context->ifName ) );
17795 
17796             // The local DNS server is unscoped, so we must set our question to unscoped.
17797             context->ifIndex = kDNSServiceInterfaceIndexAny;
17798 
17799             // The question name must end with "d.test.", "tag-expensive-test.ttl-86400.d.test." for example, then the test will
17800             // use the local dns server set up previously to run the test locally.
17801             require_action( gExpensiveConstrainedTest_Name != NULL && expensiveConstrainedEndsWith( gExpensiveConstrainedTest_Name, "d.test." ), test_failed,
17802                            SNPrintF( buffer, sizeof( buffer ), "The question name (%s) must end with \"d.test.\".\n", gExpensiveConstrainedTest_Name );
17803                            errorDescription = buffer );
17804 
17805             // get the quesion name
17806             context->name = gExpensiveConstrainedTest_Name;
17807 
17808             // set the initial state for the interface
17809             context->startFromExpensive = gExpensiveConstrainedTest_StartFromExpensive;
17810             err = systemf( NULL, "ifconfig %s %sexpensive && ifconfig %s -constrained", context->ifName, context->startFromExpensive ? "" : "-", context->ifName );
17811             require_noerr_action( err, test_failed, errorDescription = "systemf failed");
17812             sleep( 5 ); // wait for 5s to allow the interface change event de delivered to others
17813 
17814             // get question flag
17815             if ( gExpensiveConstrainedTest_DenyExpensive )    context->flags     |= kDNSServiceFlagsDenyExpensive;
17816             if ( gExpensiveConstrainedTest_DenyConstrained )  context->flags     |= kDNSServiceFlagsDenyConstrained;
17817             if ( gExpensiveConstrainedTest_ProtocolIPv4 )     context->protocols |= kDNSServiceProtocol_IPv4;
17818             if ( gExpensiveConstrainedTest_ProtocolIPv6 )     context->protocols |= kDNSServiceProtocol_IPv6;
17819 
17820             // prevent mDNSResponder from doing extra path evaluation and changing the interface to others(such as Bluetooth)
17821             #if( TARGET_OS_WATCH )
17822             context->flags |= kDNSServiceFlagsPathEvaluationDone;
17823             #endif
17824 
17825             // start the query
17826             DNSServiceGetAddrInfo( &context->opRef, context->flags, context->ifIndex, context->protocols, context->name, ExpensiveConstrainedCallback, context );
17827 
17828             // set the initial test status
17829             context->subtestReport_startTime    = NanoTimeGetCurrent();
17830             context->subtestProgress_startTime  = NanoTimeGetCurrent();
17831             context->state                      = TEST_EXPENSIVE_PREPARE; // start from expensive test
17832             context->isExpensiveNow             = context->startFromExpensive ? true : false;
17833             context->isConstrainedNow           = false;
17834             context->expectedOperation          = context->isExpensiveNow && ( context->flags & kDNSServiceFlagsDenyExpensive ) ? NO_UPDATE : RESULT_ADD;
17835             context->operation                  = NO_UPDATE;
17836             context->subtestProgress            = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks);
17837             require_action( context->subtestProgress != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
17838             context->subtestProgress_callBack   = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks);
17839             require_action( context->subtestProgress != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
17840 
17841             // set the queue where the callback will be called when there is an answer for the query
17842             err = DNSServiceSetDispatchQueue( context->opRef, dispatch_get_main_queue() );
17843             require_noerr( err, test_failed );
17844 
17845             ExpensiveConstrainedStartTestHandler( context );
17846         }
17847             break;
17848         case TEST_EXPENSIVE_PREPARE:
17849             require_action( context->isConstrainedNow == false, test_failed,
17850                             SNPrintF( buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
17851                             errorDescription = buffer );
17852             require_action( context->expectedOperation == context->operation, test_failed,
17853                             errorDescription = "Operation is not expected" );
17854 
17855             context->subtestProgress_startTime  = NanoTimeGetCurrent();
17856             context->state                      = TEST_EXPENSIVE;   // begin to test expensive flag
17857             context->counter                    = 0;                // the number of test repetition that has passed
17858             context->isExpensivePrev            = context->isExpensiveNow;
17859             context->isExpensiveNow             = !context->isExpensiveNow; // flip the expensive status
17860             context->isConstrainedPrev          = false;            // the interface is currently unconstrained
17861             context->isConstrainedNow           = false;            // the interface will be unconstrained in the current test
17862             if ( gExpensiveConstrainedTest_DenyExpensive )
17863                 context->expectedOperation      = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
17864             else
17865                 context->expectedOperation      = NO_UPDATE;
17866             context->operation                  = NO_UPDATE;        // NO_UPDATE means the call back function has not been called
17867             context->subtestProgress_callBack   = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
17868             require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
17869 
17870             err = systemf( NULL, "ifconfig %s %sexpensive", context->ifName, context->isExpensiveNow ? "" : "-" );
17871             require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
17872 
17873             // record the starting timestamp
17874             gettimeofday( &context->updateTime, NULL );
17875 
17876             break;
17877         case TEST_EXPENSIVE:
17878             // Since we are testing expensive flag, we should always turn the expensive flag on and off.
17879             require_action( context->isExpensivePrev ^ context->isExpensiveNow, test_failed,
17880                             SNPrintF( buffer, sizeof( buffer ), "The current expensive status should be different with the previous one: %d -> %d\n", context->isExpensivePrev, context->isExpensiveNow);
17881                             errorDescription = buffer );
17882             // constrained flag is always turned off when testing expensive
17883             require_action( context->isConstrainedNow == false, test_failed,
17884                             SNPrintF( buffer, sizeof( buffer ), "The interface %s should be unconstrained when testing \"expensive\"\n", context->ifName );
17885                             errorDescription = buffer );
17886             require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected" );
17887 
17888             context->counter++; // one test repetition has passed
17889             if ( context->counter == TEST_REPETITION ) // expensive test finished
17890             {
17891                 // prepare to test constrained flag
17892                 context->state = TEST_CONSTRAINED_PREPARE;
17893 
17894                 // reset the interface
17895                 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s -constrained", context->ifName, context->ifName );
17896                 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
17897 
17898                 context->isExpensiveNow = false;
17899                 context->isConstrainedNow = false;
17900                 gettimeofday( &context->updateTime, NULL );
17901             }
17902             else
17903             {
17904                 context->subtestProgress_startTime  = NanoTimeGetCurrent();
17905                 context->isExpensivePrev            = context->isExpensiveNow;
17906                 context->isExpensiveNow             = !context->isExpensiveNow; // flip the expensive status
17907                 if ( gExpensiveConstrainedTest_DenyExpensive )
17908                     context->expectedOperation      = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
17909                 else
17910                     context->expectedOperation      = NO_UPDATE;
17911                 context->operation                  = NO_UPDATE;
17912                 context->subtestProgress_callBack   = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
17913                 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
17914 
17915                 err = systemf( NULL, "ifconfig %s %sexpensive", context->ifName, context->isExpensiveNow ? "" : "-" );
17916                 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
17917 
17918                 gettimeofday( &context->updateTime, NULL );
17919             }
17920             break;
17921         case TEST_CONSTRAINED_PREPARE:
17922             // The interface should be inexpensive and unconstrained when the constrained test starts
17923             require_action( context->isExpensiveNow == false, test_failed, SNPrintF( buffer, sizeof( buffer ), "Interface %s should be inexpensive.", context->ifName );
17924                             errorDescription = buffer );
17925             require_action( context->isConstrainedNow == false, test_failed, SNPrintF( buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
17926                             errorDescription = buffer );
17927 
17928             context->subtestProgress_startTime  = NanoTimeGetCurrent();
17929             context->state                      = TEST_CONSTRAINED; // constrained interface is now under testing
17930             context->counter                    = 0;
17931             context->isExpensivePrev            = false;
17932             context->isExpensiveNow             = false;
17933             context->isConstrainedPrev          = false;
17934             context->isConstrainedNow           = true;             // will set constrained flag on the interface
17935             if ( gExpensiveConstrainedTest_DenyConstrained )
17936                 context->expectedOperation      = RESULT_RMV;
17937             else
17938                 context->expectedOperation      = NO_UPDATE;
17939             context->operation                  = NO_UPDATE;
17940             context->subtestProgress_callBack   = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
17941             require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
17942 
17943             // change interface to the constrained one
17944             err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s constrained", context->ifName, context->ifName );
17945             require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
17946 
17947             gettimeofday( &context->updateTime, NULL );
17948             break;
17949         case TEST_CONSTRAINED:
17950             // Since we are testing constrained flag, we should always turn the constrained flag on and off.
17951             require_action( context->isConstrainedPrev ^ context->isConstrainedNow, test_failed,
17952                             SNPrintF( buffer, sizeof( buffer ), "The current constrained status should be different with the previous one: %d -> %d\n", context->isConstrainedPrev, context->isConstrainedNow );
17953                             errorDescription = buffer );
17954             require_action( context->isExpensiveNow == false, test_failed,
17955                             SNPrintF( buffer, sizeof( buffer ), "The interface %s should be inexpensive when testing \"constrained\"\n", context->ifName );
17956                             errorDescription = buffer );
17957             require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected");
17958 
17959             context->counter++;
17960             if (context->counter == TEST_REPETITION)
17961             {
17962                 // test changing expensive and constrained flags at the same time
17963                 context->state = TEST_EXPENSIVE_CONSTRAINED_PREPARE;
17964 
17965                 // reset interface
17966                 err = systemf( NULL, "ifconfig %s -expensive && ifconfig %s -constrained", context->ifName, context->ifName );
17967                 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
17968 
17969                 context->isExpensiveNow = false;
17970                 context->isConstrainedNow = false;
17971                 gettimeofday( &context->updateTime, NULL );
17972             }
17973             else
17974             {
17975                 context->subtestProgress_startTime  = NanoTimeGetCurrent();
17976                 context->isConstrainedPrev          = context->isConstrainedNow;
17977                 context->isConstrainedNow           = !context->isConstrainedNow; // flip constrained flag
17978                 if ( gExpensiveConstrainedTest_DenyConstrained )
17979                     context->expectedOperation      = context->isConstrainedNow ? RESULT_RMV : RESULT_ADD;
17980                 else
17981                     context->expectedOperation      = NO_UPDATE;
17982                 context->operation                  = NO_UPDATE;
17983                 context->subtestProgress_callBack   = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
17984                 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
17985 
17986                 err = systemf( NULL, "ifconfig %s %sconstrained", context->ifName, context->isConstrainedNow ? "" : "-" );
17987                 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
17988 
17989                 gettimeofday(&context->updateTime, NULL);
17990             }
17991             break;
17992         case TEST_EXPENSIVE_CONSTRAINED_PREPARE:
17993             // The interface should be inexpensive and unconstrained when the constrained test starts
17994             require_action( context->isExpensiveNow == false,   test_failed,
17995                             SNPrintF( buffer, sizeof( buffer ), "Interface %s should be inexpensive.\n", context->ifName );
17996                             errorDescription = buffer );
17997             require_action( context->isConstrainedNow == false, test_failed,
17998                             SNPrintF(buffer, sizeof( buffer ), "Interface %s should be unconstrained.\n", context->ifName );
17999                             errorDescription = buffer );
18000 
18001             // now flip expensive and constrained at the same time
18002             context->subtestProgress_startTime  = NanoTimeGetCurrent();
18003             context->state                      = TEST_EXPENSIVE_CONSTRAINED;
18004             context->counter                    = 0;
18005             context->isExpensivePrev            = false;
18006             context->isExpensiveNow             = true;
18007             context->isConstrainedPrev          = false;
18008             context->isConstrainedNow           = true;
18009             if (gExpensiveConstrainedTest_DenyConstrained || gExpensiveConstrainedTest_DenyExpensive)
18010                 context->expectedOperation      = RESULT_RMV;
18011             else
18012                 context->expectedOperation      = NO_UPDATE;
18013             context->operation                  = NO_UPDATE;
18014             context->subtestProgress_callBack   = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
18015             require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
18016 
18017             err = systemf(NULL, "ifconfig %s expensive && ifconfig %s constrained", context->ifName, context->ifName );
18018             require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
18019 
18020             gettimeofday( &context->updateTime, NULL );
18021             break;
18022         case TEST_EXPENSIVE_CONSTRAINED:
18023             // expensive and constrained flag should always be changed
18024             require_action( ( context->isExpensivePrev ^ context->isExpensiveNow ) && ( context->isConstrainedPrev ^ context->isConstrainedNow ), test_failed,
18025                             SNPrintF( buffer, sizeof( buffer ), "Both expensive and constrained status need to be changed" );
18026                             errorDescription = buffer );
18027             require_action( context->isExpensiveNow == context->isConstrainedNow, test_failed, errorDescription = "context->isExpensiveNow != context->isConstrainedNow" );
18028             require_action( context->expectedOperation == context->operation, test_failed, errorDescription = "Operation is not expected" );
18029 
18030             context->counter++;
18031             if ( context->counter == TEST_REPETITION )
18032             {
18033                 context->state = TEST_SUCCEEDED;
18034             }
18035             else
18036             {
18037                 context->subtestProgress_startTime  = NanoTimeGetCurrent();
18038                 context->isExpensivePrev            = context->isExpensiveNow;
18039                 context->isExpensiveNow             = !context->isExpensiveNow;
18040                 context->isConstrainedPrev          = context->isConstrainedNow;
18041                 context->isConstrainedNow           = !context->isConstrainedNow;
18042                 if (gExpensiveConstrainedTest_DenyConstrained || gExpensiveConstrainedTest_DenyExpensive)
18043                     context->expectedOperation      = context->isExpensiveNow ? RESULT_RMV : RESULT_ADD;
18044                 else
18045                     context->expectedOperation      = NO_UPDATE;
18046                 context->operation                  = NO_UPDATE;
18047                 context->subtestProgress_callBack   = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
18048                 require_action( context->subtestProgress_callBack != NULL, test_failed, errorDescription = "CFArrayCreateMutable failed" );
18049 
18050                 err = systemf( NULL, "ifconfig %s %sexpensive && ifconfig %s %sconstrained", context->ifName, context->isExpensiveNow ? "" : "-", context->ifName, context->isConstrainedNow ? "" : "-" );
18051                 require_noerr_action( err, test_failed, errorDescription = "systemf failed" );
18052 
18053                 gettimeofday( &context->updateTime, NULL );
18054             }
18055             break;
18056         case TEST_FAILED:
18057         test_failed:
18058             ExpensiveConstrainedSubtestReport( context, errorDescription );
18059             ExpensiveConstrainedStopAndCleanTheTest( context );
18060             if ( context->numOfRetries > 0 )
18061             {
18062                 context->state = TEST_BEGIN;
18063                 context->numOfRetries--;
18064                 break;
18065             }
18066             ExpensiveConstrainedSubtestParams[context->subtestIndex++].test_passed = 0;
18067             if (context->subtestIndex == (int) countof( ExpensiveConstrainedSubtestParams ))
18068             {
18069                 ExpensiveConstrainedFinalResultReport( context, false );
18070                 exit( 2 );
18071             }
18072             if (context->timer == NULL)
18073             {
18074                 // If timer is NULL, it means that we encounter error before we set up the test handler, which is unrecoverable.
18075                 ExpensiveConstrainedFinalResultReport( context, false );
18076                 exit( 1 );
18077             }
18078             context->state = TEST_BEGIN;
18079             break;
18080         case TEST_SUCCEEDED:
18081             ExpensiveConstrainedSubtestReport( context, NULL );
18082             ExpensiveConstrainedStopAndCleanTheTest( context );
18083             ExpensiveConstrainedSubtestParams[context->subtestIndex++].test_passed = 1;
18084             if (context->subtestIndex == (int) countof( ExpensiveConstrainedSubtestParams ))
18085             {
18086                 // all the subtests have been run
18087                 Boolean hasFailed = false;
18088                 for ( int i = 0; i < (int) countof( ExpensiveConstrainedSubtestParams ) && !hasFailed; i++ )
18089                     hasFailed = ( ExpensiveConstrainedSubtestParams[i].test_passed != 1 );
18090 
18091                 ExpensiveConstrainedFinalResultReport( context, !hasFailed );
18092                 exit( hasFailed ? 2 : 0 );
18093             }
18094             context->state = TEST_BEGIN;
18095             break;
18096         default:
18097             FPrintF( stdout, "unknown error\n" );
18098             exit( 1 );
18099     }
18100 }
18101 
18102 //===========================================================================================================================
18103 //    ExpensiveConstrainedCallback
18104 //===========================================================================================================================
18105 
18106 static void DNSSD_API
ExpensiveConstrainedCallback(__unused DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inHostname,const struct sockaddr * inSockAddr,__unused uint32_t inTTL,void * inContext)18107     ExpensiveConstrainedCallback(
18108         __unused DNSServiceRef  inSDRef,
18109         DNSServiceFlags         inFlags,
18110         uint32_t                inInterfaceIndex,
18111         DNSServiceErrorType     inError,
18112         const char *            inHostname,
18113         const struct sockaddr * inSockAddr,
18114         __unused uint32_t       inTTL,
18115         void *                  inContext )
18116 {
18117     ExpensiveConstrainedContext * const   context = (ExpensiveConstrainedContext *)inContext;
18118     OSStatus                                    err;
18119     const char *                                addrStr;
18120     char                                        addrStrBuf[ kSockAddrStringMaxSize ];
18121     char                                        inFlagsDescription[ 128 ];
18122     NanoTime64                                  now;
18123     char                                        nowTimestamp[ 32 ];
18124 
18125     switch ( inError ) {
18126         case kDNSServiceErr_NoError:
18127         case kDNSServiceErr_NoSuchRecord:
18128             break;
18129 
18130         case kDNSServiceErr_Timeout:
18131             Exit( kExitReason_Timeout );
18132 
18133         default:
18134             err = inError;
18135             goto exit;
18136     }
18137 
18138     if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
18139     {
18140         dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
18141         err = kTypeErr;
18142         goto exit;
18143     }
18144 
18145     if( !inError )
18146     {
18147         err = SockAddrToString( inSockAddr, kSockAddrStringFlagsNone, addrStrBuf );
18148         require_noerr( err, exit );
18149         addrStr = addrStrBuf;
18150     }
18151     else
18152     {
18153         addrStr = ( inSockAddr->sa_family == AF_INET ) ? kNoSuchRecordAStr : kNoSuchRecordAAAAStr;
18154     }
18155 
18156     now = NanoTimeGetCurrent();
18157     _NanoTime64ToTimestamp( now, nowTimestamp, sizeof( nowTimestamp ) );
18158     SNPrintF( inFlagsDescription, sizeof( inFlagsDescription ), "%{du:cbflags}", inFlags );
18159     err = CFPropertyListAppendFormatted( kCFAllocatorDefault,  context->subtestProgress_callBack,
18160         "{"
18161             "%kO=%s"
18162             "%kO=%s"
18163             "%kO=%s"
18164             "%kO=%lli"
18165             "%kO=%s"
18166         "}",
18167         EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_TIMESTAMP,  nowTimestamp,
18168         EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_NAME,       inHostname,
18169         EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_FLAGS,      inFlagsDescription,
18170         EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_INTERFACE,  (int64_t) inInterfaceIndex,
18171         EXPENSIVE_CONSTRAINED_SUBTEST_ACTUAL_RESULT_KEY_ADDRESS,    addrStr
18172     );
18173     require_noerr_quiet( err, exit );
18174 
18175     if ( inFlags & kDNSServiceFlagsMoreComing )
18176         return;
18177 
18178     if ( inFlags & kDNSServiceFlagsAdd )
18179         context->operation = RESULT_ADD;
18180     else
18181         context->operation = RESULT_RMV;
18182 
18183     gettimeofday(&context->notificationTime, NULL);
18184 exit:
18185     if( err ) exit( 1 );
18186 }
18187 
18188 //===========================================================================================================================
18189 //    ExpensiveConstrainedInitializeContext
18190 //===========================================================================================================================
18191 
ExpensiveConstrainedInitializeContext(ExpensiveConstrainedContext * context)18192 static void ExpensiveConstrainedInitializeContext( ExpensiveConstrainedContext *context )
18193 {
18194     // clear the flags of the previous subtest
18195     context->flags      = 0;
18196     context->protocols  = 0;
18197 
18198     // get the parameter for the current subtest
18199     const ExpensiveConstrainedTestParams *param     = &ExpensiveConstrainedSubtestParams[context->subtestIndex];
18200     gExpensiveConstrainedTest_Name                  = param->qname;
18201     gExpensiveConstrainedTest_DenyExpensive         = param->deny_expensive;
18202     gExpensiveConstrainedTest_DenyConstrained       = param->deny_constrained;
18203     gExpensiveConstrainedTest_StartFromExpensive    = param->start_from_expensive;
18204     gExpensiveConstrainedTest_ProtocolIPv4          = param->ipv4_query;
18205     gExpensiveConstrainedTest_ProtocolIPv6          = param->ipv6_query;
18206 }
18207 
18208 //===========================================================================================================================
18209 //    ExpensiveConstrainedStopAndCleanTheTest
18210 //===========================================================================================================================
18211 
ExpensiveConstrainedStopAndCleanTheTest(ExpensiveConstrainedContext * context)18212 static void ExpensiveConstrainedStopAndCleanTheTest( ExpensiveConstrainedContext *context )
18213 {
18214     // Stop the ongoing query
18215     if ( context->opRef != NULL )
18216         DNSServiceRefDeallocate( context->opRef );
18217 
18218     context->opRef      = NULL;
18219     context->flags      = 0;
18220     context->protocols  = 0;
18221 }
18222 
18223 //===========================================================================================================================
18224 //    ExpensiveConstrainedSubtestProgressReport
18225 //===========================================================================================================================
18226 
ExpensiveConstrainedSubtestProgressReport(ExpensiveConstrainedContext * context)18227 static void ExpensiveConstrainedSubtestProgressReport( ExpensiveConstrainedContext *context )
18228 {
18229     OSStatus            err;
18230     NanoTime64          now;
18231     char                startTime[ 32 ];
18232     char                endTime[ 32 ];
18233     char                expensive[ 32 ];
18234     char                constrained[ 32 ];
18235 
18236     now = NanoTimeGetCurrent();
18237     _NanoTime64ToTimestamp( context->subtestProgress_startTime, startTime, sizeof( startTime ) );
18238     _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
18239 
18240     snprintf( expensive, sizeof( expensive ), "%s -> %s", context->isExpensivePrev ? "True" : "False", context->isExpensiveNow ? "True" : "False" );
18241     snprintf( constrained, sizeof( constrained ), "%s -> %s", context->isConstrainedPrev ? "True" : "False", context->isConstrainedNow ? "True" : "False" );
18242 
18243     err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestProgress,
18244         "{"
18245             "%kO=%s"
18246             "%kO=%s"
18247             "%kO=%s"
18248             "%kO=%s"
18249             "%kO=%s"
18250             "%kO=%s"
18251             "%kO=%s"
18252             "%kO=%O"
18253         "}",
18254         EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_START_TIME,              startTime,
18255         EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_END_TIME,                endTime,
18256         EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_STATE,                   ExpensiveConstrainedStateString(context->state),
18257         EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPECT_RESULT,           ExpensiveConstrainedOperationString(context->expectedOperation),
18258         EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_ACTUAL_RESULT,           ExpensiveConstrainedOperationString(context->operation),
18259         EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_EXPENSIVE_PREV_NOW,      expensive,
18260         EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CONSTRAINED_PREV_NOW,    constrained,
18261         EXPENSIVE_CONSTRAINED_SUBTEST_PROGRESS_KEY_CALL_BACK,               context->subtestProgress_callBack
18262     );
18263     require_noerr( err, exit );
18264     ForgetCF( &context->subtestProgress_callBack );
18265     return;
18266 
18267 exit:
18268     ErrQuit( 1, "error: %#m\n", err );
18269 }
18270 
18271 //===========================================================================================================================
18272 //    ExpensiveConstrainedFinalSubtestReport
18273 //===========================================================================================================================
18274 
ExpensiveConstrainedSubtestReport(ExpensiveConstrainedContext * context,const char * error_description)18275 static void ExpensiveConstrainedSubtestReport( ExpensiveConstrainedContext *context, const char *error_description )
18276 {
18277     OSStatus            err;
18278     NanoTime64          now;
18279     char                startTime[ 32 ];
18280     char                endTime[ 32 ];
18281     char                flagDescription[ 1024 ];
18282 
18283     now = NanoTimeGetCurrent();
18284     _NanoTime64ToTimestamp( context->subtestReport_startTime, startTime, sizeof( startTime ) );
18285     _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
18286     SNPrintF( flagDescription, sizeof( flagDescription ), "%#{flags}", context->flags, kDNSServiceFlagsDescriptors );
18287 
18288     if (error_description != NULL)
18289     {
18290         err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestReport,
18291             "{"
18292                 "%kO=%s"
18293                 "%kO=%s"
18294                 "%kO=%s"
18295                 "%kO=%s"
18296                 "%kO=%s"
18297                 "%kO=%lli"
18298                 "%kO=%s"
18299                 "%kO=%O"
18300                 "%kO=%s"
18301                 "%kO=%O"
18302             "}",
18303             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME,        startTime,
18304             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME,          endTime,
18305             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME,             context->name,
18306             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS,             flagDescription,
18307             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS,         ExpensiveConstrainedProtocolString( context->protocols ),
18308             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX,   (int64_t) context->ifIndex,
18309             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME,    context->ifName,
18310             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT,            CFSTR( "Fail" ),
18311             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_ERROR,             error_description,
18312             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS,     context->subtestProgress
18313         );
18314     }
18315     else
18316     {
18317         err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->subtestReport,
18318             "{"
18319                 "%kO=%s"
18320                 "%kO=%s"
18321                 "%kO=%s"
18322                 "%kO=%s"
18323                 "%kO=%s"
18324                 "%kO=%lli"
18325                 "%kO=%s"
18326                 "%kO=%O"
18327                 "%kO=%O"
18328             "}",
18329             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_START_TIME,        startTime,
18330             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_END_TIME,          endTime,
18331             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_QNAME,             context->name,
18332             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_FLAGS,             flagDescription,
18333             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_PROTOCOLS,         ExpensiveConstrainedProtocolString( context->protocols ),
18334             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_INDEX,   (int64_t) context->ifIndex,
18335             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_INTERFACE_NAME,    context->ifName,
18336             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_RESULT,            CFSTR( "Pass" ),
18337             EXPENSIVE_CONSTRAINED_SUBTEST_REPORT_KEY_TEST_PROGRESS,     context->subtestProgress
18338         );
18339     }
18340 
18341     require_noerr( err, exit );
18342     ForgetCF( &context->subtestProgress );
18343     return;
18344 exit:
18345     ErrQuit( 1, "error: %#m\n", err );
18346 }
18347 
18348 //===========================================================================================================================
18349 //    ExpensiveConstrainedFinalResultReport
18350 //===========================================================================================================================
18351 
ExpensiveConstrainedFinalResultReport(ExpensiveConstrainedContext * context,Boolean allPassed)18352 static void ExpensiveConstrainedFinalResultReport( ExpensiveConstrainedContext *context, Boolean allPassed )
18353 {
18354     OSStatus            err;
18355     CFPropertyListRef   plist;
18356     NanoTime64          now;
18357     char                startTime[ 32 ];
18358     char                endTime[ 32 ];
18359 
18360     now = NanoTimeGetCurrent();
18361     _NanoTime64ToTimestamp( context->testReport_startTime, startTime, sizeof( startTime ) );
18362     _NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
18363 
18364     err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
18365         "{"
18366             "%kO=%s"
18367             "%kO=%s"
18368             "%kO=%b"
18369             "%kO=%O"
18370         "}",
18371         EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_START_TIME,       startTime,
18372         EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_END_TIME,         endTime,
18373         EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_ALL_PASSED,       allPassed,
18374         EXPENSIVE_CONSTRAINED_TEST_REPORT_KEY_SUBTEST_RESULT,   context->subtestReport
18375     );
18376     require_noerr( err, exit );
18377     ForgetCF( &context->subtestReport );
18378 
18379     err = OutputPropertyList( plist, context->outputFormat, context->outputFilePath );
18380     CFRelease( plist );
18381     require_noerr( err, exit );
18382 
18383     return;
18384 exit:
18385     ErrQuit( 1, "error: %#m\n", err );
18386 }
18387 
18388 //===========================================================================================================================
18389 //    ExpensiveConstrainedProtocolString
18390 //===========================================================================================================================
18391 
ExpensiveConstrainedProtocolString(DNSServiceProtocol protocol)18392 static const char *ExpensiveConstrainedProtocolString( DNSServiceProtocol protocol )
18393 {
18394     const char *str = NULL;
18395     switch ( protocol ) {
18396         case kDNSServiceProtocol_IPv4:
18397             str = "IPv4";
18398             break;
18399         case kDNSServiceProtocol_IPv6:
18400             str = "IPv6";
18401             break;
18402         case kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6:
18403             str = "IPv4 & IPv6";
18404             break;
18405         default:
18406             break;
18407     }
18408     return str;
18409 }
18410 
18411 //===========================================================================================================================
18412 //    ExpensiveConstrainedStateString
18413 //===========================================================================================================================
18414 
ExpensiveConstrainedStateString(enum ExpensiveConstrainedTestState state)18415 static const char *ExpensiveConstrainedStateString( enum ExpensiveConstrainedTestState state )
18416 {
18417     const char *str = NULL;
18418     switch ( state ) {
18419         case TEST_BEGIN:
18420             str = "TEST_BEGIN";
18421             break;
18422         case TEST_EXPENSIVE_PREPARE:
18423             str = "TEST_EXPENSIVE_PREPARE";
18424             break;
18425         case TEST_EXPENSIVE:
18426             str = "TEST_EXPENSIVE";
18427             break;
18428         case TEST_CONSTRAINED_PREPARE:
18429             str = "TEST_CONSTRAINED_PREPARE";
18430             break;
18431         case TEST_CONSTRAINED:
18432             str = "TEST_CONSTRAINED";
18433             break;
18434         case TEST_EXPENSIVE_CONSTRAINED_PREPARE:
18435             str = "TEST_EXPENSIVE_CONSTRAINED_PREPARE";
18436             break;
18437         case TEST_EXPENSIVE_CONSTRAINED:
18438             str = "TEST_EXPENSIVE_CONSTRAINED";
18439             break;
18440         case TEST_FAILED:
18441             str = "TEST_FAILED";
18442             break;
18443         case TEST_SUCCEEDED:
18444             str = "TEST_SUCCEEDED";
18445             break;
18446         default:
18447             str = "UNKNOWN";
18448             break;
18449     }
18450 
18451     return str;
18452 }
18453 
18454 //===========================================================================================================================
18455 //    ExpensiveConstrainedOperationString
18456 //===========================================================================================================================
18457 
ExpensiveConstrainedOperationString(enum ExpensiveConstrainedTestOperation operation)18458 static const char *ExpensiveConstrainedOperationString( enum ExpensiveConstrainedTestOperation operation )
18459 {
18460     const char *str = NULL;
18461     switch ( operation ) {
18462         case RESULT_ADD:
18463             str = "RESULT_ADD";
18464             break;
18465         case RESULT_RMV:
18466             str = "RESULT_RMV";
18467             break;
18468         case NO_UPDATE:
18469             str = "NO_UPDATE";
18470             break;
18471         default:
18472             str = "UNKNOWN";
18473             break;
18474     }
18475     return str;
18476 }
18477 
18478 //===========================================================================================================================
18479 //    expensiveConstrainedEndsWith
18480 //===========================================================================================================================
expensiveConstrainedEndsWith(const char * str,const char * suffix)18481 static Boolean expensiveConstrainedEndsWith( const char *str, const char *suffix )
18482 {
18483     if ( !str || !suffix )
18484         return false;
18485     size_t lenstr = strlen( str );
18486     size_t lensuffix = strlen( suffix );
18487     if ( lensuffix > lenstr )
18488         return false;
18489     return strncmp( str + lenstr - lensuffix, suffix, lensuffix ) == 0;
18490 }
18491 
18492 //===========================================================================================================================
18493 //	DNSProxyTestCmd
18494 //===========================================================================================================================
18495 
18496 // DNS Proxy Test Mode Parameters
18497 
18498 typedef enum
18499 {
18500 	kDNSProxyTestMode_Normal = 0,
18501 	kDNSProxyTestMode_ForceAAAASynthesis,
18502 	kDNSProxyTestMode_Count
18503 
18504 }	DNSProxyTestMode;
18505 
18506 check_compile_time( kDNSProxyTestMode_Count > 0 );
18507 
18508 // DNS Proxy Test DNS64 Prefix Parameters
18509 // See <https://tools.ietf.org/html/rfc6052#section-2.2>.
18510 
18511 static const char * const		kDNSProxyTestParams_DNS64Prefixes[] =
18512 {
18513 	NULL,								// No prefix.
18514 	"3ffe:ffff::/32",					// 32-bit prefix. Note: Prefix is from deprecated 3ffe::/16 block (see RFC 3701).
18515 	"2001:db8:ff00::/40",				// 40-bit prefix.
18516 	"2001:db8:ffff::/48",				// 48-bit prefix.
18517 	"2001:db8:ffff:ff00::/56",			// 56-bit prefix.
18518 	"2001:db8:ffff:ffff::/64",			// 64-bit prefix.
18519 	"2001:db8:ffff:ff00:ffff:ffff::/96"	// 96-bit prefix. Note: bits 64 - 71 MUST be zero.
18520 };
18521 
18522 // DNS Proxy Test Transport Parameters
18523 
18524 typedef enum
18525 {
18526 	kDNSProxyTestTransport_UDPv4 = 0,
18527 	kDNSProxyTestTransport_TCPv4,
18528 	kDNSProxyTestTransport_UDPv6,
18529 	kDNSProxyTestTransport_TCPv6,
18530 	kDNSProxyTestTransport_Count
18531 
18532 }	DNSProxyTestTransport;
18533 
18534 check_compile_time( kDNSProxyTestTransport_Count > 0 );
18535 
18536 // DNS Proxy Test Query Parameters
18537 
18538 typedef enum
18539 {
18540 	kDNSProxyTestQuery_A = 0,
18541 	kDNSProxyTestQuery_AAAA,
18542 	kDNSProxyTestQuery_IPv6OnlyA,
18543 	kDNSProxyTestQuery_IPv6OnlyAAAA,
18544 	kDNSProxyTestQuery_IPv4OnlyAAAA,
18545 	kDNSProxyTestQuery_AliasA,
18546 	kDNSProxyTestQuery_AliasAAAA,
18547 	kDNSProxyTestQuery_AliasIPv6OnlyA,
18548 	kDNSProxyTestQuery_AliasIPv6OnlyAAAA,
18549 	kDNSProxyTestQuery_AliasIPv4OnlyAAAA,
18550 	kDNSProxyTestQuery_NXDomainA,
18551 	kDNSProxyTestQuery_NXDomainAAAA,
18552 	kDNSProxyTestQuery_ReverseIPv6,
18553 	kDNSProxyTestQuery_ReverseIPv6NXDomain,
18554 	kDNSProxyTestQuery_ReverseIPv6DNS64,
18555 	kDNSProxyTestQuery_ReverseIPv6DNS64NXDomain,
18556 	kDNSProxyTestQuery_Count
18557 
18558 }	DNSProxyTestQuery;
18559 
18560 check_compile_time( kDNSProxyTestQuery_Count > 0 );
18561 
18562 #define kDNSProxyTestQueryIterationCount		2
18563 
18564 typedef struct DNSProxyTest *		DNSProxyTestRef;
18565 struct DNSProxyTest
18566 {
18567 	dispatch_queue_t			queue;				// Serial queue for test events.
18568 	dispatch_semaphore_t		doneSem;			// Semaphore to signal when the test is done.
18569 	DNSServiceRef				probeGAI;			// Probe GAI for DNS server.
18570 	char *						probeHostname;		// Probe hostname.
18571 	DNSXConnRef					dnsProxy;			// DNS proxy connection reference.
18572 	dispatch_source_t			timer;				// Timer to put time limit on queries.
18573 	mdns_resolver_t				resolver;			// Resolver to represent the DNS proxy as a DNS service.
18574 	CFMutableDictionaryRef		report;				// Test's report.
18575 	CFMutableArrayRef			modeResults;		// "Weak" pointer to the 1st-level array of mode results.
18576 	CFMutableArrayRef			prefixResults;		// "Weak" pointer to current 2nd-level array of DNS64 prefix results.
18577 	CFMutableArrayRef			transportResults;	// "Weak" pointer to current 3rd-level array of transport results.
18578 	CFMutableArrayRef			queryResults;		// "Weak" pointer to current 4th-level array of query results.
18579 	DNSProxyTestMode			modeParam;			// Current mode parameter.
18580 	unsigned int				prefixParamIdx;		// Current DNS64 prefix parameter index.
18581 	DNSProxyTestTransport		transportParam;		// Current transport parameter.
18582 	DNSProxyTestQuery			queryParam;			// Current query parameter.
18583 	unsigned int				queryParamIter;		// Current query iteration.
18584 	uint32_t					loopbackIndex;		// Loopback interface's index.
18585 	mdns_querier_t				querier;			// Subtest's querier to send queries to DNS proxy.
18586 	NanoTime64					startTime;			// Subtest's start time.
18587 	char *						subtestDesc;		// Subtest's description.
18588 	char *						qnameStr;			// Subtest's query QNAME as a C string.
18589 	uint8_t *					qname;				// Subtest's query QNAME in label format.
18590 	uint16_t					qtype;				// Subtest's query QTYPE.
18591 	unsigned int				aliasCount;			// Subtest's expected number of CNAMEs in response answer section.
18592 	unsigned int				answerCount;		// Subtest's expected number of QTYPE records.
18593 	int							responseCode;		// Subtest's expected response code.
18594 	uint8_t *					canonicalName;		// Subtest's expected CNAME rdata for reverse IPv6 queries.
18595 	uint8_t *					answerName;			// Subtest's expected PTR rdata for reverse IPv6 queries.
18596 	pid_t						serverPID;			// PID of spawned DNS server.
18597 	int							subtestCount;		// Number of subtests that have completed or are in progress.
18598 	int							subtestPassCount;	// Number of subtests that have passed so far.
18599 	int32_t						refCount;			// Test object's reference count.
18600 	OSStatus					error;				// Overall test's error.
18601 	int							dns64PrefixBitLen;	// Current DNS64 prefix length (valid if > 0).
18602 	uint8_t						dns64Prefix[ 16 ];	// Current DNS64 prefix (valid if dns64PrefixBitLen > 0).
18603 	char						tag[ 6 + 1 ];		// Current subtest's random tag to uniquify QNAMEs.
18604 	Boolean						synthesizedAAAA;	// True if the current subtest expects DNS64 synthesized AAAA records.
18605 	Boolean						startedSubtests;	// True if the test has started running subtests.
18606 };
18607 
18608 ulog_define_ex( kDNSSDUtilIdentifier, DNSProxyTest, kLogLevelInfo, kLogFlags_None, "DNSProxyTest", NULL );
18609 #define dpt_ulog( LEVEL, ... )		ulog( &log_category_from_name( DNSProxyTest ), (LEVEL), __VA_ARGS__ )
18610 
18611 static OSStatus	_DNSProxyTestCreate( DNSProxyTestRef *outTest );
18612 static OSStatus	_DNSProxyTestRun( DNSProxyTestRef inTest, Boolean *outPassed );
18613 static void		_DNSProxyTestRetain( DNSProxyTestRef inTest );
18614 static void		_DNSProxyTestRelease( DNSProxyTestRef inTest );
18615 
DNSProxyTestCmd(void)18616 static void	DNSProxyTestCmd( void )
18617 {
18618 	OSStatus				err;
18619 	OutputFormatType		outputFormat;
18620 	DNSProxyTestRef			test	= NULL;
18621 	Boolean					passed	= false;
18622 
18623 	err = OutputFormatFromArgString( gDNSProxyTest_OutputFormat, &outputFormat );
18624 	require_noerr_quiet( err, exit );
18625 
18626 	err = _DNSProxyTestCreate( &test );
18627 	require_noerr( err, exit );
18628 
18629 	err = _DNSProxyTestRun( test, &passed );
18630 	require_noerr( err, exit );
18631 
18632 	err = OutputPropertyList( test->report, outputFormat, gDNSProxyTest_OutputFilePath );
18633 	require_noerr( err, exit );
18634 
18635 exit:
18636 	if( test ) _DNSProxyTestRelease( test );
18637     gExitCode = err ? 1 : ( passed ? 0 : 2 );
18638 }
18639 
18640 //===========================================================================================================================
18641 
18642 static void				_DNSProxyTestStart( void *inCtx );
18643 static void				_DNSProxyTestStop( DNSProxyTestRef inTest, OSStatus inError );
18644 static OSStatus			_DNSProxyTestContinue( DNSProxyTestRef inTest, OSStatus inSubtestError, Boolean *outDone );
18645 static const char *		_DNSProxyTestGetCurrentDNS64PrefixParam( DNSProxyTestRef inTest );
18646 static OSStatus			_DNSProxyTestPrepareMode( DNSProxyTestRef inTest );
18647 static OSStatus			_DNSProxyTestPrepareDNSProxy( DNSProxyTestRef inTest );
18648 static OSStatus			_DNSProxyTestPrepareResolver( DNSProxyTestRef inTest );
18649 static OSStatus			_DNSProxyTestStartQuery( DNSProxyTestRef inTest, Boolean *outSkipQuery );
18650 static OSStatus
18651 	_DNSProxyTestSynthesizeIPv6(
18652 		const uint8_t *	inIPv6Prefix,
18653 		int				inIPv6PrefixBitLen,
18654 		uint32_t		inIPv4Addr,
18655 		uint8_t			outIPv6Addr[ STATIC_PARAM 16 ] );
18656 static void	_DNSProxyTestHandleQuerierResult( DNSProxyTestRef inTest );
18657 static OSStatus
18658 	_DNSProxyTestVerifyAddressResponse(
18659 		const uint8_t *	inMsgPtr,
18660 		size_t			inMsgLen,
18661 		const uint8_t *	inQName,
18662 		uint16_t		inQType,
18663 		int				inResponseCode,
18664 		unsigned int	inAliasCount,
18665 		unsigned int	inAnswerCount,
18666 		const uint8_t *	inDNS64Prefix,
18667 		int				inDNS64PrefixBitLen );
18668 static OSStatus
18669 	_DNSProxyTestVerifyReverseIPv6Response(
18670 		const uint8_t *	inMsgPtr,
18671 		size_t			inMsgLen,
18672 		const uint8_t *	inQName,
18673 		int				inResponseCode,
18674 		const uint8_t *	inCanonicalName,
18675 		const uint8_t *	inAnswerName );
18676 static void DNSSD_API
18677 	_DNSProxyTestProbeGAICallback(
18678 		DNSServiceRef			inSDRef,
18679 		DNSServiceFlags			inFlags,
18680 		uint32_t				inInterfaceIndex,
18681 		DNSServiceErrorType		inError,
18682 		const char *			inHostname,
18683 		const struct sockaddr *	inSockAddr,
18684 		uint32_t				inTTL,
18685 		void *					inCtx );
18686 static void			_DNSProxyTestProbeTimerHandler( void *inCtx );
18687 
_DNSProxyTestCreate(DNSProxyTestRef * outTest)18688 static OSStatus	_DNSProxyTestCreate( DNSProxyTestRef *outTest )
18689 {
18690 	OSStatus			err;
18691 	DNSProxyTestRef		obj;
18692 
18693 	obj = (DNSProxyTestRef) calloc( 1, sizeof( *obj ) );
18694 	require_action( obj, exit, err = kNoMemoryErr );
18695 
18696 	obj->refCount	= 1;
18697 	obj->error		= kInProgressErr;
18698 	obj->serverPID	= -1;
18699 
18700 	obj->queue = dispatch_queue_create( "com.apple.dnssdutil.dns-proxy-test", DISPATCH_QUEUE_SERIAL );
18701 	require_action( obj->queue, exit, err = kNoResourcesErr );
18702 
18703 	obj->doneSem = dispatch_semaphore_create( 0 );
18704 	require_action( obj->doneSem, exit, err = kNoResourcesErr );
18705 
18706 	*outTest = obj;
18707 	obj = NULL;
18708 	err = kNoErr;
18709 
18710 exit:
18711 	if( obj ) _DNSProxyTestRelease( obj );
18712 	return( err );
18713 }
18714 
18715 //===========================================================================================================================
18716 
_DNSProxyTestRun(DNSProxyTestRef me,Boolean * outPassed)18717 static OSStatus	_DNSProxyTestRun( DNSProxyTestRef me, Boolean *outPassed )
18718 {
18719 	Boolean		passed;
18720 
18721 	dispatch_async_f( me->queue, me, _DNSProxyTestStart );
18722 	dispatch_semaphore_wait( me->doneSem, DISPATCH_TIME_FOREVER );
18723 
18724 	passed = ( !me->error && ( me->subtestPassCount == me->subtestCount ) ) ? true : false;
18725 	CFDictionarySetBoolean( me->report, CFSTR( "pass" ), passed );
18726 	dpt_ulog( kLogLevelInfo, "Test result: %s\n", passed ? "pass" : "fail" );
18727 
18728 	if( outPassed ) *outPassed = passed;
18729 	return( me->error );
18730 }
18731 
18732 //===========================================================================================================================
18733 
_DNSProxyTestRetain(DNSProxyTestRef me)18734 static void	_DNSProxyTestRetain( DNSProxyTestRef me )
18735 {
18736 	atomic_add_32( &me->refCount, 1 );
18737 }
18738 
18739 //===========================================================================================================================
18740 
_DNSProxyTestRelease(DNSProxyTestRef me)18741 static void	_DNSProxyTestRelease( DNSProxyTestRef me )
18742 {
18743 	if( atomic_add_and_fetch_32( &me->refCount, -1 ) == 0 )
18744 	{
18745 		check( !me->probeGAI );
18746 		check( !me->probeHostname );
18747 		check( !me->dnsProxy );
18748 		check( !me->timer );
18749 		check( !me->resolver );
18750 		check( !me->modeResults );
18751 		check( !me->prefixResults );
18752 		check( !me->transportResults );
18753 		check( !me->queryResults );
18754 		check( !me->querier );
18755 		check( !me->subtestDesc );
18756 		check( !me->qnameStr );
18757 		check( !me->qname );
18758 		check( !me->canonicalName );
18759 		check( !me->answerName );
18760 		check( me->serverPID < 0 );
18761 		dispatch_forget( &me->queue );
18762 		dispatch_forget( &me->doneSem );
18763 		ForgetCF( &me->report );
18764 		free( me );
18765 	}
18766 }
18767 
18768 //===========================================================================================================================
18769 
18770 #define kDNSProxyTestProbeQueryTimeoutSecs		5
18771 
_DNSProxyTestStart(void * inCtx)18772 static void _DNSProxyTestStart( void *inCtx )
18773 {
18774 	OSStatus					err;
18775 	const DNSProxyTestRef		me			= (DNSProxyTestRef) inCtx;
18776 	char *						serverCmd	= NULL;
18777 	NanoTime64					startTime;
18778 	char						startTimeStr[ 32 ];
18779 	char						tag[ 6 + 1 ];
18780 
18781 	startTime = NanoTimeGetCurrent();
18782 
18783 	dpt_ulog( kLogLevelInfo, "Starting test\n" );
18784 
18785 	me->error			= kInProgressErr;
18786 	me->loopbackIndex	= if_nametoindex( "lo0" );
18787 	err = map_global_value_errno( me->loopbackIndex != 0, me->loopbackIndex );
18788 	require_noerr_action_quiet( err, exit, dpt_ulog( kLogLevelError, "Failed to get interface index for lo0: %#m", err ) );
18789 
18790 	serverCmd = NULL;
18791 	ASPrintF( &serverCmd, "dnssdutil server --loopback --follow %lld --port 0 --defaultTTL 300 --responseDelay 10",
18792 		(int64_t) getpid() );
18793 	require_action_quiet( serverCmd, exit, err = kUnknownErr );
18794 
18795 	err = _SpawnCommand( &me->serverPID, "/dev/null", "/dev/null", "%s", serverCmd );
18796 	require_noerr( err, exit );
18797 
18798 	check( !me->probeHostname );
18799 	ASPrintF( &me->probeHostname, "tag-dns-proxy-test-probe-%s.ipv4.d.test.",
18800 		_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
18801 	require_action( me->probeHostname, exit, err = kNoMemoryErr );
18802 
18803 	err = DNSServiceGetAddrInfo( &me->probeGAI, 0, kDNSServiceInterfaceIndexAny, kDNSServiceProtocol_IPv4,
18804 		me->probeHostname, _DNSProxyTestProbeGAICallback, me );
18805 	require_noerr( err, exit );
18806 
18807 	err = DNSServiceSetDispatchQueue( me->probeGAI, me->queue );
18808 	require_noerr( err, exit );
18809 
18810 	check( !me->timer );
18811 	err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDNSProxyTestProbeQueryTimeoutSecs ),
18812 		kDNSProxyTestProbeQueryTimeoutSecs * ( UINT64_C_safe( kNanosecondsPerSecond ) / 10 ),
18813 		me->queue, _DNSProxyTestProbeTimerHandler, me, &me->timer );
18814 	require_noerr( err, exit );
18815 	dispatch_resume( me->timer );
18816 
18817 	_NanoTime64ToTimestamp( startTime, startTimeStr, sizeof( startTimeStr ) );
18818 	err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &me->report,
18819 		"{"
18820 			"%kO=%s"	// startTime
18821 			"%kO=%s"	// serverCmd
18822 			"%kO=%s"	// probeHostname
18823 			"%kO=[%@]"	// results
18824 		"}",
18825 		CFSTR( "startTime" ),		startTimeStr,
18826 		CFSTR( "serverCmd" ),		serverCmd,
18827 		CFSTR( "probeHostname" ),	me->probeHostname,
18828 		CFSTR( "results" ),			&me->modeResults );
18829 	require_noerr( err, exit );
18830 
18831 exit:
18832 	FreeNullSafe( serverCmd );
18833 	if( err ) _DNSProxyTestStop( me, err );
18834 }
18835 
18836 //===========================================================================================================================
18837 
18838 static void		_DNSProxyTestSubtestCleanup( DNSProxyTestRef inTest );
18839 
18840 #define _DNSXForget( X )		ForgetCustom( X, DNSXRefDeAlloc )
18841 
_DNSProxyTestStop(DNSProxyTestRef me,OSStatus inError)18842 static void	_DNSProxyTestStop( DNSProxyTestRef me, OSStatus inError )
18843 {
18844 	OSStatus		err;
18845 	NanoTime64		endTime;
18846 	char			endTimeStr[ 32 ];
18847 
18848 	endTime = NanoTimeGetCurrent();
18849 	me->error = inError;
18850 	dpt_ulog( kLogLevelInfo, "Stopping test with error: %#m\n", me->error );
18851 
18852 	DNSServiceForget( &me->probeGAI );
18853 	ForgetMem( &me->probeHostname );
18854 	_DNSXForget( &me->dnsProxy );
18855 	dispatch_source_forget( &me->timer );
18856 	mdns_resolver_forget( &me->resolver );
18857 	me->prefixResults		= NULL;
18858 	me->modeResults			= NULL;
18859 	me->transportResults	= NULL;
18860 	me->queryResults		= NULL;
18861 	_DNSProxyTestSubtestCleanup( me );
18862 	if( me->serverPID >= 0 )
18863 	{
18864 		OSStatus		killErr;
18865 
18866 		killErr = kill( me->serverPID, SIGTERM );
18867 		killErr = map_global_noerr_errno( killErr );
18868 		check_noerr( killErr );
18869 		me->serverPID = -1;
18870 	}
18871 	_NanoTime64ToTimestamp( endTime, endTimeStr, sizeof( endTimeStr ) );
18872 	err = CFPropertyListAppendFormatted( kCFAllocatorDefault, me->report,
18873 		"%kO=%s"		// endTime
18874 		"%kO=%lli"		// subtestCount
18875 		"%kO=%lli",		// subtestPassCount
18876 		CFSTR( "endTime" ),				endTimeStr,
18877 		CFSTR( "subtestCount" ),		(int64_t) me->subtestCount,
18878 		CFSTR( "subtestPassCount" ),	(int64_t) me->subtestPassCount );
18879 	check_noerr( err );
18880 	if( err && !me->error ) me->error = err;
18881 	dispatch_semaphore_signal( me->doneSem );
18882 }
18883 
18884 //===========================================================================================================================
18885 
_DNSProxyTestSubtestCleanup(DNSProxyTestRef me)18886 static void	_DNSProxyTestSubtestCleanup( DNSProxyTestRef me )
18887 {
18888 	dispatch_source_forget( &me->timer );
18889 	mdns_querier_forget( &me->querier );
18890 	ForgetMem( &me->subtestDesc );
18891 	ForgetMem( &me->qnameStr );
18892 	ForgetMem( &me->qname );
18893 	ForgetMem( &me->canonicalName );
18894 	ForgetMem( &me->answerName );
18895 }
18896 
18897 //===========================================================================================================================
18898 
18899 static OSStatus		_DNSProxyTestHandleSubtestCompletion( DNSProxyTestRef inTest, OSStatus inSubtestError );
18900 static char *		_DNSProxyTestCreateSubtestDescription( DNSProxyTestRef inTest );
18901 static const char *	_DNSProxyTestQueryToString( DNSProxyTestQuery inQuery );
18902 static const char *	_DNSProxyTestTransportToString( DNSProxyTestTransport inTransport );
18903 static const char *	_DNSProxyTestModeToString( DNSProxyTestMode inMode );
18904 
_DNSProxyTestContinue(DNSProxyTestRef me,OSStatus inSubtestError,Boolean * outDone)18905 static OSStatus	_DNSProxyTestContinue( DNSProxyTestRef me, OSStatus inSubtestError, Boolean *outDone )
18906 {
18907 	OSStatus		err;
18908 	Boolean			skipQueries	= false;
18909 	Boolean			done		= false;
18910 
18911 	do
18912 	{
18913 		if( me->startedSubtests )
18914 		{
18915 			if( !skipQueries )
18916 			{
18917 				err = _DNSProxyTestHandleSubtestCompletion( me, inSubtestError );
18918 				require_noerr( err, exit );
18919 			}
18920 			else
18921 			{
18922 				dpt_ulog( kLogLevelInfo, "Skipped subtest: %s\n", me->subtestDesc );
18923 			}
18924 			_DNSProxyTestSubtestCleanup( me );
18925 			if( skipQueries || ( ++me->queryParamIter == kDNSProxyTestQueryIterationCount ) )
18926 			{
18927 				me->queryParamIter = 0;
18928 				if( ++me->queryParam == kDNSProxyTestQuery_Count )
18929 				{
18930 					me->queryParam		= 0;
18931 					me->queryResults	= NULL;
18932 					dpt_ulog( kLogLevelInfo, "Invalidating resolver: %@\n", me->resolver );
18933 					mdns_resolver_forget( &me->resolver );
18934 					if( ++me->transportParam == kDNSProxyTestTransport_Count )
18935 					{
18936 						me->transportParam		= 0;
18937 						me->transportResults	= NULL;
18938 						dpt_ulog( kLogLevelInfo, "Disabling DNS proxy\n" );
18939 						_DNSXForget( &me->dnsProxy );
18940 						if( ++me->prefixParamIdx == countof( kDNSProxyTestParams_DNS64Prefixes ) )
18941 						{
18942 							me->prefixParamIdx	= 0;
18943 							me->prefixResults	= NULL;
18944 							if( ++me->modeParam == kDNSProxyTestMode_Count )
18945 							{
18946 								me->modeParam	= 0;
18947 								me->modeResults	= NULL;
18948 								done			= true;
18949 								err				= kNoErr;
18950 								goto exit;
18951 							}
18952 						}
18953 					}
18954 				}
18955 			}
18956 		}
18957 		else
18958 		{
18959 			me->startedSubtests = true;
18960 		}
18961 		if( ( me->queryParamIter == 0 ) && ( me->queryParam == 0 ) )
18962 		{
18963 			if( me->transportParam == 0 )
18964 			{
18965 				if( me->prefixParamIdx == 0 )
18966 				{
18967 					err = _DNSProxyTestPrepareMode( me );
18968 					require_noerr( err, exit );
18969 				}
18970 				err = _DNSProxyTestPrepareDNSProxy( me );
18971 				require_noerr( err, exit );
18972 			}
18973 			err = _DNSProxyTestPrepareResolver( me );
18974 			require_noerr( err, exit );
18975 		}
18976 		err = _DNSProxyTestStartQuery( me, &skipQueries );
18977 		require_noerr( err, exit );
18978 
18979 		check( !me->subtestDesc );
18980 		me->subtestDesc = _DNSProxyTestCreateSubtestDescription( me );
18981 		require_action( me->subtestDesc, exit, err = kNoMemoryErr );
18982 
18983 	}	while( skipQueries );
18984 
18985 	++me->subtestCount;
18986 	dpt_ulog( kLogLevelInfo, "Started subtest #%d: %s\n", me->subtestCount, me->subtestDesc );
18987 
18988 exit:
18989 	if( outDone ) *outDone = done;
18990 	return( err );
18991 }
18992 
_DNSProxyTestHandleSubtestCompletion(DNSProxyTestRef me,OSStatus inSubtestError)18993 static OSStatus	_DNSProxyTestHandleSubtestCompletion( DNSProxyTestRef me, OSStatus inSubtestError )
18994 {
18995 	OSStatus		err;
18996 	NanoTime64		endTime;
18997 	char			errorStr[ 128 ];
18998 	char			startTimeStr[ 32 ];
18999 	char			endTimeStr[ 32 ];
19000 
19001 	endTime = NanoTimeGetCurrent();
19002 
19003 	if( !inSubtestError ) ++me->subtestPassCount;
19004 
19005 	dpt_ulog( kLogLevelInfo, "Subtest #%d result: %s (pass rate: %d/%d)\n",
19006 		me->subtestCount, inSubtestError ? "fail" : "pass", me->subtestPassCount, me->subtestCount );
19007 
19008 	_NanoTime64ToTimestamp( me->startTime, startTimeStr, sizeof( startTimeStr ) );
19009 	_NanoTime64ToTimestamp( endTime, endTimeStr, sizeof( endTimeStr ) );
19010 	SNPrintF( errorStr, sizeof( errorStr ), "%m", inSubtestError );
19011 	err = CFPropertyListAppendFormatted( kCFAllocatorDefault, me->queryResults,
19012 		"{"
19013 			"%kO=%s"		// description
19014 			"%kO=%s"		// startTime
19015 			"%kO=%s"		// endTime
19016 			"%kO=%s"		// qname
19017 			"%kO=%s"		// qtype
19018 			"%kO=%b"		// pass
19019 			"%kO="			// error
19020 			"{"
19021 				"%kO=%lli"	// code
19022 				"%kO=%s"	// description
19023 			"}"
19024 		"}",
19025 		CFSTR( "description" ),	me->subtestDesc,
19026 		CFSTR( "startTime" ),	startTimeStr,
19027 		CFSTR( "endTime" ),		endTimeStr,
19028 		CFSTR( "qname" ),		me->qnameStr,
19029 		CFSTR( "qtype" ),		DNSRecordTypeValueToString( me->qtype ),
19030 		CFSTR( "pass" ),		inSubtestError ? false : true,
19031 		CFSTR( "error" ),
19032 		CFSTR( "code" ),		(int64_t) inSubtestError,
19033 		CFSTR( "description" ),	errorStr );
19034 	return( err );
19035 }
19036 
_DNSProxyTestCreateSubtestDescription(DNSProxyTestRef me)19037 static char *	_DNSProxyTestCreateSubtestDescription( DNSProxyTestRef me )
19038 {
19039 	const char *		queryStr;
19040 	const char *		transportStr;
19041 	const char *		dns64PrefixStr;
19042 	const char *		modeStr;
19043 	char *				description;
19044 
19045 	queryStr		= _DNSProxyTestQueryToString( me->queryParam );
19046 	transportStr	= _DNSProxyTestTransportToString( me->transportParam );
19047 	dns64PrefixStr	= _DNSProxyTestGetCurrentDNS64PrefixParam( me );
19048 	modeStr			= _DNSProxyTestModeToString( me->modeParam );
19049 	description		= NULL;
19050 	if( dns64PrefixStr )
19051 	{
19052 		ASPrintF( &description, "%s over %s to DNS proxy using DNS64 prefix %s in %s mode (%u of %d)",
19053 			queryStr, transportStr, dns64PrefixStr, modeStr, me->queryParamIter + 1, kDNSProxyTestQueryIterationCount );
19054 	}
19055 	else
19056 	{
19057 		ASPrintF( &description, "%s over %s to DNS proxy in %s mode (%u of %d)",
19058 			queryStr, transportStr, modeStr, me->queryParamIter + 1, kDNSProxyTestQueryIterationCount );
19059 	}
19060 	return( description );
19061 }
19062 
19063 //===========================================================================================================================
19064 
_DNSProxyTestQueryToString(DNSProxyTestQuery inQuery)19065 static const char *	_DNSProxyTestQueryToString( DNSProxyTestQuery inQuery )
19066 {
19067 	switch( inQuery )
19068 	{
19069 		case kDNSProxyTestQuery_A:							return( "A record query" );
19070 		case kDNSProxyTestQuery_AAAA:						return( "AAAA record query" );
19071 		case kDNSProxyTestQuery_IPv6OnlyA:					return( "IPv6-only A record query" );
19072 		case kDNSProxyTestQuery_IPv6OnlyAAAA:				return( "IPv6-only AAAA record query" );
19073 		case kDNSProxyTestQuery_IPv4OnlyAAAA:				return( "IPv4-only AAAA record query" );
19074 		case kDNSProxyTestQuery_AliasA:						return( "A record query with CNAMEs" );
19075 		case kDNSProxyTestQuery_AliasAAAA:					return( "AAAA record query with CNAMEs" );
19076 		case kDNSProxyTestQuery_AliasIPv6OnlyA:				return( "IPv6-only A record query with CNAMEs" );
19077 		case kDNSProxyTestQuery_AliasIPv6OnlyAAAA:			return( "IPv6-only AAAA record query with CNAMEs" );
19078 		case kDNSProxyTestQuery_AliasIPv4OnlyAAAA:			return( "IPv4-only AAAA record query with CNAMEs" );
19079 		case kDNSProxyTestQuery_NXDomainA:					return( "A record query (NXDomain)" );
19080 		case kDNSProxyTestQuery_NXDomainAAAA:				return( "AAAA record query (NXDomain)" );
19081 		case kDNSProxyTestQuery_ReverseIPv6:				return( "Reverse IPv6 query" );
19082 		case kDNSProxyTestQuery_ReverseIPv6NXDomain:		return( "Reverse IPv6 query (NXDomain)" );
19083 		case kDNSProxyTestQuery_ReverseIPv6DNS64:			return( "Reverse IPv6 query with DNS64 prefix" );
19084 		case kDNSProxyTestQuery_ReverseIPv6DNS64NXDomain:	return( "Reverse IPv6 query with DNS64 prefix (NXDomain)" );
19085 		default:											return( "<INVALID QUERY>" );
19086     }
19087 }
19088 
19089 //===========================================================================================================================
19090 
_DNSProxyTestTransportToString(DNSProxyTestTransport inTransport)19091 static const char *	_DNSProxyTestTransportToString( DNSProxyTestTransport inTransport )
19092 {
19093     switch( inTransport )
19094 	{
19095 		case kDNSProxyTestTransport_UDPv4:	return( "IPv4-UDP" );
19096 		case kDNSProxyTestTransport_TCPv4:	return( "IPv4-TCP" );
19097 		case kDNSProxyTestTransport_UDPv6:	return( "IPv6-UDP" );
19098 		case kDNSProxyTestTransport_TCPv6:	return( "IPv6-TCP" );
19099 		default:							return( "<INVALID TRANSPORT>" );
19100 	}
19101 }
19102 
19103 //===========================================================================================================================
19104 
_DNSProxyTestModeToString(DNSProxyTestMode inMode)19105 static const char *	_DNSProxyTestModeToString( DNSProxyTestMode inMode )
19106 {
19107     switch( inMode )
19108 	{
19109 		case kDNSProxyTestMode_Normal:				return( "normal" );
19110 		case kDNSProxyTestMode_ForceAAAASynthesis:	return( "force-AAAA-synthesis" );
19111 		default:									return( "<INVALID MODE>" );
19112 	}
19113 }
19114 
19115 //===========================================================================================================================
19116 
_DNSProxyTestGetCurrentDNS64PrefixParam(DNSProxyTestRef me)19117 static const char *	_DNSProxyTestGetCurrentDNS64PrefixParam( DNSProxyTestRef me )
19118 {
19119 	const unsigned int		index = me->prefixParamIdx;
19120 	const unsigned int		count = countof( kDNSProxyTestParams_DNS64Prefixes );
19121 
19122 	require_fatal( index < count, "DNSProxyTest DNS Proxy DNS64 prefix parameter index %u out-of-range.", index );
19123 	return( kDNSProxyTestParams_DNS64Prefixes[ index ] );
19124 }
19125 
19126 //===========================================================================================================================
19127 
_DNSProxyTestPrepareMode(DNSProxyTestRef me)19128 static OSStatus	_DNSProxyTestPrepareMode( DNSProxyTestRef me )
19129 {
19130 	OSStatus		err;
19131 
19132 	err = CFPropertyListAppendFormatted( kCFAllocatorDefault, me->modeResults,
19133 		"{"
19134 			"%kO=%s"	// mode
19135 			"%kO=[%@]"	// results
19136 		"}",
19137 		CFSTR( "mode" ),	_DNSProxyTestModeToString( me->modeParam ),
19138 		CFSTR( "results" ),	&me->prefixResults );
19139 	return( err );
19140 }
19141 
19142 //===========================================================================================================================
19143 
19144 static void	_DNSProxyTestDNSProxyCallback( DNSXConnRef inDNSProxy, DNSXErrorType inError );
19145 
_DNSProxyTestPrepareDNSProxy(DNSProxyTestRef me)19146 static OSStatus	_DNSProxyTestPrepareDNSProxy( DNSProxyTestRef me )
19147 {
19148 	OSStatus			err;
19149 	const char *		dns64PrefixStr;
19150 	IfIndex				interfaces[ MaxInputIf ];
19151 
19152 	me->dns64PrefixBitLen = 0;
19153 	memset( me->dns64Prefix, 0, sizeof( me->dns64Prefix ) );
19154 
19155 	dns64PrefixStr = _DNSProxyTestGetCurrentDNS64PrefixParam( me );
19156 	if( dns64PrefixStr )
19157 	{
19158 		const char *		end;
19159 
19160 		err = _StringToIPv6Address( dns64PrefixStr, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoScope,
19161 			me->dns64Prefix, NULL, NULL, &me->dns64PrefixBitLen, &end );
19162 		if( !err && ( *end != '\0' ) ) err = kMalformedErr;
19163 		require_noerr_quiet( err, exit );
19164 	}
19165 	memset( interfaces, 0, sizeof( interfaces ) );
19166 	interfaces[ 0 ] = me->loopbackIndex;
19167 
19168 	check( !me->dnsProxy );
19169 	sleep( 1 ); // Workaround: Allow one second for previous DNS proxy state to get cleaned up.
19170 	if( dns64PrefixStr )
19171 	{
19172 		if( __builtin_available( macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, * ) )
19173 		{
19174 			DNSXProxyFlags		flags;
19175 
19176 			switch( me->modeParam )
19177 			{
19178 				case kDNSProxyTestMode_Normal:
19179 					flags = kDNSXProxyFlagNull;
19180 					break;
19181 
19182 				case kDNSProxyTestMode_ForceAAAASynthesis:
19183 					flags = kDNSXProxyFlagForceAAAASynthesis;
19184 					break;
19185 
19186 				default:
19187 					FatalErrorF( "Unhandled DNSProxyTestMode value %ld", (long) me->modeParam );
19188 			}
19189 			dpt_ulog( kLogLevelInfo, "Enabling DNS proxy with DNS64 prefix %.16a/%d\n",
19190 				me->dns64Prefix, me->dns64PrefixBitLen );
19191 			err = DNSXEnableProxy64( &me->dnsProxy, kDNSProxyEnable, interfaces, 0, me->dns64Prefix, me->dns64PrefixBitLen,
19192 				flags, me->queue, _DNSProxyTestDNSProxyCallback );
19193 			require_noerr_quiet( err, exit );
19194 		}
19195 		else
19196 		{
19197 			dpt_ulog( kLogLevelError, "DNSXEnableProxy64() is not available on this OS.\n" );
19198 			err = kUnsupportedErr;
19199 			goto exit;
19200 		}
19201 	}
19202 	else
19203 	{
19204 		dpt_ulog( kLogLevelInfo, "Enabling DNS proxy (without a DNS64 prefix)\n" );
19205 		err = DNSXEnableProxy( &me->dnsProxy, kDNSProxyEnable, interfaces, 0, me->queue, _DNSProxyTestDNSProxyCallback );
19206 		require_noerr_quiet( err, exit );
19207 	}
19208 	err = CFPropertyListAppendFormatted( kCFAllocatorDefault, me->prefixResults,
19209 		"{"
19210 			"%kO=%s"	// dns64Prefix
19211 			"%kO=[%@]"	// results
19212 		"}",
19213 		CFSTR( "dns64Prefix" ),	dns64PrefixStr ? dns64PrefixStr : "",
19214 		CFSTR( "results" ),		&me->transportResults );
19215 	require_noerr( err, exit );
19216 
19217 exit:
19218 	return( err );
19219 }
19220 
_DNSProxyTestDNSProxyCallback(DNSXConnRef inDNSProxy,DNSXErrorType inError)19221 static void	_DNSProxyTestDNSProxyCallback( DNSXConnRef inDNSProxy, DNSXErrorType inError )
19222 {
19223 	Unused( inDNSProxy );
19224 
19225 	if( !inError )	dpt_ulog( kLogLevelInfo, "DNS proxy callback: no error\n" );
19226 	else			dpt_ulog( kLogLevelError, "DNS proxy callback: error %#m\n", inError );
19227 }
19228 
19229 //===========================================================================================================================
19230 
19231 #define kDNSProxyTestDNSProxyAddrStr_IPv4		"127.0.0.1:53"
19232 #define kDNSProxyTestDNSProxyAddrStr_IPv6		"[::1]:53"
19233 
_DNSProxyTestPrepareResolver(DNSProxyTestRef me)19234 static OSStatus	_DNSProxyTestPrepareResolver( DNSProxyTestRef me )
19235 {
19236 	OSStatus					err;
19237 	const char *				addrStr;
19238 	mdns_address_t				addr = NULL;
19239 	mdns_resolver_type_t		resolverType;
19240 
19241 	switch( me->transportParam )
19242 	{
19243 		case kDNSProxyTestTransport_UDPv4:
19244 			addrStr			= kDNSProxyTestDNSProxyAddrStr_IPv4;
19245 			resolverType	= mdns_resolver_type_normal;
19246 			break;
19247 
19248 		case kDNSProxyTestTransport_TCPv4:
19249 			addrStr			= kDNSProxyTestDNSProxyAddrStr_IPv4;
19250 			resolverType	= mdns_resolver_type_tcp;
19251 			break;
19252 
19253 		case kDNSProxyTestTransport_UDPv6:
19254 			addrStr			= kDNSProxyTestDNSProxyAddrStr_IPv6;
19255 			resolverType	= mdns_resolver_type_normal;
19256 			break;
19257 
19258 		case kDNSProxyTestTransport_TCPv6:
19259 			addrStr			= kDNSProxyTestDNSProxyAddrStr_IPv6;
19260 			resolverType	= mdns_resolver_type_tcp;
19261 			break;
19262 
19263 		default:
19264 			FatalErrorF( "Unhandled DNSProxyTestTransport value %ld", (long) me->transportParam );
19265 	}
19266 	check( !me->resolver );
19267 	me->resolver = mdns_resolver_create( resolverType, 0, &err );
19268 	require_noerr( err, exit );
19269 
19270 	addr = mdns_address_create_from_ip_address_string( addrStr );
19271 	require_action( addr, exit, err = kUnknownErr );
19272 
19273 	err = mdns_resolver_add_server_address( me->resolver, addr );
19274 	mdns_forget( &addr );
19275 	require_noerr( err, exit );
19276 
19277 	dpt_ulog( kLogLevelInfo, "Activating resolver: %@\n", me->resolver );
19278 	mdns_resolver_activate( me->resolver );
19279 
19280 	_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( me->tag ) - 1, me->tag );
19281 
19282 	err = CFPropertyListAppendFormatted( kCFAllocatorDefault, me->transportResults,
19283 		"{"
19284 			"%kO=%s"	// transport
19285 			"%kO=[%@]"	// results
19286 		"}",
19287 		CFSTR( "transport" ),	_DNSProxyTestTransportToString( me->transportParam ),
19288 		CFSTR( "results" ),		&me->queryResults );
19289 	require_noerr( err, exit );
19290 
19291 exit:
19292 	mdns_release_null_safe( addr );
19293 	return( err );
19294 }
19295 
19296 //===========================================================================================================================
19297 
19298 #define kDNSProxyTestQuerierTimeLimitSecs		5
19299 #define kDNSProxyTestRecordTTL					( 5 * kSecondsPerMinute )
19300 #define kDNSProxyTestAddressCount				4
19301 #define kDNSProxyTestAliasCount					4
19302 
19303 static void	_DNSProxyTestQuerierTimerHandler( void *inCtx );
19304 
_DNSProxyTestStartQuery(DNSProxyTestRef me,Boolean * outSkipQuery)19305 static OSStatus	_DNSProxyTestStartQuery( DNSProxyTestRef me, Boolean *outSkipQuery )
19306 {
19307 	OSStatus			err;
19308 	mdns_querier_t		querier;
19309 	uint8_t				tmpName[ kDomainNameLengthMax ];
19310 	Boolean				skipQuery = false;
19311 
19312 	me->startTime		= NanoTimeGetCurrent();
19313 	me->qtype			= 0;
19314 	me->aliasCount		= 0;
19315 	me->answerCount		= 0;
19316 	me->responseCode	= 0;
19317 	me->canonicalName	= NULL;
19318 	me->answerName		= NULL;
19319 	me->synthesizedAAAA	= false;
19320 
19321 	check( !me->qnameStr );
19322 	switch( me->queryParam )
19323 	{
19324 		case kDNSProxyTestQuery_A:
19325 			me->qtype			= kDNSRecordType_A;
19326 			me->answerCount		= kDNSProxyTestAddressCount;
19327 			me->responseCode	= kDNSRCode_NoError;
19328 			ASPrintF( &me->qnameStr, "count-%u.ttl-%u.tag-a-%s.d.test.",
19329 				kDNSProxyTestAddressCount, kDNSProxyTestRecordTTL, me->tag );
19330 			require_action( me->qnameStr, exit, err = kNoMemoryErr );
19331 			break;
19332 
19333 		case kDNSProxyTestQuery_AAAA:
19334 			me->qtype			= kDNSRecordType_AAAA;
19335 			me->answerCount		= kDNSProxyTestAddressCount;
19336 			me->responseCode	= kDNSRCode_NoError;
19337 			if( ( me->dns64PrefixBitLen > 0 ) && ( me->modeParam == kDNSProxyTestMode_ForceAAAASynthesis ) )
19338 			{
19339 				me->synthesizedAAAA = true;
19340 			}
19341 			else
19342 			{
19343 				me->synthesizedAAAA = false;
19344 			}
19345 			ASPrintF( &me->qnameStr, "count-%u.ttl-%u.tag-aaaa-%s.d.test.",
19346 				kDNSProxyTestAddressCount, kDNSProxyTestRecordTTL, me->tag );
19347 			require_action( me->qnameStr, exit, err = kNoMemoryErr );
19348 			break;
19349 
19350 		case kDNSProxyTestQuery_IPv6OnlyA:
19351 			me->qtype			= kDNSRecordType_A;
19352 			me->answerCount		= 0;
19353 			me->responseCode	= kDNSRCode_NoError;
19354 			ASPrintF( &me->qnameStr, "ipv6.ttl-%u.tag-ipv6-only-a-%s.d.test.", kDNSProxyTestRecordTTL, me->tag );
19355 			require_action( me->qnameStr, exit, err = kNoMemoryErr );
19356 			break;
19357 
19358 		case kDNSProxyTestQuery_IPv6OnlyAAAA:
19359 			me->qtype			= kDNSRecordType_AAAA;
19360 			me->responseCode	= kDNSRCode_NoError;
19361 			if( ( me->dns64PrefixBitLen > 0 ) && ( me->modeParam == kDNSProxyTestMode_ForceAAAASynthesis ) )
19362 			{
19363 				me->answerCount = 0;
19364 			}
19365 			else
19366 			{
19367 				me->answerCount = kDNSProxyTestAddressCount;
19368 			}
19369 			ASPrintF( &me->qnameStr, "count-%u.ipv6.ttl-%u.tag-ipv6-only-aaaa-%s.d.test.",
19370 				kDNSProxyTestAddressCount, kDNSProxyTestRecordTTL, me->tag );
19371 			require_action( me->qnameStr, exit, err = kNoMemoryErr );
19372 			break;
19373 
19374 		case kDNSProxyTestQuery_IPv4OnlyAAAA:
19375 			me->qtype			= kDNSRecordType_AAAA;
19376 			me->responseCode	= kDNSRCode_NoError;
19377 			if( me->dns64PrefixBitLen > 0 )
19378 			{
19379 				me->answerCount		= kDNSProxyTestAddressCount;
19380 				me->synthesizedAAAA	= true;
19381 			}
19382 			else
19383 			{
19384 				me->answerCount		= 0;
19385 				me->synthesizedAAAA	= false;
19386 			}
19387 			ASPrintF( &me->qnameStr, "count-%u.ipv4.ttl-%u.tag-ipv4-only-aaaa-%s.d.test.",
19388 				kDNSProxyTestAddressCount, kDNSProxyTestRecordTTL, me->tag );
19389 			require_action( me->qnameStr, exit, err = kNoMemoryErr );
19390 			break;
19391 
19392 		case kDNSProxyTestQuery_AliasA:
19393 			me->qtype			= kDNSRecordType_A;
19394 			me->aliasCount		= kDNSProxyTestAliasCount;
19395 			me->answerCount		= kDNSProxyTestAddressCount;
19396 			me->responseCode	= kDNSRCode_NoError;
19397 			ASPrintF( &me->qnameStr, "alias-%u.count-%u.ttl-%u.tag-alias-a-%s.d.test.",
19398 				kDNSProxyTestAliasCount, kDNSProxyTestAddressCount, kDNSProxyTestRecordTTL, me->tag );
19399 			require_action( me->qnameStr, exit, err = kNoMemoryErr );
19400 			break;
19401 
19402 		case kDNSProxyTestQuery_AliasAAAA:
19403 			me->qtype			= kDNSRecordType_AAAA;
19404 			me->aliasCount		= kDNSProxyTestAliasCount;
19405 			me->answerCount		= kDNSProxyTestAddressCount;
19406 			me->responseCode	= kDNSRCode_NoError;
19407 			if( ( me->dns64PrefixBitLen > 0 ) && ( me->modeParam == kDNSProxyTestMode_ForceAAAASynthesis ) )
19408 			{
19409 				me->synthesizedAAAA = true;
19410 			}
19411 			else
19412 			{
19413 				me->synthesizedAAAA = false;
19414 			}
19415 			ASPrintF( &me->qnameStr, "alias-%u.count-%u.ttl-%u.tag-alias-aaaa-%s.d.test.",
19416 				kDNSProxyTestAliasCount, kDNSProxyTestAddressCount, kDNSProxyTestRecordTTL, me->tag );
19417 			require_action( me->qnameStr, exit, err = kNoMemoryErr );
19418 			break;
19419 
19420 		case kDNSProxyTestQuery_AliasIPv6OnlyA:
19421 			me->qtype			= kDNSRecordType_A;
19422 			me->aliasCount		= kDNSProxyTestAliasCount;
19423 			me->answerCount		= 0;
19424 			me->responseCode	= kDNSRCode_NoError;
19425 			ASPrintF( &me->qnameStr, "alias-%u.ipv6.ttl-%u.tag-alias-ipv6-only-a-%s.d.test.",
19426 				kDNSProxyTestAliasCount, kDNSProxyTestRecordTTL, me->tag );
19427 			require_action( me->qnameStr, exit, err = kNoMemoryErr );
19428 			break;
19429 
19430 		case kDNSProxyTestQuery_AliasIPv6OnlyAAAA:
19431 			me->qtype			= kDNSRecordType_AAAA;
19432 			me->aliasCount		= kDNSProxyTestAliasCount;
19433 			me->responseCode	= kDNSRCode_NoError;
19434 			if( ( me->dns64PrefixBitLen > 0 ) && ( me->modeParam == kDNSProxyTestMode_ForceAAAASynthesis ) )
19435 			{
19436 				me->answerCount	= 0;
19437 			}
19438 			else
19439 			{
19440 				me->answerCount	= kDNSProxyTestAddressCount;
19441 			}
19442 			ASPrintF( &me->qnameStr, "alias-%u.ipv6.count-%u.ttl-%u.tag-alias-ipv6-only-aaaa-%s.d.test.",
19443 				kDNSProxyTestAliasCount, kDNSProxyTestAddressCount, kDNSProxyTestRecordTTL, me->tag );
19444 			require_action( me->qnameStr, exit, err = kNoMemoryErr );
19445 			break;
19446 
19447 		case kDNSProxyTestQuery_AliasIPv4OnlyAAAA:
19448 			me->qtype			= kDNSRecordType_AAAA;
19449 			me->aliasCount		= kDNSProxyTestAliasCount;
19450 			me->responseCode	= kDNSRCode_NoError;
19451 			if( me->dns64PrefixBitLen > 0 )
19452 			{
19453 				me->answerCount		= kDNSProxyTestAddressCount;
19454 				me->synthesizedAAAA	= true;
19455 			}
19456 			else
19457 			{
19458 				me->answerCount		= 0;
19459 				me->synthesizedAAAA	= false;
19460 			}
19461 			ASPrintF( &me->qnameStr, "alias-%u.count-%u.ipv4.ttl-%u.tag-alias-ipv4-only-aaaa-%s.d.test.",
19462 				kDNSProxyTestAliasCount, kDNSProxyTestAddressCount, kDNSProxyTestRecordTTL, me->tag );
19463 			require_action( me->qnameStr, exit, err = kNoMemoryErr );
19464 			break;
19465 
19466 		case kDNSProxyTestQuery_NXDomainA:
19467 			me->qtype			= kDNSRecordType_A;
19468 			me->answerCount		= 0;
19469 			me->responseCode	= kDNSRCode_NXDomain;
19470 			ASPrintF( &me->qnameStr, "does-not-exist.tag-nx-domain-a-%s.d.test.", me->tag );
19471 			require_action( me->qnameStr, exit, err = kNoMemoryErr );
19472 			break;
19473 
19474 		case kDNSProxyTestQuery_NXDomainAAAA:
19475 			me->qtype			= kDNSRecordType_AAAA;
19476 			me->answerCount		= 0;
19477 			me->responseCode	= kDNSRCode_NXDomain;
19478 			ASPrintF( &me->qnameStr, "does-not-exist.tag-nx-domain-aaaa-%s.d.test.", me->tag );
19479 			require_action( me->qnameStr, exit, err = kNoMemoryErr );
19480 			break;
19481 
19482 		case kDNSProxyTestQuery_ReverseIPv6:
19483 		case kDNSProxyTestQuery_ReverseIPv6NXDomain:
19484 		{
19485 			unsigned int		hostID;
19486 			uint8_t				ipv6Addr[ 16 ];
19487 			char				reverseIPv6NameStr[ kReverseIPv6DomainNameBufLen ];
19488 
19489 			// To force mDNSResponder to have to send a query on the first query iteration, use a different reverse IPv6
19490 			// PTR query for each mode/DNS64 prefix/transport combination.
19491 
19492 			check_compile_time_code( kDNSProxyTestTransport_Count <= 4 );
19493 			check_compile_time_code( countof( kDNSProxyTestParams_DNS64Prefixes ) <= 8 );
19494 			check_compile_time_code( kDNSProxyTestMode_Count <= 4 );
19495 			hostID  =   ( (unsigned int) me->transportParam ) & 0x03;			// Set bits 1 - 0 to transport param.
19496 			hostID |= (   me->prefixParamIdx                  & 0x07 ) << 2;	// Set bits 4 - 2 to prefix param index.
19497 			hostID |= ( ( (unsigned int) me->modeParam )      & 0x03 ) << 5;	// Set bits 6 - 5 to mode param.
19498 			hostID |= 1U << 7;													// Set bit 7 to ensure a non-zero hostID.
19499 			check( ( hostID >= 1 ) && ( hostID <= 255 ) );
19500 
19501 			memcpy( ipv6Addr, kDNSServerBaseAddrV6, 16 );
19502 			ipv6Addr[ 15 ] = (uint8_t) hostID;
19503 
19504 			me->qtype = kDNSRecordType_PTR;
19505 			if( me->queryParam == kDNSProxyTestQuery_ReverseIPv6 )
19506 			{
19507 				char				answerNameStr[ 128 ];
19508 				char *				dst = answerNameStr;
19509 				char * const		lim = &answerNameStr[ countof( answerNameStr ) ];
19510 				int					i;
19511 
19512 				me->responseCode = kDNSRCode_NoError;
19513 
19514 				// Create the expected RDATA.
19515 
19516 				SNPrintF_Add( &dst, lim, "ipv6" );
19517 				for( i = 0; i < 8; ++i )
19518 				{
19519 					SNPrintF_Add( &dst, lim, "-%04x", ReadBig16( &ipv6Addr[ i * 2 ] ) );
19520 				}
19521 				SNPrintF_Add( &dst, lim, ".d.test." );
19522 				err = DomainNameFromString( tmpName, answerNameStr, NULL );
19523 				require_noerr_quiet( err, exit );
19524 
19525 				err = DomainNameDup( tmpName, &me->answerName, NULL );
19526 				require_noerr( err, exit );
19527 			}
19528 			else
19529 			{
19530 				me->responseCode	= kDNSRCode_NXDomain;
19531 				me->answerName		= NULL;
19532 
19533 				// Make the IPv6 address invalid by setting bits 15-8. This makes the host identifier bogus.
19534 				// The DNS server's IPv6 prefix is 96 bits, but only host identifiers in [1, 255] are recognized.
19535 
19536 				ipv6Addr[ 14 ] = 0xFF;
19537 			}
19538 			_WriteReverseIPv6DomainNameString( ipv6Addr, reverseIPv6NameStr );
19539 			me->qnameStr = strdup( reverseIPv6NameStr );
19540 			require_action( me->qnameStr, exit, err = kNoMemoryErr );
19541 			break;
19542 		}
19543 		case kDNSProxyTestQuery_ReverseIPv6DNS64:
19544 		case kDNSProxyTestQuery_ReverseIPv6DNS64NXDomain:
19545 		{
19546 			uint32_t	ipv4Addr;
19547 			uint8_t		ipv6Addr[ 16 ];
19548 			char		reverseIPNameStr[ kReverseIPv6DomainNameBufLen ];
19549 
19550 			if( me->dns64PrefixBitLen <= 0 )
19551 			{
19552 				skipQuery = true;
19553 				err = kNoErr;
19554 				goto exit;
19555 			}
19556 			me->qtype = kDNSRecordType_PTR;
19557 			if( me->queryParam == kDNSProxyTestQuery_ReverseIPv6DNS64 )
19558 			{
19559 				char		answerNameStr[ 64 ];
19560 
19561 				me->responseCode = kDNSRCode_NoError;
19562 
19563 				ipv4Addr = kDNSServerBaseAddrV4 + 1;
19564 				_WriteReverseIPv4DomainNameString( ipv4Addr, reverseIPNameStr );
19565 
19566 				err = DomainNameFromString( tmpName, reverseIPNameStr, NULL );
19567 				require_noerr_quiet( err, exit );
19568 
19569 				err = DomainNameDup( tmpName, &me->canonicalName, NULL );
19570 				require_noerr( err, exit );
19571 
19572 				SNPrintF( answerNameStr, sizeof( answerNameStr ), "ipv4-%u-%u-%u-%u.d.test.",
19573 					( ipv4Addr >> 24 ) & 0xFF,
19574 					( ipv4Addr >> 16 ) & 0xFF,
19575 					( ipv4Addr >>  8 ) & 0xFF,
19576 					  ipv4Addr         & 0xFF );
19577 				err = DomainNameFromString( tmpName, answerNameStr, NULL );
19578 				require_noerr_quiet( err, exit );
19579 
19580 				err = DomainNameDup( tmpName, &me->answerName, NULL );
19581 				require_noerr( err, exit );
19582 			}
19583 			else
19584 			{
19585 				ipv4Addr = kDNSServerBaseAddrV4 + 0;
19586 
19587 				me->responseCode	= kDNSRCode_NXDomain;
19588 				me->canonicalName	= NULL;
19589 				me->answerName		= NULL;
19590 			}
19591 			err = _DNSProxyTestSynthesizeIPv6( me->dns64Prefix, me->dns64PrefixBitLen, ipv4Addr, ipv6Addr );
19592 			require_noerr( err, exit );
19593 
19594 			_WriteReverseIPv6DomainNameString( ipv6Addr, reverseIPNameStr );
19595 			me->qnameStr = strdup( reverseIPNameStr );
19596 			require_action( me->qnameStr, exit, err = kNoMemoryErr );
19597 			break;
19598 		}
19599 		default:
19600 			FatalErrorF( "Unhandled DNSProxyTestQuery value %ld", (long) me->queryParam );
19601 	}
19602 	check( !me->qname );
19603 	err = DomainNameFromString( tmpName, me->qnameStr, NULL );
19604 	require_noerr_quiet( err, exit );
19605 
19606 	err = DomainNameDup( tmpName, &me->qname, NULL );
19607 	require_noerr( err, exit );
19608 
19609 	check( !me->querier );
19610 	me->querier = mdns_resolver_create_querier( me->resolver, &err );
19611 	require_noerr( err, exit );
19612 
19613 	err = mdns_querier_set_query( me->querier, me->qname, me->qtype, kDNSClassType_IN );
19614 	require_noerr( err, exit );
19615 
19616 	mdns_querier_set_queue( me->querier, me->queue );
19617 	_DNSProxyTestRetain( me );
19618 	querier = me->querier;
19619 	mdns_retain( querier );
19620 	mdns_querier_set_result_handler( me->querier,
19621 	^{
19622 		if( me->querier == querier ) _DNSProxyTestHandleQuerierResult( me );
19623 		_DNSProxyTestRelease( me );
19624 		mdns_release( querier );
19625 	} );
19626 	mdns_querier_activate( me->querier );
19627 
19628 	check( !me->timer );
19629 	err = DispatchTimerOneShotCreate( dispatch_time_seconds( kDNSProxyTestQuerierTimeLimitSecs ),
19630 		kDNSProxyTestQuerierTimeLimitSecs * ( UINT64_C_safe( kNanosecondsPerSecond ) / 10 ),
19631 		me->queue, _DNSProxyTestQuerierTimerHandler, me, &me->timer );
19632 	require_noerr( err, exit );
19633 	dispatch_resume( me->timer );
19634 
19635 exit:
19636 	if( outSkipQuery ) *outSkipQuery = skipQuery;
19637 	return( err );
19638 }
19639 
_DNSProxyTestQuerierTimerHandler(void * inCtx)19640 static void	_DNSProxyTestQuerierTimerHandler( void *inCtx )
19641 {
19642 	OSStatus					err;
19643 	const DNSProxyTestRef		me = (DNSProxyTestRef) inCtx;
19644 	Boolean						done;
19645 
19646 	dpt_ulog( kLogLevelInfo, "Query for '%{du:dname}' timed out.\n", me->qname );
19647 
19648 	err = _DNSProxyTestContinue( me, kTimeoutErr, &done );
19649 	check_noerr( err );
19650 	if( err || done ) _DNSProxyTestStop( me, err );
19651 }
19652 
19653 //===========================================================================================================================
19654 
19655 static OSStatus
_DNSProxyTestSynthesizeIPv6(const uint8_t * inIPv6Prefix,int inIPv6PrefixBitLen,uint32_t inIPv4Addr,uint8_t outIPv6Addr[STATIC_PARAM16])19656 	_DNSProxyTestSynthesizeIPv6(
19657 		const uint8_t *	inIPv6Prefix,
19658 		int				inIPv6PrefixBitLen,
19659 		uint32_t		inIPv4Addr,
19660 		uint8_t			outIPv6Addr[ STATIC_PARAM 16 ] )
19661 {
19662 	// From <https://tools.ietf.org/html/rfc6052#section-2.2>:
19663 	//
19664 	// 2.2.  IPv4-Embedded IPv6 Address Format
19665 	//
19666 	//   IPv4-converted IPv6 addresses and IPv4-translatable IPv6 addresses
19667 	//   follow the same format, described here as the IPv4-embedded IPv6
19668 	//   address Format.  IPv4-embedded IPv6 addresses are composed of a
19669 	//   variable-length prefix, the embedded IPv4 address, and a variable-
19670 	//   length suffix, as presented in the following diagram, in which PL
19671 	//   designates the prefix length:
19672 	//
19673 	//    +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
19674 	//    |PL| 0-------------32--40--48--56--64--72--80--88--96--104---------|
19675 	//    +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
19676 	//    |32|     prefix    |v4(32)         | u | suffix                    |
19677 	//    +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
19678 	//    |40|     prefix        |v4(24)     | u |(8)| suffix                |
19679 	//    +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
19680 	//    |48|     prefix            |v4(16) | u | (16)  | suffix            |
19681 	//    +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
19682 	//    |56|     prefix                |(8)| u |  v4(24)   | suffix        |
19683 	//    +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
19684 	//    |64|     prefix                    | u |   v4(32)      | suffix    |
19685 	//    +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
19686 	//    |96|     prefix                                    |    v4(32)     |
19687 	//    +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
19688 	//
19689 	//                                 Figure 1
19690 	switch( inIPv6PrefixBitLen )
19691 	{
19692 		case 32:
19693 		case 40:
19694 		case 48:
19695 		case 56:
19696 		case 64:
19697 		case 96:
19698 		{
19699 			const int		prefixLen = inIPv6PrefixBitLen / 8;
19700 			int				i, j;
19701 			uint8_t			v4Addr[ 4 ];
19702 
19703 			memcpy( outIPv6Addr, inIPv6Prefix, (size_t) prefixLen );
19704 			WriteBig32( v4Addr, inIPv4Addr );
19705 
19706 			// 1. Bits 64 - 71, i.e., reserved octet "u", MUST be zero.
19707 			// 2. Except for bits 64 - 71, the 32 bits following the prefix are the bits of the embedded IPv4 address.
19708 			// 3. The remaining bits, if any, are the suffix bits, which SHOULD be zero.
19709 
19710 			j = 0;
19711 			for( i = prefixLen; i < 16; ++i )
19712 			{
19713 				if( ( j < 4 ) && ( i != 8 ) )	outIPv6Addr[ i ] = v4Addr[ j++ ];
19714 				else							outIPv6Addr[ i ] = 0;
19715 			}
19716 			return( kNoErr );
19717 		}
19718 		default:
19719 			return( kSizeErr );
19720 	}
19721 }
19722 
19723 //===========================================================================================================================
19724 
_DNSProxyTestHandleQuerierResult(DNSProxyTestRef me)19725 static void	_DNSProxyTestHandleQuerierResult( DNSProxyTestRef me )
19726 {
19727 	OSStatus								err, verifyErr;
19728 	const uint8_t *							msgPtr;
19729 	size_t									msgLen;
19730 	const mdns_querier_result_type_t		resultType	= mdns_querier_get_result_type( me->querier );
19731 	Boolean									done		= false;
19732 
19733 	if( resultType == mdns_querier_result_type_response )
19734 	{
19735 		msgPtr = mdns_querier_get_response_ptr( me->querier );
19736 		msgLen = mdns_querier_get_response_length( me->querier );
19737 		dpt_ulog( kLogLevelInfo, "Querier response: %.1{du:dnsmsg}\n", msgPtr, msgLen );
19738 	}
19739 	else
19740 	{
19741 		if( resultType == mdns_querier_result_type_error )
19742 		{
19743 			err = mdns_querier_get_error( me->querier );
19744 			if( !err ) err = kUnknownErr;
19745 		}
19746 		else
19747 		{
19748 			err = kUnexpectedErr;
19749 		}
19750 		dpt_ulog( kLogLevelError, "Querier result: %s, error: %#m\n", mdns_querier_get_result_type( me->querier ), err );
19751 		goto exit;
19752 	}
19753 	switch( me->queryParam )
19754 	{
19755 		case kDNSProxyTestQuery_A:
19756 		case kDNSProxyTestQuery_AAAA:
19757 		case kDNSProxyTestQuery_IPv6OnlyA:
19758 		case kDNSProxyTestQuery_IPv6OnlyAAAA:
19759 		case kDNSProxyTestQuery_IPv4OnlyAAAA:
19760 		case kDNSProxyTestQuery_AliasA:
19761 		case kDNSProxyTestQuery_AliasAAAA:
19762 		case kDNSProxyTestQuery_AliasIPv6OnlyA:
19763 		case kDNSProxyTestQuery_AliasIPv6OnlyAAAA:
19764 		case kDNSProxyTestQuery_AliasIPv4OnlyAAAA:
19765 		case kDNSProxyTestQuery_NXDomainA:
19766 		case kDNSProxyTestQuery_NXDomainAAAA:
19767 			verifyErr = _DNSProxyTestVerifyAddressResponse( msgPtr, msgLen, me->qname, me->qtype, me->responseCode,
19768 				me->aliasCount, me->answerCount,
19769 				me->synthesizedAAAA ? me->dns64Prefix : NULL,
19770 				me->synthesizedAAAA ? me->dns64PrefixBitLen : 0 );
19771 			break;
19772 
19773 		case kDNSProxyTestQuery_ReverseIPv6:
19774 		case kDNSProxyTestQuery_ReverseIPv6NXDomain:
19775 		case kDNSProxyTestQuery_ReverseIPv6DNS64:
19776 		case kDNSProxyTestQuery_ReverseIPv6DNS64NXDomain:
19777 			verifyErr = _DNSProxyTestVerifyReverseIPv6Response( msgPtr, msgLen, me->qname, me->responseCode,
19778 				me->canonicalName, me->answerName );
19779 			break;
19780 
19781 		default:
19782 			FatalErrorF( "Unhandled DNSProxyTestQuery value %ld", (long) me->queryParam );
19783 	}
19784 	err = _DNSProxyTestContinue( me, verifyErr, &done );
19785 	require_noerr( err, exit );
19786 
19787 exit:
19788 	if( err || done ) _DNSProxyTestStop( me, err );
19789 }
19790 
19791 //===========================================================================================================================
19792 
19793 static OSStatus
_DNSProxyTestVerifyAddressResponse(const uint8_t * inMsgPtr,size_t inMsgLen,const uint8_t * inQName,uint16_t inQType,int inResponseCode,unsigned int inAliasCount,unsigned int inAnswerCount,const uint8_t * inDNS64Prefix,int inDNS64PrefixBitLen)19794 	_DNSProxyTestVerifyAddressResponse(
19795 		const uint8_t *	inMsgPtr,
19796 		size_t			inMsgLen,
19797 		const uint8_t *	inQName,
19798 		uint16_t		inQType,
19799 		int				inResponseCode,
19800 		unsigned int	inAliasCount,
19801 		unsigned int	inAnswerCount,
19802 		const uint8_t *	inDNS64Prefix,
19803 		int				inDNS64PrefixBitLen )
19804 {
19805 	OSStatus				err;
19806 	const DNSHeader *		hdr;
19807 	const uint8_t *			ptr;
19808 	const uint8_t *			answerSection;
19809 	unsigned int			flags, qCount, answerCount;
19810 	int						rcode;
19811 	uint16_t				qtype, qclass;
19812 	uint8_t					qname[ kDomainNameLengthMax ];
19813 
19814 	require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kMalformedErr );
19815 
19816 	hdr		= (const DNSHeader *) inMsgPtr;
19817 	flags	= DNSHeaderGetFlags( hdr );
19818 	rcode	= DNSFlagsGetRCode( flags );
19819 	require_action_quiet( rcode == inResponseCode, exit, err = kValueErr );
19820 
19821 	qCount = DNSHeaderGetQuestionCount( hdr );
19822 	require_action_quiet( qCount == 1, exit, err = kCountErr );
19823 
19824 	ptr = (const uint8_t *) &hdr[ 1 ];
19825 	err = DNSMessageExtractQuestion( inMsgPtr, inMsgLen, ptr, qname, &qtype, &qclass, &ptr );
19826 	require_noerr_quiet( err, exit );
19827 	require_action_quiet( DomainNameEqual( qname, inQName ), exit, err = kNameErr );
19828 	require_action_quiet( qtype  == inQType, exit, err = kTypeErr );
19829 	require_action_quiet( qclass == kDNSClassType_IN, exit, kTypeErr );
19830 
19831 	answerCount = DNSHeaderGetAnswerCount( hdr );
19832 	require_action_quiet( answerCount == ( inAliasCount + inAnswerCount ), exit, err = kCountErr );
19833 
19834 	answerSection = ptr;
19835 	if( inAliasCount > 0 )
19836 	{
19837 		unsigned int		i;
19838 		const uint8_t *		parentDomain;
19839 		uint8_t				target[ kDomainNameLengthMax ];
19840 
19841 		parentDomain = DomainNameGetNextLabel( inQName );
19842 		require_fatal( parentDomain, "Invalid qname '%{du:dname}' for non-zero alias count.", inQName );
19843 
19844 		target[ 0 ] = 0;
19845 		err = DomainNameAppendDomainName( target, inQName, NULL );
19846 		require_noerr( err, exit );
19847 
19848 		for( i = 0; i < inAliasCount; ++i )
19849 		{
19850 			unsigned int			j;
19851 			const unsigned int		aliasNumber	= inAliasCount - i;
19852 			Boolean					foundCNAME	= false;
19853 
19854 			ptr = answerSection;
19855 			for( j = 0; j < answerCount; ++j )
19856 			{
19857 				const uint8_t *		rdataPtr;
19858 				unsigned int		nextAliasNumber;
19859 				uint16_t			type;
19860 				uint16_t			class;
19861 				uint8_t				name[ kDomainNameLengthMax ];
19862 				char				aliasLabelStr[ 32 ];
19863 
19864 				err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, NULL, &rdataPtr, NULL, &ptr );
19865 				require_noerr( err, exit );
19866 
19867 				if( type  != kDNSRecordType_CNAME )		continue;
19868 				if( class != kDNSClassType_IN )		continue;
19869 				if( !DomainNameEqual( name, target ) )	continue;
19870 
19871 				target[ 0 ] = 0;
19872 				nextAliasNumber = aliasNumber - 1;
19873 				if( nextAliasNumber >= 2 )
19874 				{
19875 					SNPrintF( aliasLabelStr, sizeof( aliasLabelStr ), "alias-%u", nextAliasNumber );
19876 					err = DomainNameAppendString( target, aliasLabelStr, NULL );
19877 					require_noerr( err, exit );
19878 				}
19879 				else if( nextAliasNumber == 1 )
19880 				{
19881 					SNPrintF( aliasLabelStr, sizeof( aliasLabelStr ), "alias" );
19882 					err = DomainNameAppendString( target, aliasLabelStr, NULL );
19883 					require_noerr( err, exit );
19884 				}
19885 				err = DomainNameAppendDomainName( target, parentDomain, NULL );
19886 				require_noerr( err, exit );
19887 
19888 				err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, rdataPtr, name, NULL );
19889 				require_noerr( err, exit );
19890 
19891 				if( !DomainNameEqual( name, target ) ) continue;
19892 				foundCNAME = true;
19893 				break;
19894 			}
19895 			require_action_quiet( foundCNAME, exit, err = kNotFoundErr );
19896 		}
19897 	}
19898 	if( inAnswerCount > 0 )
19899 	{
19900 		const uint8_t *		target;
19901 		unsigned int		i;
19902 
19903 		if( inAliasCount > 0 )
19904 		{
19905 			target = DomainNameGetNextLabel( inQName );
19906 			require_fatal( target, "Invalid qname '%{du:dname}' for non-zero alias count.", inQName );
19907 		}
19908 		else
19909 		{
19910 			target = inQName;
19911 		}
19912 		for( i = 0; i < inAnswerCount; ++i )
19913 		{
19914 			unsigned int		j;
19915 			size_t				expectedLen;
19916 			uint8_t				expectedData[ 16 ];
19917 			Boolean				foundRecord = false;
19918 
19919 			if( inQType == kDNSRecordType_A )
19920 			{
19921 				const uint32_t		ipv4Addr = kDNSServerBaseAddrV4 + ( i + 1 );
19922 
19923 				WriteBig32( expectedData, ipv4Addr );
19924 				expectedLen = 4;
19925 			}
19926 			else
19927 			{
19928 				if( inDNS64PrefixBitLen != 0 )
19929 				{
19930 					const uint32_t		ipv4Addr = kDNSServerBaseAddrV4 + ( i + 1 );
19931 
19932 					err = _DNSProxyTestSynthesizeIPv6( inDNS64Prefix, inDNS64PrefixBitLen, ipv4Addr, expectedData );
19933 					require_noerr( err, exit );
19934 				}
19935 				else
19936 				{
19937 					memcpy( expectedData, kDNSServerBaseAddrV6, 16 );
19938 					expectedData[ 15 ] = (uint8_t)( i + 1 );
19939 				}
19940 				expectedLen = 16;
19941 			}
19942 			ptr = answerSection;
19943 			for( j = 0; j < answerCount; ++j )
19944 			{
19945 				const uint8_t *		rdataPtr;
19946 				size_t				rdataLen;
19947 				uint16_t			type;
19948 				uint16_t			class;
19949 				uint8_t				name[ kDomainNameLengthMax ];
19950 
19951 				err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, NULL, &rdataPtr, &rdataLen,
19952 					&ptr );
19953 				require_noerr( err, exit );
19954 
19955 				if( type  != inQType )												continue;
19956 				if( class != kDNSClassType_IN )										continue;
19957 				if( !DomainNameEqual( name, target ) )								continue;
19958 				if( !MemEqual( rdataPtr, rdataLen, expectedData, expectedLen ) )	continue;
19959 				foundRecord = true;
19960 				break;
19961 			}
19962 			require_action_quiet( foundRecord, exit, err = kNotFoundErr );
19963 		}
19964 	}
19965 
19966 exit:
19967 	return( err );
19968 }
19969 
19970 //===========================================================================================================================
19971 
19972 static OSStatus
19973 	_DNSProxyTestFindDomainNameRecord(
19974 		const uint8_t *	inMsgPtr,
19975 		size_t			inMsgLen,
19976 		const uint8_t *	inRecordSection,
19977 		unsigned int	inRecordCount,
19978 		const uint8_t *	inRecordName,
19979 		uint16_t		inRecordType,
19980 		const uint8_t *	inRDataName );
19981 
19982 static OSStatus
_DNSProxyTestVerifyReverseIPv6Response(const uint8_t * inMsgPtr,size_t inMsgLen,const uint8_t * inQName,int inResponseCode,const uint8_t * inCanonicalName,const uint8_t * inAnswerName)19983 	_DNSProxyTestVerifyReverseIPv6Response(
19984 		const uint8_t *	inMsgPtr,
19985 		size_t			inMsgLen,
19986 		const uint8_t *	inQName,
19987 		int				inResponseCode,
19988 		const uint8_t *	inCanonicalName,
19989 		const uint8_t *	inAnswerName )
19990 {
19991 	OSStatus				err;
19992 	const DNSHeader *		hdr;
19993 	const uint8_t *			ptr;
19994 	const uint8_t *			answerSection;
19995 	const uint8_t *			ownerName;
19996 	unsigned int			flags, qCount, answerCount, answerCountExpected;
19997 	int						rcode;
19998 	uint16_t				qtype, qclass;
19999 	uint8_t					qname[ kDomainNameLengthMax ];
20000 
20001 	require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kMalformedErr );
20002 
20003 	hdr		= (const DNSHeader *) inMsgPtr;
20004 	flags	= DNSHeaderGetFlags( hdr );
20005 	rcode	= DNSFlagsGetRCode( flags );
20006 	require_action_quiet( rcode == inResponseCode, exit, err = kValueErr );
20007 
20008 	qCount = DNSHeaderGetQuestionCount( hdr );
20009 	require_action_quiet( qCount == 1, exit, err = kCountErr );
20010 
20011 	ptr = (const uint8_t *) &hdr[ 1 ];
20012 	err = DNSMessageExtractQuestion( inMsgPtr, inMsgLen, ptr, qname, &qtype, &qclass, &ptr );
20013 	require_noerr_quiet( err, exit );
20014 	require_action_quiet( DomainNameEqual( qname, inQName ), exit, err = kNameErr );
20015 	require_action_quiet( qtype  == kDNSRecordType_PTR, exit, err = kTypeErr );
20016 	require_action_quiet( qclass == kDNSClassType_IN, exit, kTypeErr );
20017 
20018 	answerCountExpected	= ( inCanonicalName ? 1 : 0 ) + ( inAnswerName ? 1 : 0 );
20019 	answerCount			= DNSHeaderGetAnswerCount( hdr );
20020 	require_action_quiet( answerCount == answerCountExpected, exit, err = kCountErr );
20021 
20022 	answerSection	= ptr;
20023 	ownerName		= inQName;
20024 	if( inCanonicalName )
20025 	{
20026 		err = _DNSProxyTestFindDomainNameRecord( inMsgPtr, inMsgLen, answerSection, answerCount, ownerName,
20027 			kDNSRecordType_CNAME, inCanonicalName );
20028 		require_noerr( err, exit );
20029 
20030 		ownerName = inCanonicalName;
20031 	}
20032 	if( inAnswerName )
20033 	{
20034 		err = _DNSProxyTestFindDomainNameRecord( inMsgPtr, inMsgLen, answerSection, answerCount, ownerName,
20035 			kDNSRecordType_PTR, inAnswerName );
20036 		require_noerr( err, exit );
20037 	}
20038 
20039 exit:
20040 	return( err );
20041 }
20042 
20043 static OSStatus
_DNSProxyTestFindDomainNameRecord(const uint8_t * inMsgPtr,size_t inMsgLen,const uint8_t * inRecordSection,unsigned int inRecordCount,const uint8_t * inRecordName,uint16_t inRecordType,const uint8_t * inRDataName)20044 	_DNSProxyTestFindDomainNameRecord(
20045 		const uint8_t *	inMsgPtr,
20046 		size_t			inMsgLen,
20047 		const uint8_t *	inRecordSection,
20048 		unsigned int	inRecordCount,
20049 		const uint8_t *	inRecordName,
20050 		uint16_t		inRecordType,
20051 		const uint8_t *	inRDataName )
20052 {
20053 	OSStatus			err;
20054 	const uint8_t *		ptr;
20055 	unsigned int		i;
20056 	Boolean				found = false;
20057 
20058 	ptr = inRecordSection;
20059 	for( i = 0; i < inRecordCount; ++i )
20060 	{
20061 		const uint8_t *		rdataPtr;
20062 		uint16_t			type;
20063 		uint16_t			class;
20064 		uint8_t				name[ kDomainNameLengthMax ];
20065 
20066 		err = DNSMessageExtractRecord( inMsgPtr, inMsgLen, ptr, name, &type, &class, NULL, &rdataPtr, NULL, &ptr );
20067 		require_noerr( err, exit );
20068 
20069 		if( type  != inRecordType )						continue;
20070 		if( class != kDNSClassType_IN )					continue;
20071 		if( !DomainNameEqual( name, inRecordName ) )	continue;
20072 
20073 		err = DNSMessageExtractDomainName( inMsgPtr, inMsgLen, rdataPtr, name, NULL );
20074 		require_noerr( err, exit );
20075 
20076 		if( !DomainNameEqual( name, inRDataName ) ) continue;
20077 		found = true;
20078 		break;
20079 	}
20080 	err = found ? kNoErr : kNotFoundErr;
20081 
20082 exit:
20083 	return( err );
20084 }
20085 
20086 //===========================================================================================================================
20087 
20088 static void DNSSD_API
_DNSProxyTestProbeGAICallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inHostname,const struct sockaddr * inSockAddr,uint32_t inTTL,void * inCtx)20089 	_DNSProxyTestProbeGAICallback(
20090 		DNSServiceRef			inSDRef,
20091 		DNSServiceFlags			inFlags,
20092 		uint32_t				inInterfaceIndex,
20093 		DNSServiceErrorType		inError,
20094 		const char *			inHostname,
20095 		const struct sockaddr *	inSockAddr,
20096 		uint32_t				inTTL,
20097 		void *					inCtx )
20098 {
20099 	OSStatus					err;
20100 	const DNSProxyTestRef		me		= (DNSProxyTestRef) inCtx;
20101 	Boolean						done	= false;
20102 
20103 	Unused( inSDRef );
20104 	Unused( inInterfaceIndex );
20105 	Unused( inHostname );
20106 	Unused( inTTL );
20107 
20108 	if( ( inFlags & kDNSServiceFlagsAdd ) && !inError )
20109 	{
20110 		DNSServiceForget( &me->probeGAI );
20111 		dispatch_source_forget( &me->timer );
20112 
20113 		dpt_ulog( kLogLevelInfo, "Probe: Got GAI address %##a for %s\n", inSockAddr, me->probeHostname );
20114 
20115 		err = _DNSProxyTestContinue( me, kNoErr, &done );
20116 		require_noerr( err, exit );
20117 	}
20118 	err = kNoErr;
20119 
20120 exit:
20121 	if( err || done ) _DNSProxyTestStop( me, err );
20122 }
20123 
20124 //===========================================================================================================================
20125 
_DNSProxyTestProbeTimerHandler(void * inCtx)20126 static void	_DNSProxyTestProbeTimerHandler( void *inCtx )
20127 {
20128 	const DNSProxyTestRef		me = (DNSProxyTestRef) inCtx;
20129 
20130 	dpt_ulog( kLogLevelInfo, "Probe: GAI request for '%s' timed out.\n", me->probeHostname );
20131 	_DNSProxyTestStop( me, kNotPreparedErr );
20132 }
20133 
20134 //===========================================================================================================================
20135 //	RCodeTestCmd
20136 //===========================================================================================================================
20137 
20138 #define kRCodeTestMaxRCodeValue		15
20139 
20140 typedef struct RCodeTest *		RCodeTestRef;
20141 struct RCodeTest
20142 {
20143 	dispatch_queue_t			queue;				// Serial queue for test events.
20144 	dispatch_semaphore_t		doneSem;			// Semaphore to signal when the test is done.
20145 	dnssd_getaddrinfo_t			gai;				// Current subtest's GAI object. (Also used for probing test DNS server.)
20146 	dispatch_source_t			timer;				// Timer for enforcing time limit on current dnssd_getaddrinfo.
20147 	CFMutableDictionaryRef		report;				// Test's report, as a plist.
20148 	CFMutableArrayRef			subtestResults;		// Pointer to report's subtest results.
20149 	CFMutableArrayRef			gaiResults;			// Array of dnssd_getaddrinfo_result objects for the current GAI.
20150 	char *						hostname;			// Current subtest's hostname. (Also used for probing test DNS server.)
20151 	char *						description;		// Current subtest description.
20152 	NanoTime64					startTime;			// Current subtest's start time.
20153 	pid_t						serverPID;			// PID of spawned test DNS server.
20154 	OSStatus					error;				// Current test error.
20155 	int32_t						refCount;			// Test's reference count.
20156 	int							rcode;				// Argument to use in current subtest's hostname's RCODE label.
20157 	int							indexIdx;			// Index of argument to use in current subtest's hostname's Index label.
20158 	int							subtestCount;		// Number of subtests that have completed or are in progress.
20159 	int							subtestPassCount;	// Number of subtests that have passed so far.
20160 	Boolean						hostnameIsAlias;	// True if current subtest's hostname is an alias.
20161 	Boolean						hostnameHasAddr;	// True if current subtest's hostname that has an IPv4 address.
20162 	Boolean						done;				// True if all subtests have completed.
20163 };
20164 
20165 ulog_define_ex( kDNSSDUtilIdentifier, RCodeTest, kLogLevelInfo, kLogFlags_None, "RCodeTest", NULL );
20166 #define rct_ulog( LEVEL, ... )		ulog( &log_category_from_name( RCodeTest ), (LEVEL), __VA_ARGS__ )
20167 
20168 static OSStatus	_RCodeTestCreate( RCodeTestRef *outTest );
20169 static OSStatus	_RCodeTestRun( RCodeTestRef inTest, Boolean *outPassed );
20170 static void		_RCodeTestRetain( RCodeTestRef inTest );
20171 static void		_RCodeTestRelease( RCodeTestRef inTest );
20172 
RCodeTestCmd(void)20173 static void	RCodeTestCmd( void )
20174 {
20175 	OSStatus				err;
20176 	OutputFormatType		outputFormat;
20177 	RCodeTestRef			test	= NULL;
20178 	Boolean					passed	= false;
20179 
20180 	err = CheckRootUser();
20181 	require_noerr_quiet( err, exit );
20182 
20183 	err = OutputFormatFromArgString( gRCodeTest_OutputFormat, &outputFormat );
20184 	require_noerr_quiet( err, exit );
20185 
20186 	err = _RCodeTestCreate( &test );
20187 	require_noerr( err, exit );
20188 
20189 	err = _RCodeTestRun( test, &passed );
20190 	require_noerr( err, exit );
20191 
20192 	err = OutputPropertyList( test->report, outputFormat, gRCodeTest_OutputFilePath );
20193 	require_noerr( err, exit );
20194 
20195 exit:
20196 	if( test ) _RCodeTestRelease( test );
20197     gExitCode = err ? 1 : ( passed ? 0 : 2 );
20198 }
20199 
20200 //===========================================================================================================================
20201 
_RCodeTestCreate(RCodeTestRef * outTest)20202 static OSStatus	_RCodeTestCreate( RCodeTestRef *outTest )
20203 {
20204 	OSStatus			err;
20205 	RCodeTestRef		obj;
20206 
20207 	obj = (RCodeTestRef) calloc( 1, sizeof( *obj ) );
20208 	require_action( obj, exit, err = kNoMemoryErr );
20209 
20210 	obj->refCount	= 1;
20211 	obj->error		= kInProgressErr;
20212 	obj->serverPID	= -1;
20213 
20214 	obj->queue = dispatch_queue_create( "com.apple.dnssdutil.rcode-test", DISPATCH_QUEUE_SERIAL );
20215 	require_action( obj->queue, exit, err = kNoResourcesErr );
20216 
20217 	obj->doneSem = dispatch_semaphore_create( 0 );
20218 	require_action( obj->doneSem, exit, err = kNoResourcesErr );
20219 
20220 	obj->report = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
20221 	require_action( obj->report, exit, err = kNoMemoryErr );
20222 
20223 	obj->indexIdx = -1;
20224 	*outTest = obj;
20225 	obj = NULL;
20226 	err = kNoErr;
20227 
20228 exit:
20229 	if( obj ) _RCodeTestRelease( obj );
20230 	return( err );
20231 }
20232 
20233 //===========================================================================================================================
20234 
20235 static void		_RCodeTestStart( void *inCtx );
20236 static void		_RCodeTestStop( RCodeTestRef inTest, OSStatus inError );
20237 static OSStatus	_RCodeTestStartSubtest( RCodeTestRef inTest );
20238 static OSStatus	_RCodeTestContinue( RCodeTestRef inType, OSStatus inSubtestError, Boolean *outDone );
20239 static Boolean	_RCodeTestRCodeIsGood( const int inRCode );
20240 
_RCodeTestRun(RCodeTestRef me,Boolean * outPassed)20241 static OSStatus	_RCodeTestRun( RCodeTestRef me, Boolean *outPassed )
20242 {
20243 	Boolean		passed;
20244 
20245 	dispatch_async_f( me->queue, me, _RCodeTestStart );
20246 	dispatch_semaphore_wait( me->doneSem, DISPATCH_TIME_FOREVER );
20247 
20248 	passed = ( !me->error && ( me->subtestPassCount == me->subtestCount ) ) ? true : false;
20249 	CFDictionarySetBoolean( me->report, CFSTR( "pass" ), passed );
20250 	rct_ulog( kLogLevelInfo, "Test result: %s\n", passed ? "pass" : "fail" );
20251 
20252 	if( outPassed ) *outPassed = passed;
20253 	return( me->error );
20254 }
20255 
20256 //===========================================================================================================================
20257 
_RCodeTestRetain(const RCodeTestRef me)20258 static void	_RCodeTestRetain( const RCodeTestRef me )
20259 {
20260 	atomic_add_32( &me->refCount, 1 );
20261 }
20262 
20263 //===========================================================================================================================
20264 
_RCodeTestRelease(const RCodeTestRef me)20265 static void	_RCodeTestRelease( const RCodeTestRef me )
20266 {
20267 	if( atomic_add_and_fetch_32( &me->refCount, -1 ) == 0 )
20268 	{
20269 		check( !me->gai );
20270 		check( !me->timer );
20271 		check( !me->subtestResults );
20272 		check( !me->gaiResults );
20273 		check( !me->hostname );
20274 		check( !me->description );
20275 		check( me->serverPID < 0 );
20276 		dispatch_forget( &me->queue );
20277 		dispatch_forget( &me->doneSem );
20278 		ForgetCF( &me->report );
20279 		free( me );
20280 	}
20281 }
20282 
20283 //===========================================================================================================================
20284 
20285 #define kRCodeTestProbeQueryTimeoutSecs		5
20286 
20287 static void	_RCodeTestHandleGAIProbeResults( RCodeTestRef inTest, dnssd_getaddrinfo_result_t *inResults, size_t inCount );
20288 static void	_RCodeTestProbeQueryTimerHandler( void *inCtx );
20289 
_RCodeTestStart(void * const inCtx)20290 static void	_RCodeTestStart( void * const inCtx )
20291 {
20292 	OSStatus				err;
20293 	const RCodeTestRef		me			= (RCodeTestRef) inCtx;
20294 	char *					serverCmd	= NULL;
20295 	dnssd_getaddrinfo_t		gai;
20296 	NanoTime64				startTime;
20297 	char					startTimeStr[ 32 ];
20298 	char					tag[ 6 + 1 ];
20299 
20300 	startTime = NanoTimeGetCurrent();
20301 	rct_ulog( kLogLevelInfo, "Starting test\n" );
20302 
20303 	// The "dnssdutil server" command will create a resolver entry for the server's "d.test." domain containing an array
20304 	// of the server's IP addresses. Because configd favors IPv6 addresses, when there's a mix of IPv4 and IPv6
20305 	// addresses, configd may rearrange the array in order to ensure that IPv6 addresses come before the IPv4 addresses.
20306 	// To preserve the original address order, the server is specified to run in IPv6-only mode. This way,
20307 	// mDNSResponder's view of the address will be such that address with index value 1 is first, address with index
20308 	// value 2 is second, etc.
20309 
20310 	serverCmd = NULL;
20311 	ASPrintF( &serverCmd,
20312 		"dnssdutil server --loopback --follow %lld --responseDelay 20 --ipv6 --extraIPv6 3", (int64_t) getpid() );
20313 	require_action_quiet( serverCmd, exit, err = kNoMemoryErr );
20314 
20315 	err = _SpawnCommand( &me->serverPID, "/dev/null", "/dev/null", "%s", serverCmd );
20316 	require_noerr( err, exit );
20317 
20318 	check( !me->hostname );
20319 	me->hostname = NULL;
20320 	ASPrintF( &me->hostname, "tag-rcode-test-probe-%s.ipv4.d.test.",
20321 		_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
20322 	require_action( me->hostname, exit, err = kNoMemoryErr );
20323 
20324 	check( !me->gai );
20325 	me->gai = dnssd_getaddrinfo_create();
20326 	require_action( me->gai, exit, err = kNoResourcesErr );
20327 
20328 	dnssd_getaddrinfo_set_hostname( me->gai, me->hostname );
20329 	dnssd_getaddrinfo_set_flags( me->gai, 0 );
20330 	dnssd_getaddrinfo_set_interface_index( me->gai, kDNSServiceInterfaceIndexAny );
20331 	dnssd_getaddrinfo_set_protocols( me->gai, kDNSServiceProtocol_IPv4 );
20332 	dnssd_getaddrinfo_set_queue( me->gai, me->queue );
20333 	gai = me->gai;
20334 	dnssd_retain( gai );
20335 	_RCodeTestRetain( me );
20336 	dnssd_getaddrinfo_set_result_handler( me->gai,
20337 	^( dnssd_getaddrinfo_result_t * const inResults, const size_t inCount )
20338 	{
20339 		require_return( me->gai == gai );
20340 		_RCodeTestHandleGAIProbeResults( me, inResults, inCount );
20341 	} );
20342 	dnssd_getaddrinfo_set_event_handler( me->gai,
20343 	^( const dnssd_event_t inEvent, const DNSServiceErrorType inGAIError )
20344 	{
20345 		if( inEvent == dnssd_event_invalidated )
20346 		{
20347 			dnssd_release( gai );
20348 			_RCodeTestRelease( me );
20349 		}
20350 		else if( inEvent == dnssd_event_error )
20351 		{
20352 			require_return( me->gai == gai );
20353 			rct_ulog( kLogLevelError, "dnssd_getaddrinfo error: %#m\n", inGAIError );
20354 			_RCodeTestStop( me, inGAIError );
20355 		}
20356 	} );
20357 	dnssd_getaddrinfo_activate( me->gai );
20358 
20359 	check( !me->timer );
20360 	err = DispatchTimerOneShotCreate( dispatch_time_seconds( kRCodeTestProbeQueryTimeoutSecs ),
20361 		kRCodeTestProbeQueryTimeoutSecs * ( UINT64_C_safe( kNanosecondsPerSecond ) / 10 ),
20362 		me->queue, _RCodeTestProbeQueryTimerHandler, me, &me->timer );
20363 	require_noerr( err, exit );
20364 	dispatch_resume( me->timer );
20365 
20366 	_NanoTime64ToTimestamp( startTime, startTimeStr, sizeof( startTimeStr ) );
20367 	err = CFPropertyListAppendFormatted( kCFAllocatorDefault, me->report,
20368 		"%kO=%s"	// startTime
20369 		"%kO=%s"	// serverCmd
20370 		"%kO=%s"	// probeHostname
20371 		"%kO=[%@]",	// results
20372 		CFSTR( "startTime" ),		startTimeStr,
20373 		CFSTR( "serverCmd" ),		serverCmd,
20374 		CFSTR( "probeHostname" ),	me->hostname,
20375 		CFSTR( "results" ),			&me->subtestResults );
20376 	require_noerr( err, exit );
20377 
20378 exit:
20379 	FreeNullSafe( serverCmd );
20380 	if( err ) _RCodeTestStop( me, err );
20381 }
20382 
20383 static void
_RCodeTestHandleGAIProbeResults(const RCodeTestRef me,dnssd_getaddrinfo_result_t * const inResults,const size_t inCount)20384 	_RCodeTestHandleGAIProbeResults(
20385 		const RCodeTestRef					me,
20386 		dnssd_getaddrinfo_result_t * const	inResults,
20387 		const size_t						inCount )
20388 {
20389 	size_t		i;
20390 	Boolean		startSubtests = false;
20391 
20392 	for( i = 0; i < inCount; ++i )
20393 	{
20394 		const dnssd_getaddrinfo_result_t		result = inResults[ i ];
20395 
20396 		if( dnssd_getaddrinfo_result_get_type( result ) == dnssd_getaddrinfo_result_type_add )
20397 		{
20398 			rct_ulog( kLogLevelInfo, "Probe GAI got %##a for %s\n",
20399 				dnssd_getaddrinfo_result_get_address( result ), me->hostname );
20400 			startSubtests = true;
20401 			break;
20402 		}
20403 	}
20404 	if( startSubtests )
20405 	{
20406 		OSStatus		err;
20407 
20408 		dnssd_getaddrinfo_forget( &me->gai );
20409 		dispatch_source_forget( &me->timer );
20410 		err = _RCodeTestStartSubtest( me );
20411 		if( err ) _RCodeTestStop( me, err );
20412 	}
20413 }
20414 
_RCodeTestProbeQueryTimerHandler(void * const inCtx)20415 static void	_RCodeTestProbeQueryTimerHandler( void * const inCtx )
20416 {
20417 	const RCodeTestRef		me = (RCodeTestRef) inCtx;
20418 
20419 	rct_ulog( kLogLevelInfo, "Probe GAI request for '%s' timed out.\n", me->hostname );
20420 	_RCodeTestStop( me, kNotPreparedErr );
20421 }
20422 
20423 //===========================================================================================================================
20424 
_RCodeTestStop(RCodeTestRef me,OSStatus inError)20425 static void	_RCodeTestStop( RCodeTestRef me, OSStatus inError )
20426 {
20427 	OSStatus		err;
20428 	NanoTime64		endTime;
20429 	char			endTimeStr[ 32 ];
20430 
20431 	endTime = NanoTimeGetCurrent();
20432 	me->error = inError;
20433 	rct_ulog( kLogLevelInfo, "Stopping test with error: %#m\n", me->error );
20434 
20435 	dnssd_getaddrinfo_forget( &me->gai );
20436 	dispatch_source_forget( &me->timer );
20437 	me->subtestResults = NULL;
20438 	ForgetCF( &me->gaiResults );
20439 	ForgetMem( &me->hostname );
20440 	ForgetMem( &me->description );
20441 	if( me->serverPID >= 0 )
20442 	{
20443 		OSStatus		killErr;
20444 
20445 		killErr = kill( me->serverPID, SIGTERM );
20446 		killErr = map_global_noerr_errno( killErr );
20447 		check_noerr( killErr );
20448 		me->serverPID = -1;
20449 	}
20450 	_NanoTime64ToTimestamp( endTime, endTimeStr, sizeof( endTimeStr ) );
20451 	err = CFPropertyListAppendFormatted( kCFAllocatorDefault, me->report,
20452 		"%kO=%s"		// endTime
20453 		"%kO=%lli"		// subtestCount
20454 		"%kO=%lli",		// subtestPassCount
20455 		CFSTR( "endTime" ),				endTimeStr,
20456 		CFSTR( "subtestCount" ),		(int64_t) me->subtestCount,
20457 		CFSTR( "subtestPassCount" ),	(int64_t) me->subtestPassCount );
20458 	check_noerr( err );
20459 	if( err && !me->error ) me->error = err;
20460 	dispatch_semaphore_signal( me->doneSem );
20461 }
20462 
20463 //===========================================================================================================================
20464 
20465 #define kRCodeSubtestRegularTimeLimitSecs		4
20466 #define kRCodeSubtestExtendedTimeLimitSecs		12	// Allow three seconds for each of the four server addresses.
20467 
20468 static void	_RCodeSubtestHandleGAIResults( RCodeTestRef inTest, dnssd_getaddrinfo_result_t *inResults, size_t inCount );
20469 static void	_RCodeSubtestTimerHandler( void *inCtx );
20470 
20471 static const void *	_DNSSDObjectCFArrayCallbackRetain( CFAllocatorRef inAllocator, const void *inObject );
20472 static void			_DNSSDObjectCFArrayCallbackRelease( CFAllocatorRef inAllocator, const void *inObject );
20473 
20474 static const CFArrayCallBacks kDNSSDObjectArrayCallbacks =
20475 {
20476 	.version	= 0,
20477 	.retain		= _DNSSDObjectCFArrayCallbackRetain,
20478 	.release	= _DNSSDObjectCFArrayCallbackRelease
20479 };
20480 
20481 // Arguments to use for Index labels. Since the test uses a test DNS server with four IPv6 addresses, which
20482 // mDNSResponder views as four different servers, this is an arbitrary mix of the four possible index values. An Index
20483 // label makes it so that only the server specified by the Index label's argument responds with the correct response.
20484 // The point of the mix is to force mDNSResponder to have to send queries to more than one server before it gets the
20485 // right response.
20486 
20487 static const int		kRCodeTestIndexArguments[] = { 2, 4, 1, 3, 2, 1, 4, 3 };
20488 #define kRCodeTestMaxIndexArgumentIndex		( countof( kRCodeTestIndexArguments ) - 1 )
20489 
_RCodeTestStartSubtest(const RCodeTestRef me)20490 static OSStatus	_RCodeTestStartSubtest( const RCodeTestRef me )
20491 {
20492 	OSStatus				err;
20493 	dnssd_getaddrinfo_t		gai;
20494 	const char *			rcodeStr;
20495 	int						index;
20496 	unsigned int			timeLimitSecs;
20497 	char					rcodeStrBuf[ 32 ];
20498 	char					indexLabelStr[ 32 ];
20499 	char					rcodeLabelStr[ 32 ];
20500 	char					tag[ 6 + 1 ];
20501 
20502 	require_action_quiet( !me->done, exit, err = kInternalErr );
20503 
20504 	check( !me->gai );
20505 	check( !me->timer );
20506 
20507 	me->startTime = NanoTimeGetCurrent();
20508 
20509 	if( me->indexIdx < 0 )
20510 	{
20511 		index = -1;
20512 		indexLabelStr[ 0 ] = '\0';
20513 		require_fatal( ( me->rcode >= 0 ) && ( me->rcode <= kRCodeTestMaxRCodeValue ),
20514 			"Unexpected subtest rcode value %d.", me->rcode );
20515 	}
20516 	else
20517 	{
20518 		index = kRCodeTestIndexArguments[ me->indexIdx ];
20519 		SNPrintF( indexLabelStr, sizeof( indexLabelStr ), "index-%d.", index );
20520 		me->hostnameHasAddr = true;
20521 		me->hostnameIsAlias = true;
20522 		require_fatal( ( me->rcode >= -1 ) && ( me->rcode <= kRCodeTestMaxRCodeValue ),
20523 			"Unexpected subtest rcode value %d.", me->rcode );
20524 	}
20525 	if( me->rcode > 0 )
20526 	{
20527 		SNPrintF( rcodeLabelStr, sizeof( rcodeLabelStr ), "rcode-%d.", me->rcode );
20528 	}
20529 	else
20530 	{
20531 		rcodeLabelStr[ 0 ] = '\0';
20532 	}
20533 	ForgetMem( &me->hostname );
20534 	ASPrintF( &me->hostname, "%s%s%scount-%d.tag-rcode-test-%s.d.test.",
20535 		me->hostnameIsAlias ? "alias." : "", indexLabelStr, rcodeLabelStr, me->hostnameHasAddr ? 1 : 0,
20536 		_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
20537 	require_action( me->hostname, exit, err = kNoMemoryErr );
20538 
20539 	CFReleaseNullSafe( me->gaiResults );
20540 	me->gaiResults = CFArrayCreateMutable( NULL, 0, &kDNSSDObjectArrayCallbacks );
20541 	require_action( me->gaiResults, exit, err = kNoMemoryErr );
20542 
20543 	me->gai = dnssd_getaddrinfo_create();
20544 	require_action( me->gai, exit, err = kNoResourcesErr );
20545 
20546 	dnssd_getaddrinfo_set_hostname( me->gai, me->hostname );
20547 	dnssd_getaddrinfo_set_flags( me->gai, kDNSServiceFlagsReturnIntermediates );
20548 	dnssd_getaddrinfo_set_interface_index( me->gai, kDNSServiceInterfaceIndexAny );
20549 	dnssd_getaddrinfo_set_protocols( me->gai, kDNSServiceProtocol_IPv4 );
20550 	dnssd_getaddrinfo_set_queue( me->gai, me->queue );
20551 	gai = me->gai;
20552 	dnssd_retain( gai );
20553 	_RCodeTestRetain( me );
20554 	dnssd_getaddrinfo_set_result_handler( me->gai,
20555 	^( dnssd_getaddrinfo_result_t * const inResults, const size_t inCount )
20556 	{
20557 		require_return( me->gai == gai );
20558 		_RCodeSubtestHandleGAIResults( me, inResults, inCount );
20559 	} );
20560 	dnssd_getaddrinfo_set_event_handler( gai,
20561 	^( const dnssd_event_t inEvent, const DNSServiceErrorType inGAIError )
20562 	{
20563 		if( inEvent == dnssd_event_invalidated )
20564 		{
20565 			dnssd_release( gai );
20566 			_RCodeTestRelease( me );
20567 		}
20568 		else if( inEvent == dnssd_event_error )
20569 		{
20570 			OSStatus		testErr;
20571 			Boolean			done;
20572 
20573 			require_return( me->gai == gai );
20574 			rct_ulog( kLogLevelError, "dnssd_getaddrinfo error: %#m\n", inGAIError );
20575 			testErr = _RCodeTestContinue( me, inGAIError, &done );
20576 			if( testErr || done ) _RCodeTestStop( me, testErr );
20577 		}
20578 	} );
20579 
20580 	// If an Index label is being used without an RCode label, then only the server specified by the index label will
20581 	// respond to the query, so we need more time to allow for query retries due to unresponsive servers.
20582 
20583 	if( ( index > 0 ) && ( me->rcode < 0 ) )
20584 	{
20585 		timeLimitSecs = kRCodeSubtestExtendedTimeLimitSecs;
20586 	}
20587 	else
20588 	{
20589 		timeLimitSecs = kRCodeSubtestRegularTimeLimitSecs;
20590 	}
20591 	check( !me->timer );
20592 	err = DispatchTimerOneShotCreate( dispatch_time_seconds( timeLimitSecs ),
20593 		timeLimitSecs * ( UINT64_C_safe( kNanosecondsPerSecond ) / 20 ),
20594 		me->queue, _RCodeSubtestTimerHandler, me, &me->timer );
20595 	require_noerr( err, exit );
20596 
20597 	rcodeStr = DNSRCodeToString( me->rcode );
20598 	if( !rcodeStr )
20599 	{
20600 		SNPrintF( rcodeStrBuf, sizeof( rcodeStrBuf ), "RCODE%d", me->rcode );
20601 		rcodeStr = rcodeStrBuf;
20602 	}
20603 	ForgetMem( &me->description );
20604 	if( me->indexIdx < 0 )
20605 	{
20606 		ASPrintF( &me->description, "DNS response with RCODE %s (%d), %s CNAME record, and %s A record from all servers",
20607 			rcodeStr, me->rcode, me->hostnameIsAlias ? "one" : "no", me->hostnameHasAddr ? "one" : "no" );
20608 		require_action( me->description, exit, err = kNoMemoryErr );
20609 	}
20610 	else
20611 	{
20612 		int		n;
20613 
20614 		ASPrintF( &me->description, "DNS response with RCODE NoError (0), %s CNAME record, and %s A record from server #%d.",
20615 			me->hostnameIsAlias ? "one" : "no", me->hostnameHasAddr ? "one" : "no", index );
20616 		require_action( me->description, exit, err = kNoMemoryErr );
20617 
20618 		if( me->rcode < 0 )
20619 		{
20620 			n = AppendPrintF( &me->description, " No DNS respones from all other servers." );
20621 			require_action( n >= 0, exit, err = kNoMemoryErr );
20622 		}
20623 		else
20624 		{
20625 			n = AppendPrintF( &me->description, " DNS responses with RCODE %s (%d) and no records from all other servers.",
20626 				rcodeStr, me->rcode );
20627 			require_action( n >= 0, exit, err = kNoMemoryErr );
20628 		}
20629 	}
20630 	++me->subtestCount;
20631 	rct_ulog( kLogLevelInfo, "Starting subtest #%d: %s\n", me->subtestCount, me->description );
20632 
20633 	dnssd_getaddrinfo_activate( me->gai );
20634 	dispatch_resume( me->timer );
20635 
20636 exit:
20637 	return( err );
20638 }
20639 
20640 static void
_RCodeSubtestHandleGAIResults(const RCodeTestRef me,dnssd_getaddrinfo_result_t * const inResults,const size_t inCount)20641 	_RCodeSubtestHandleGAIResults(
20642 		const RCodeTestRef					me,
20643 		dnssd_getaddrinfo_result_t * const	inResults,
20644 		const size_t						inCount )
20645 {
20646 	size_t			i;
20647 
20648 	for( i = 0; i < inCount; ++i )
20649 	{
20650 		const dnssd_getaddrinfo_result_t		result = inResults[ i ];
20651 
20652 		rct_ulog( kLogLevelInfo, "GAI result -- %@\n", result );
20653 		CFArrayAppendValue( me->gaiResults, result );
20654 	}
20655 }
20656 
_RCodeSubtestTimerHandler(void * const inCtx)20657 static void	_RCodeSubtestTimerHandler( void * const inCtx )
20658 {
20659 	OSStatus							err;
20660 	const RCodeTestRef					me = (RCodeTestRef) inCtx;
20661 	dnssd_getaddrinfo_result_t			result;
20662 	const sockaddr_ip *					sip;
20663 	const uint8_t *						targetName;
20664 	CFIndex								n;
20665 	dnssd_getaddrinfo_result_type_t		resultType;
20666 	int									expectedResultCount;
20667 	uint8_t								hostname[ kDomainNameLengthMax ];
20668 	uint8_t								actualHostname[ kDomainNameLengthMax ];
20669 	Boolean								expectAddress, expectCanonName, done;
20670 
20671 	// mDNSResponder has traditionally ignored responses with RCODEs not equal to NoError, NXDomain, or NotAuth.
20672 	// Such responses result in a kDNSServiceErr_NoSuchRecord error, or NoAddress in the case of dnssd_getaddrinfo.
20673 
20674 	if( ( me->indexIdx >= 0 ) || _RCodeTestRCodeIsGood( me->rcode ) )
20675 	{
20676 		expectAddress		= me->hostnameHasAddr;
20677 		expectCanonName		= me->hostnameIsAlias;
20678 		expectedResultCount	= 1;
20679 	}
20680 	else
20681 	{
20682 		expectAddress		= false;
20683 		expectCanonName		= false;
20684 		expectedResultCount	= 1;
20685 	}
20686 	n = CFArrayGetCount( me->gaiResults );
20687 	require_action_quiet( n == expectedResultCount, exit, err = kCountErr );
20688 
20689 	if( n != 0 )
20690 	{
20691 		result = (dnssd_getaddrinfo_result_t) CFArrayGetValueAtIndex( me->gaiResults, 0 );
20692 		resultType = dnssd_getaddrinfo_result_get_type( result );
20693 
20694 		sip = (const sockaddr_ip *) dnssd_getaddrinfo_result_get_address( result );
20695 		require_action_quiet( sip->sa.sa_family == AF_INET, exit, err = kAddressErr );
20696 
20697 		if( expectAddress )
20698 		{
20699 			require_action_quiet( resultType == dnssd_getaddrinfo_result_type_add, exit, err = kTypeErr );
20700 			require_action_quiet( ntohl( sip->v4.sin_addr.s_addr ) == ( kDNSServerBaseAddrV4 + 1 ), exit, err = kAddressErr );
20701 		}
20702 		else
20703 		{
20704 			require_action_quiet( resultType == dnssd_getaddrinfo_result_type_no_address, exit, err = kTypeErr );
20705 		}
20706 		err = DomainNameFromString( hostname, me->hostname, NULL );
20707 		require_noerr_fatal( err, "Failed to convert hostname to DNS wire format -- hostname: %s, error: %#m",
20708 			me->hostname, err );
20709 
20710 		err = DomainNameFromString( actualHostname, dnssd_getaddrinfo_result_get_actual_hostname( result ), NULL );
20711 		require_noerr_quiet( err, exit );
20712 
20713 		if( expectCanonName )
20714 		{
20715 			size_t		firstLabelLen;
20716 
20717 			firstLabelLen = hostname[ 0 ];
20718 			require_fatal( firstLabelLen > 0, "Hostname's first label length is zero -- hostname: %s", me->hostname );
20719 
20720 			targetName = &hostname[ 1 + firstLabelLen ];
20721 		}
20722 		else
20723 		{
20724 			targetName = hostname;
20725 		}
20726 		require_action_quiet( DomainNameEqual( actualHostname, targetName ), exit, err = kNameErr );
20727 	}
20728 	err = kNoErr;
20729 
20730 exit:
20731 	err = _RCodeTestContinue( me, err, &done );
20732 	if( err || done ) _RCodeTestStop( me, err );
20733 }
20734 
_DNSSDObjectCFArrayCallbackRetain(__unused const CFAllocatorRef inAllocator,const void * inObject)20735 static const void *	_DNSSDObjectCFArrayCallbackRetain( __unused const CFAllocatorRef inAllocator, const void *inObject )
20736 {
20737 	dnssd_retain( (dnssd_object_t) inObject );
20738 	return inObject;
20739 }
20740 
_DNSSDObjectCFArrayCallbackRelease(__unused const CFAllocatorRef inAllocator,const void * inObject)20741 static void	_DNSSDObjectCFArrayCallbackRelease( __unused const CFAllocatorRef inAllocator, const void *inObject )
20742 {
20743 	dnssd_release( (dnssd_object_t) inObject );
20744 }
20745 
20746 //===========================================================================================================================
20747 
20748 static OSStatus	_RCodeTestStopSubtest( RCodeTestRef inTest, OSStatus inError );
20749 
_RCodeTestContinue(const RCodeTestRef me,OSStatus inSubtestError,Boolean * outDone)20750 static OSStatus	_RCodeTestContinue( const RCodeTestRef me, OSStatus inSubtestError, Boolean *outDone )
20751 {
20752 	OSStatus		err;
20753 
20754 	require_action_quiet( !me->done, exit, err = kNoErr );
20755 
20756 	err = _RCodeTestStopSubtest( me, inSubtestError );
20757 	require_noerr( err, exit );
20758 	require_action_quiet( !me->done, exit, err = kNoErr );
20759 
20760 	err = _RCodeTestStartSubtest( me );
20761 	require_noerr( err, exit );
20762 
20763 exit:
20764 	if( outDone ) *outDone = me->done;
20765 	return( err );
20766 }
20767 
20768 //===========================================================================================================================
20769 
_RCodeTestStopSubtest(const RCodeTestRef me,const OSStatus inError)20770 static OSStatus	_RCodeTestStopSubtest( const RCodeTestRef me, const OSStatus inError )
20771 {
20772 	OSStatus				err;
20773 	NanoTime64				endTime;
20774 	CFMutableArrayRef		gaiResultDescs;
20775 	char					errorStr[ 128 ];
20776 	char					startTimeStr[ 32 ];
20777 	char					endTimeStr[ 32 ];
20778 	CFIndex					i, n;
20779 
20780 	dnssd_getaddrinfo_forget( &me->gai );
20781 	dispatch_source_forget( &me->timer );
20782 
20783 	endTime = NanoTimeGetCurrent();
20784 
20785 	if( !inError ) ++me->subtestPassCount;
20786 
20787 	rct_ulog( kLogLevelInfo, "Subtest #%d result: %s (pass rate: %d/%d)\n",
20788 		me->subtestCount, inError ? "fail" : "pass", me->subtestPassCount, me->subtestCount );
20789 
20790 	_NanoTime64ToTimestamp( me->startTime, startTimeStr, sizeof( startTimeStr ) );
20791 	_NanoTime64ToTimestamp( endTime, endTimeStr, sizeof( endTimeStr ) );
20792 	SNPrintF( errorStr, sizeof( errorStr ), "%m", inError );
20793 	err = CFPropertyListAppendFormatted( kCFAllocatorDefault, me->subtestResults,
20794 		"{"
20795 			"%kO=%s"		// description
20796 			"%kO=%s"		// startTime
20797 			"%kO=%s"		// endTime
20798 			"%kO=%s"		// hostname
20799 			"%kO=%b"		// pass
20800 			"%kO="			// error
20801 			"{"
20802 				"%kO=%lli"	// code
20803 				"%kO=%s"	// description
20804 			"}"
20805 			"%kO=[%@]"		// GAIResults
20806 		"}",
20807 		CFSTR( "description" ),	me->description,
20808 		CFSTR( "startTime" ),	startTimeStr,
20809 		CFSTR( "endTime" ),		endTimeStr,
20810 		CFSTR( "hostname" ),	me->hostname,
20811 		CFSTR( "pass" ),		!inError ? true : false,
20812 		CFSTR( "error" ),
20813 		CFSTR( "code" ),		(int64_t) inError,
20814 		CFSTR( "description" ),	errorStr,
20815 		CFSTR( "GAIResults" ),	&gaiResultDescs );
20816 	require_noerr( err, exit );
20817 
20818 	n = CFArrayGetCount( me->gaiResults );
20819 	for( i = 0; i < n; ++ i )
20820 	{
20821 		dnssd_getaddrinfo_result_t		result = (dnssd_getaddrinfo_result_t) CFArrayGetValueAtIndex( me->gaiResults, i );
20822 		char *							resultDesc;
20823 
20824 		resultDesc = dnssd_copy_description( result );
20825 		require_action_quiet( resultDesc, exit, err = kNoMemoryErr );
20826 
20827 		err = CFPropertyListAppendFormatted( NULL, gaiResultDescs, "%s", resultDesc );
20828 		ForgetMem( &resultDesc );
20829 		require_noerr( err, exit );
20830 	}
20831 
20832 	if( me->indexIdx < 0 )
20833 	{
20834 		// Each subtest is one of the 64 possible combinations of
20835 		// rcode ∈ {0, 1, 2, …, 15}, hostnameIsAlias ∈ {false, true}, hostnameHasAddr ∈ {false, true}.
20836 
20837 		if( !me->hostnameHasAddr )
20838 		{
20839 			me->hostnameHasAddr = true;
20840 		}
20841 		else
20842 		{
20843 			me->hostnameHasAddr = false;
20844 			if( !me->hostnameIsAlias )
20845 			{
20846 				me->hostnameIsAlias = true;
20847 			}
20848 			else
20849 			{
20850 				me->hostnameIsAlias = false;
20851 				if( me->rcode < kRCodeTestMaxRCodeValue )
20852 				{
20853 					++me->rcode;
20854 				}
20855 				else
20856 				{
20857 					me->indexIdx	= 0;	// At this point we move on to using Index labels.
20858 					me->rcode		= -1;	// Start with rcode set to -1 to indicate no RCode label.
20859 				}
20860 			}
20861 		}
20862 	}
20863 	else
20864 	{
20865 		if( me->indexIdx < (int) kRCodeTestMaxIndexArgumentIndex )
20866 		{
20867 			++me->indexIdx;
20868 		}
20869 		else
20870 		{
20871 			me->indexIdx = 0;
20872 
20873 			// When using Index labels to limit responses to one server, we want the other servers to respond
20874 			// with bad RCODEs, so if the current RCODE value is good, try incrementing again.
20875 
20876 			do
20877 			{
20878 				if( me->rcode < kRCodeTestMaxRCodeValue )
20879 				{
20880 					++me->rcode;
20881 				}
20882 				else
20883 				{
20884 					me->done = true;
20885 				}
20886 
20887 			}	while( !me->done && _RCodeTestRCodeIsGood( me->rcode ) );
20888 		}
20889 	}
20890 
20891 exit:
20892 	return( err );
20893 }
20894 
20895 //===========================================================================================================================
20896 
_RCodeTestRCodeIsGood(const int inRCode)20897 static Boolean	_RCodeTestRCodeIsGood( const int inRCode )
20898 {
20899 	return( ( inRCode == kDNSRCode_NoError ) || ( inRCode == kDNSRCode_NXDomain ) || ( inRCode == kDNSRCode_NotAuth ) );
20900 }
20901 #endif	// MDNSRESPONDER_PROJECT
20902 
20903 //===========================================================================================================================
20904 //	RegistrationTestCmd
20905 //===========================================================================================================================
20906 
20907 typedef struct RegistrationSubtest		RegistrationSubtest;
20908 
20909 typedef struct
20910 {
20911 	CFMutableArrayRef			subtestReports;				// Array of subtest reports.
20912 	dispatch_source_t			timer;						// Timer to enforce subtest durations.
20913 	dispatch_source_t			sigSourceINT;				// SIGINT signal handler for a clean test exit.
20914 	dispatch_source_t			sigSourceTERM;				// SIGTERM signal handler for a clean test exit.
20915 	RegistrationSubtest *		subtest;					// Current subtest.
20916 	char *						outputFilePath;				// Path of test result output file. If NULL, stdout will be used.
20917 	OutputFormatType			outputFormat;				// Format of test results output.
20918 	CFStringRef					computerNamePrev;			// Previous ComputerName.
20919 	CFStringRef					localHostNamePrev;			// Previous LocalHostName.
20920 	NanoTime64					startTime;					// Test's start time.
20921 	char *						computerName;				// Temporary ComputerName to set during testing. (malloc'd)
20922 	char *						localHostName;				// Temporary LocalHostName to set during testing. (malloc'd)
20923 	CFStringEncoding			computerNamePrevEncoding;	// Previous ComputerName's encoding.
20924 	int							subtestIndex;				// Index of current subtest.
20925 	Boolean						computerNameSet;			// True if a temporary ComputerName was set.
20926 	Boolean						localHostNameSet;			// True if a temporary LocalHostName was set.
20927 	Boolean						failed;						// True if at least one non-skipped subtest failed.
20928 	Boolean						forBATS;					// True if the test is running in a BATS environment.
20929 
20930 }	RegistrationTest;
20931 
20932 typedef enum
20933 {
20934 	kRegistrationInterfaceSet_Null			= 0,
20935 	kRegistrationInterfaceSet_All			= 1,
20936 	kRegistrationInterfaceSet_AllPlusAWDL	= 2,
20937 	kRegistrationInterfaceSet_LoopbackOnly	= 3,
20938 	kRegistrationInterfaceSet_AWDLOnly		= 4
20939 
20940 }	RegistrationInterfaceSet;
20941 
20942 typedef struct
20943 {
20944 	RegistrationInterfaceSet		interfaceSet;	// Interfaces to register the service over.
20945 	Boolean							useDefaultName;	// True if registration is to use the default service name.
20946 	Boolean							useLODiscovery;	// True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
20947 
20948 }	RegistrationSubtestParams;
20949 
20950 static const RegistrationSubtestParams		kRegistrationSubtestParams[] =
20951 {
20952 	{ kRegistrationInterfaceSet_All,			true,	false },
20953 	{ kRegistrationInterfaceSet_All,			false,	false },
20954 	{ kRegistrationInterfaceSet_AllPlusAWDL,	true,	false },
20955 	{ kRegistrationInterfaceSet_AllPlusAWDL,	false,	false },
20956 	{ kRegistrationInterfaceSet_LoopbackOnly,	true,	false },
20957 	{ kRegistrationInterfaceSet_LoopbackOnly,	false,	false },
20958 	{ kRegistrationInterfaceSet_AWDLOnly,		true,	false },
20959 	{ kRegistrationInterfaceSet_AWDLOnly,		false,	false },
20960 	{ kRegistrationInterfaceSet_All,			true,	true  },
20961 	{ kRegistrationInterfaceSet_All,			false,	true  },
20962 	{ kRegistrationInterfaceSet_AllPlusAWDL,	true,	true  },
20963 	{ kRegistrationInterfaceSet_AllPlusAWDL,	false,	true  },
20964 	{ kRegistrationInterfaceSet_LoopbackOnly,	true,	true  },
20965 	{ kRegistrationInterfaceSet_LoopbackOnly,	false,	true  },
20966 	{ kRegistrationInterfaceSet_AWDLOnly,		true,	true  },
20967 	{ kRegistrationInterfaceSet_AWDLOnly,		false,	true  }
20968 };
20969 
20970 typedef struct
20971 {
20972 	NanoTime64		browseResultTime;	// Per-interface browse result time.
20973 	NanoTime64		querySRVResultTime;	// Per-interface SRV record query result time.
20974 	NanoTime64		queryTXTResultTime;	// Per-interface TXT record query result time.
20975 
20976 }	RegistrationResultTimes;
20977 
20978 typedef struct
20979 {
20980 	MDNSInterfaceItem			base;	// Underlying MDNSInterface linked-list item.
20981 	RegistrationResultTimes		times;	// Per-interface result times.
20982 
20983 }	RegistrationInterfaceItem;
20984 
20985 struct RegistrationSubtest
20986 {
20987 	DNSServiceRef					registration;		// DNS-SD service registration.
20988 	DNSServiceRef					connection;			// Shared DNS-SD connection.
20989 	DNSServiceRef					browse;				// DNS-SD browse for service's type.
20990 	DNSServiceRef					querySRV;			// DNS-SD query request for service's SRV record.
20991 	DNSServiceRef					queryTXT;			// DNS-SD query request for service's TXT record.
20992 	CFMutableArrayRef				unexpected;			// Array of unexpected registration, browse, and query results.
20993 #if( TARGET_OS_WATCH )
20994 	CFMutableArrayRef				ignored;			// Array of unexpected, but ignored, browse and query results.
20995 #endif
20996 	const char *					serviceName;		// Service's name.
20997 	char *							serviceNameCustom;	// Service's name if using a custom name. (malloc'd)
20998 	char *							serviceType;		// Service's service type. (malloc'd)
20999 	size_t							serviceTypeLen;		// C string length of service's service type.
21000 	char *							serviceFQDN;		// Service's FQDN, i.e., name of its SRV and TXT records.
21001 	uint8_t *						txtPtr;				// Pointer to service's TXT record data. (malloc'd)
21002 	size_t							txtLen;				// Length of service's TXT record data.
21003 	RegistrationInterfaceItem *		ifList;				// If ifIndex == 0, interfaces that service should register over.
21004 	RegistrationResultTimes			ifTimes;			// If ifIndex != 0, result times for interface with that index.
21005 	RegistrationTest *				test;				// Pointer to parent test.
21006 	NanoTime64						startTime;			// Subtest's start time.
21007 	char *							description;		// Subtest's description. (malloc'd)
21008 	uint32_t						ifIndex;			// Interface index used for service registration.
21009 	uint16_t						port;				// Service's port number.
21010 	Boolean							useLODiscovery;		// True if discovery is to use kDNSServiceInterfaceIndexLocalOnly.
21011 	Boolean							includeAWDL;		// True if the IncludeAWDL flag was used during registration.
21012 	Boolean							ifIsAWDL;			// True if ifIndex is the index of an AWDL interface.
21013 	Boolean							skipped;			// True if this subtest is to be skipped.
21014 	Boolean							registered;			// True if the test service was successfully registered.
21015 	Boolean							useDefaultName;		// True if the service is to use the default service name.
21016 };
21017 
21018 static OSStatus	_RegistrationTestCreate( RegistrationTest **outTest );
21019 static void		_RegistrationTestFree( RegistrationTest *inTest );
21020 static void		_RegistrationTestBegin( void *inContext );
21021 static void		_RegistrationTestProceed( RegistrationTest *inTest );
21022 static OSStatus	_RegistrationTestStart( RegistrationTest *inTest );
21023 static void		_RegistrationTestStop( RegistrationTest *inTest );
21024 #define _RegistrationTestForget( X )		ForgetCustomEx( X, _RegistrationTestStop, _RegistrationTestFree )
21025 static OSStatus
21026 	_RegistrationTestStartSubtest(
21027 		RegistrationTest *					inTest,
21028 		const RegistrationSubtestParams *	inParams,
21029 		Boolean *							outSkipped );
21030 static OSStatus	_RegistrationTestEndSubtest( RegistrationTest *inTest );
21031 static void		_RegistrationTestEnd( RegistrationTest *inTest ) ATTRIBUTE_NORETURN;
21032 static void		_RegistrationTestExit( RegistrationTest *inTest, OSStatus inError ) ATTRIBUTE_NORETURN;
21033 static OSStatus	_RegistrationSubtestCreate( RegistrationSubtest **outSubtest );
21034 static void		_RegistrationSubtestStop( RegistrationSubtest *inSubtest );
21035 static void		_RegistrationSubtestFree( RegistrationSubtest *inSubtest );
21036 #define _RegistrationSubtestForget( X )		ForgetCustomEx( X, _RegistrationSubtestStop, _RegistrationSubtestFree )
21037 static OSStatus	_RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL, RegistrationInterfaceItem **outList );
21038 static OSStatus
21039 	_RegistrationTestCreateRandomTXTRecord(
21040 		size_t		inMinLen,
21041 		size_t		inMaxLen,
21042 		uint8_t **	outTXTPtr,
21043 		size_t *	outTXTLen );
21044 static void DNSSD_API
21045 	_RegistrationSubtestRegisterCallback(
21046 		DNSServiceRef		inSDRef,
21047 		DNSServiceFlags		inFlags,
21048 		DNSServiceErrorType	inError,
21049 		const char *		inName,
21050 		const char *		inType,
21051 		const char *		inDomain,
21052 		void *				inContext );
21053 static void DNSSD_API
21054 	_RegistrationSubtestBrowseCallback(
21055 		DNSServiceRef		inSDRef,
21056 		DNSServiceFlags		inFlags,
21057 		uint32_t			inIfIndex,
21058 		DNSServiceErrorType	inError,
21059 		const char *		inServiceName,
21060 		const char *		inServiceType,
21061 		const char *		inDomain,
21062 		void *				inContext );
21063 static void DNSSD_API
21064 	_RegistrationSubtestQueryCallback(
21065 		DNSServiceRef			inSDRef,
21066 		DNSServiceFlags			inFlags,
21067 		uint32_t				inIfIndex,
21068 		DNSServiceErrorType		inError,
21069 		const char *			inName,
21070 		uint16_t				inType,
21071 		uint16_t				inClass,
21072 		uint16_t				inRDataLen,
21073 		const void *			inRDataPtr,
21074 		uint32_t				inTTL,
21075 		void *					inContext );
21076 static Boolean	_RegistrationSubtestValidServiceType( const RegistrationSubtest *inSubtest, const char *inServiceType );
21077 static RegistrationResultTimes *
21078 	_RegistrationSubtestGetInterfaceResultTimes(
21079 		RegistrationSubtest *	inSubtest,
21080 		uint32_t				inIfIndex,
21081 		Boolean *				outIsAWDL );
21082 static void		_RegistrationTestTimerHandler( void *inContext );
21083 #if( TARGET_OS_WATCH )
21084 static Boolean	_RegistrationTestInterfaceIsWiFi( const char *inIfName );
21085 #endif
21086 
RegistrationTestCmd(void)21087 static void	RegistrationTestCmd( void )
21088 {
21089 	OSStatus				err;
21090 	RegistrationTest *		test;
21091 
21092 	err = _RegistrationTestCreate( &test );
21093 	require_noerr( err, exit );
21094 
21095 	if( gRegistrationTest_BATSEnvironment ) test->forBATS = true;
21096 	if( gRegistrationTest_OutputFilePath )
21097 	{
21098 		test->outputFilePath = strdup( gRegistrationTest_OutputFilePath );
21099 		require_action( test->outputFilePath, exit, err = kNoMemoryErr );
21100 	}
21101 
21102 	err = OutputFormatFromArgString( gRegistrationTest_OutputFormat, &test->outputFormat );
21103 	require_noerr_quiet( err, exit );
21104 
21105 	dispatch_async_f( dispatch_get_main_queue(), test, _RegistrationTestBegin );
21106 	dispatch_main();
21107 
21108 exit:
21109 	if( test ) _RegistrationTestFree( test );
21110 	ErrQuit( 1, "error: %#m\n", err );
21111 }
21112 
21113 //===========================================================================================================================
21114 
_RegistrationTestCreate(RegistrationTest ** outTest)21115 static OSStatus	_RegistrationTestCreate( RegistrationTest **outTest )
21116 {
21117 	OSStatus				err;
21118 	RegistrationTest *		obj;
21119 
21120 	obj = (RegistrationTest *) calloc( 1, sizeof( *obj ) );
21121 	require_action( obj, exit, err = kNoMemoryErr );
21122 
21123 	obj->subtestReports = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
21124 	require_action( obj->subtestReports, exit, err = kNoMemoryErr );
21125 
21126 	*outTest = obj;
21127 	obj = NULL;
21128 	err = kNoErr;
21129 
21130 exit:
21131 	if( obj ) _RegistrationTestFree( obj );
21132 	return( err );
21133 }
21134 
21135 //===========================================================================================================================
21136 
_RegistrationTestFree(RegistrationTest * inTest)21137 static void	_RegistrationTestFree( RegistrationTest *inTest )
21138 {
21139 	check( !inTest->timer );
21140 	check( !inTest->sigSourceINT );
21141 	check( !inTest->sigSourceTERM );
21142 	check( !inTest->computerNameSet );
21143 	check( !inTest->localHostNameSet );
21144 	check( !inTest->subtest );
21145 	ForgetCF( &inTest->subtestReports );
21146 	ForgetMem( &inTest->outputFilePath );
21147 	ForgetCF( &inTest->computerNamePrev );
21148 	ForgetCF( &inTest->localHostNamePrev );
21149 	ForgetMem( &inTest->computerName );
21150 	ForgetMem( &inTest->localHostName );
21151 }
21152 
21153 //===========================================================================================================================
21154 
_RegistrationTestBegin(void * inContext)21155 static void	_RegistrationTestBegin( void *inContext )
21156 {
21157 	_RegistrationTestProceed( (RegistrationTest *) inContext );
21158 }
21159 
21160 //===========================================================================================================================
21161 
_RegistrationTestProceed(RegistrationTest * inTest)21162 static void	_RegistrationTestProceed( RegistrationTest *inTest )
21163 {
21164 	OSStatus		err;
21165 	Boolean			skippedSubtest;
21166 
21167 	do
21168 	{
21169 		int		subtestIndex;
21170 
21171 		if( !inTest->startTime )
21172 		{
21173 			err = _RegistrationTestStart( inTest );
21174 			require_noerr_quiet( err, exit );
21175 
21176 			inTest->startTime = NanoTimeGetCurrent();
21177 		}
21178 		else
21179 		{
21180 			err = _RegistrationTestEndSubtest( inTest );
21181 			require_noerr( err, exit );
21182 
21183 			++inTest->subtestIndex;
21184 		}
21185 
21186 		subtestIndex = inTest->subtestIndex;
21187 		if( subtestIndex < (int) countof( kRegistrationSubtestParams ) )
21188 		{
21189 			err = _RegistrationTestStartSubtest( inTest, &kRegistrationSubtestParams[ subtestIndex ], &skippedSubtest );
21190 			require_noerr_quiet( err, exit );
21191 		}
21192 		else
21193 		{
21194 			_RegistrationTestEnd( inTest );
21195 		}
21196 
21197 	}	while( skippedSubtest );
21198 
21199 exit:
21200 	if( err ) _RegistrationTestExit( inTest, err );
21201 }
21202 
21203 //===========================================================================================================================
21204 
21205 static void	_RegistrationTestSignalHandler( void *inContext );
21206 
_RegistrationTestStart(RegistrationTest * inTest)21207 static OSStatus	_RegistrationTestStart( RegistrationTest *inTest )
21208 {
21209 	OSStatus		err;
21210 	char			tag[ 6 + 1 ];
21211 
21212 	// Save original ComputerName and LocalHostName.
21213 
21214 	check( !inTest->computerNamePrev );
21215 	inTest->computerNamePrev = SCDynamicStoreCopyComputerName( NULL, &inTest->computerNamePrevEncoding );
21216 	err = map_scerror( inTest->computerNamePrev );
21217 	require_noerr( err, exit );
21218 
21219 	check( !inTest->localHostNamePrev );
21220 	inTest->localHostNamePrev = SCDynamicStoreCopyLocalHostName( NULL );
21221 	err = map_scerror( inTest->localHostNamePrev );
21222 	require_noerr( err, exit );
21223 
21224 	// Generate a unique test ComputerName.
21225 
21226 	check( !inTest->computerName );
21227 	ASPrintF( &inTest->computerName, "dnssdutil-regtest-computer-name-%s",
21228 		_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
21229 	require_action( inTest->computerName, exit, err = kNoMemoryErr );
21230 
21231 	// Generate a unique test LocalHostName.
21232 
21233 	check( !inTest->localHostName );
21234 	ASPrintF( &inTest->localHostName, "dnssdutil-regtest-local-hostname-%s",
21235 		_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
21236 	require_action( inTest->localHostName, exit, err = kNoMemoryErr );
21237 
21238 	// Set up SIGINT signal handler.
21239 
21240 	signal( SIGINT, SIG_IGN );
21241 	check( !inTest->sigSourceINT );
21242 	err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), _RegistrationTestSignalHandler, inTest,
21243 		&inTest->sigSourceINT );
21244 	require_noerr( err, exit );
21245 	dispatch_resume( inTest->sigSourceINT );
21246 
21247 	// Set up SIGTERM signal handler.
21248 
21249 	signal( SIGTERM, SIG_IGN );
21250 	check( !inTest->sigSourceTERM );
21251 	err = DispatchSignalSourceCreate( SIGTERM, dispatch_get_main_queue(), _RegistrationTestSignalHandler, inTest,
21252 		&inTest->sigSourceTERM );
21253 	require_noerr( err, exit );
21254 	dispatch_resume( inTest->sigSourceTERM );
21255 
21256 	// Set test ComputerName.
21257 
21258 	check( !inTest->computerNameSet );
21259 	err = _SetComputerNameWithUTF8CString( inTest->computerName );
21260 	require_noerr( err, exit );
21261 	inTest->computerNameSet = true;
21262 
21263 	// Set test LocalHostName.
21264 
21265 	check( !inTest->localHostNameSet );
21266 	err = _SetLocalHostNameWithUTF8CString( inTest->localHostName );
21267 	require_noerr( err, exit );
21268 	inTest->localHostNameSet = true;
21269 
21270 exit:
21271 	if( err ) _RegistrationTestStop( inTest );
21272 	return( err );
21273 }
21274 
_RegistrationTestSignalHandler(void * inContext)21275 static void	_RegistrationTestSignalHandler( void *inContext )
21276 {
21277 	RegistrationTest * const		test = (RegistrationTest *) inContext;
21278 
21279 	FPrintF( stderr, "Registration test got a SIGINT or SIGTERM signal, exiting..." );
21280 
21281 	_RegistrationTestExit( test, kCanceledErr );
21282 }
21283 
21284 //===========================================================================================================================
21285 
_RegistrationTestStop(RegistrationTest * inTest)21286 static void	_RegistrationTestStop( RegistrationTest *inTest )
21287 {
21288 	OSStatus		err;
21289 
21290 	dispatch_source_forget( &inTest->timer );
21291 	dispatch_source_forget( &inTest->sigSourceINT );
21292 	dispatch_source_forget( &inTest->sigSourceTERM );
21293 	_RegistrationSubtestForget( &inTest->subtest );
21294 	if( inTest->computerNameSet )
21295 	{
21296 		err = _SetComputerName( inTest->computerNamePrev, inTest->computerNamePrevEncoding );
21297 		check_noerr( err );
21298 		if( !err ) inTest->computerNameSet = false;
21299 	}
21300 	if( inTest->localHostNameSet )
21301 	{
21302 		err = _SetLocalHostName( inTest->localHostNamePrev );
21303 		check_noerr( err );
21304 		if( !err ) inTest->localHostNameSet = false;
21305 	}
21306 }
21307 
21308 //===========================================================================================================================
21309 
21310 #define kRegistrationTestSubtestDurationSecs		5
21311 
21312 static OSStatus
_RegistrationTestStartSubtest(RegistrationTest * inTest,const RegistrationSubtestParams * inParams,Boolean * outSkipped)21313 	_RegistrationTestStartSubtest(
21314 		RegistrationTest *					inTest,
21315 		const RegistrationSubtestParams *	inParams,
21316 		Boolean *							outSkipped )
21317 {
21318 	OSStatus					err;
21319 	RegistrationSubtest *		subtest;
21320 	const char *				interfaceDesc;
21321 	DNSServiceFlags				flags;
21322 	char						tag[ 6 + 1 ];
21323 
21324 	subtest	= NULL;
21325 	err = _RegistrationSubtestCreate( &subtest );
21326 	require_noerr( err, exit );
21327 
21328 	subtest->test			= inTest;
21329 	subtest->useDefaultName	= inParams->useDefaultName;
21330 	subtest->useLODiscovery	= inParams->useLODiscovery;
21331 
21332 	// Determine registration interfaces.
21333 
21334 	switch( inParams->interfaceSet )
21335 	{
21336 		case kRegistrationInterfaceSet_All:
21337 			subtest->ifIndex = kDNSServiceInterfaceIndexAny;
21338 
21339 			if( !subtest->useLODiscovery )
21340 			{
21341 				err = _RegistrationTestInterfaceListCreate( false, &subtest->ifList );
21342 				require_noerr( err, exit );
21343 			}
21344 			interfaceDesc = "all interfaces (excluding AWDL)";
21345 			break;
21346 
21347 		case kRegistrationInterfaceSet_AllPlusAWDL:
21348 			subtest->ifIndex		= kDNSServiceInterfaceIndexAny;
21349 			subtest->includeAWDL	= true;
21350 
21351 			if( !subtest->useLODiscovery )
21352 			{
21353 				err = _RegistrationTestInterfaceListCreate( true, &subtest->ifList );
21354 				require_noerr( err, exit );
21355 			}
21356 			interfaceDesc = "all interfaces (including AWDL)";
21357 			break;
21358 
21359 		case kRegistrationInterfaceSet_LoopbackOnly:
21360 			subtest->ifIndex = if_nametoindex( "lo0" );
21361 			if( subtest->ifIndex == 0 )
21362 			{
21363 				FPrintF( stderr, "Failed to get index for loopback interface lo0.\n" );
21364 				err = kNoResourcesErr;
21365 				goto exit;
21366 			}
21367 			interfaceDesc = "loopback interface";
21368 			break;
21369 
21370 		case kRegistrationInterfaceSet_AWDLOnly:
21371 			err = _MDNSInterfaceGetAny( kMDNSInterfaceSubset_AWDL, NULL, &subtest->ifIndex );
21372 			if( err == kNotFoundErr )
21373 			{
21374 				FPrintF( stderr, "Warning: No mDNS-capable AWDL interface is available.\n" );
21375 				subtest->skipped = true;
21376 				err = kNoErr;
21377 			}
21378 			require_noerr( err, exit );
21379 
21380 			subtest->ifIsAWDL = true;
21381 			interfaceDesc = "AWDL interface";
21382 			break;
21383 
21384 		default:
21385 			err = kParamErr;
21386 			goto exit;
21387 	}
21388 
21389 	// Create description.
21390 
21391 	ASPrintF( &subtest->description, "Service registration over %s using %s service name.%s",
21392 		interfaceDesc, subtest->useDefaultName ? "default" : "custom",
21393 		subtest->useLODiscovery ? " (LocalOnly discovery)" : "" );
21394 	require_action( subtest->description, exit, err = kNoMemoryErr );
21395 
21396 	if( subtest->skipped )
21397 	{
21398 		subtest->startTime = NanoTimeGetCurrent();
21399 	}
21400 	else
21401 	{
21402 		// Generate a service name.
21403 
21404 		if( subtest->useDefaultName )
21405 		{
21406 			subtest->serviceName = inTest->computerName;
21407 		}
21408 		else
21409 		{
21410 			ASPrintF( &subtest->serviceNameCustom, "dnssdutil-regtest-service-name-%s",
21411 				_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
21412 			require_action( subtest->serviceNameCustom, exit, err = kNoMemoryErr );
21413 
21414 			subtest->serviceName = subtest->serviceNameCustom;
21415 		}
21416 
21417 		// Generate a service type.
21418 
21419 		ASPrintF( &subtest->serviceType, "_regtest-%s._udp",
21420 			_RandomStringExact( kLowerAlphaNumericCharSet, kLowerAlphaNumericCharSetSize, sizeof( tag ) - 1, tag ) );
21421 		require_action( subtest->serviceType, exit, err = kNoMemoryErr );
21422 
21423 		subtest->serviceTypeLen = strlen( subtest->serviceType );
21424 
21425 		// Create SRV and TXT record name FQDN.
21426 
21427 		ASPrintF( &subtest->serviceFQDN, "%s.%s.local.", subtest->serviceName, subtest->serviceType );
21428 		require_action( subtest->serviceFQDN, exit, err = kNoMemoryErr );
21429 
21430 		// Generate a port number.
21431 
21432 		subtest->port = (uint16_t) RandomRange( 60000, 65535 );
21433 
21434 		// Generate TXT record data.
21435 
21436 		err = _RegistrationTestCreateRandomTXTRecord( 100, 1000, &subtest->txtPtr, &subtest->txtLen );
21437 		require_noerr( err, exit );
21438 
21439 		// Register service.
21440 
21441 		subtest->startTime = NanoTimeGetCurrent();
21442 
21443 		flags = kDNSServiceFlagsNoAutoRename;
21444 		if( subtest->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
21445 		err = DNSServiceRegister( &subtest->registration, flags, subtest->ifIndex,
21446 			subtest->useDefaultName ? NULL : subtest->serviceNameCustom, subtest->serviceType, "local.",
21447 			NULL, htons( subtest->port ), (uint16_t) subtest->txtLen, subtest->txtPtr,
21448 			_RegistrationSubtestRegisterCallback, subtest );
21449 		require_noerr( err, exit );
21450 
21451 		err = DNSServiceSetDispatchQueue( subtest->registration, dispatch_get_main_queue() );
21452 		require_noerr( err, exit );
21453 
21454 		// Start timer.
21455 
21456 		check( !inTest->timer );
21457 		err = DispatchTimerOneShotCreate( dispatch_time_seconds( kRegistrationTestSubtestDurationSecs ),
21458 			INT64_C_safe( kRegistrationTestSubtestDurationSecs ) * kNanosecondsPerSecond / 10, dispatch_get_main_queue(),
21459 			_RegistrationTestTimerHandler, inTest, &inTest->timer );
21460 		require_noerr( err, exit );
21461 		dispatch_resume( inTest->timer );
21462 	}
21463 
21464 	*outSkipped = subtest->skipped;
21465 
21466 	check( !inTest->subtest );
21467 	inTest->subtest = subtest;
21468 	subtest = NULL;
21469 
21470 exit:
21471 	_RegistrationSubtestForget( &subtest );
21472 	return( err );
21473 }
21474 
21475 //===========================================================================================================================
21476 
21477 #define kRegistrationTestReportKey_ComputerName			CFSTR( "computerName" )			// String
21478 #define kRegistrationTestReportKey_Description			CFSTR( "description" )			// String
21479 #define kRegistrationTestReportKey_Domain				CFSTR( "domain" )				// String
21480 #define kRegistrationTestReportKey_EndTime				CFSTR( "endTime" )				// String
21481 #define kRegistrationTestReportKey_Error				CFSTR( "error" )				// Integer
21482 #define kRegistrationTestReportKey_Flags				CFSTR( "flags" )				// Integer
21483 #define kRegistrationTestReportKey_IgnoredResults		CFSTR( "ignoredResults" )		// Array of dictionaries
21484 #define kRegistrationTestReportKey_InterfaceIndex		CFSTR( "ifIndex" )				// Integer
21485 #define kRegistrationTestReportKey_InterfaceName		CFSTR( "ifName" )				// String
21486 #define kRegistrationTestReportKey_LocalHostName		CFSTR( "localHostName" )		// String
21487 #define kRegistrationTestReportKey_MissingResults		CFSTR( "missingResults" )		// Array of dictionaries
21488 #define kRegistrationTestReportKey_Pass					CFSTR( "pass" )					// Boolean
21489 #define kRegistrationTestReportKey_Port					CFSTR( "port" )					// Integer
21490 #define kRegistrationTestReportKey_RDataFormatted		CFSTR( "rdataFormatted" )		// String
21491 #define kRegistrationTestReportKey_RDataHexString		CFSTR( "rdataHexString" )		// String
21492 #define kRegistrationTestReportKey_RecordClass			CFSTR( "recordClass" )			// Integer
21493 #define kRegistrationTestReportKey_RecordType			CFSTR( "recordType" )			// Integer
21494 #define kRegistrationTestReportKey_Registered			CFSTR( "registered" )			// Boolean
21495 #define kRegistrationTestReportKey_ResultType			CFSTR( "resultType" )			// String
21496 #define kRegistrationTestReportKey_ServiceFQDN			CFSTR( "serviceFQDN" )			// String
21497 #define kRegistrationTestReportKey_ServiceName			CFSTR( "serviceName" )			// String
21498 #define kRegistrationTestReportKey_ServiceType			CFSTR( "serviceType" )			// String
21499 #define kRegistrationTestReportKey_Skipped				CFSTR( "skipped" )				// Boolean
21500 #define kRegistrationTestReportKey_StartTime			CFSTR( "startTime" )			// String
21501 #define kRegistrationTestReportKey_Subtests				CFSTR( "subtests" )				// Array of dictionaries
21502 #define kRegistrationTestReportKey_Timestamp			CFSTR( "timestamp" )			// String
21503 #define kRegistrationTestReportKey_TXT					CFSTR( "txt" )					// String
21504 #define kRegistrationTestReportKey_UnexpectedResults	CFSTR( "unexpectedResults" )	// Array of dictionaries
21505 #define kRegistrationTestReportKey_UsedDefaultName		CFSTR( "usedDefaultName" )		// Boolean
21506 #define kRegistrationTestReportKey_UsedLODiscovery		CFSTR( "usedLODiscovery" )		// Boolean
21507 
21508 #define kRegistrationTestResultType_Browse				CFSTR( "browse" )
21509 #define kRegistrationTestResultType_Query				CFSTR( "query" )
21510 #define kRegistrationTestResultType_QuerySRV			CFSTR( "querySRV" )
21511 #define kRegistrationTestResultType_QueryTXT			CFSTR( "queryTXT" )
21512 #define kRegistrationTestResultType_Registration		CFSTR( "registration" )
21513 
21514 static OSStatus
21515 	_RegistrationTestAppendMissingResults(
21516 		CFMutableArrayRef				inMissingResults,
21517 		const RegistrationResultTimes *	inTimes,
21518 		uint32_t						inIfIndex,
21519 		const char *					inIfName );
21520 
_RegistrationTestEndSubtest(RegistrationTest * inTest)21521 static OSStatus	_RegistrationTestEndSubtest( RegistrationTest *inTest )
21522 {
21523 	OSStatus					err;
21524 	RegistrationSubtest *		subtest;
21525 	CFMutableDictionaryRef		subtestReport;
21526 	CFMutableArrayRef			missing;
21527 	char *						txtStr;
21528 	NanoTime64					now;
21529 	Boolean						subtestFailed;
21530 	char						startTime[ 32 ];
21531 	char						endTime[ 32 ];
21532 	char						ifNameBuf[ IF_NAMESIZE + 1 ];
21533 
21534 	now = NanoTimeGetCurrent();
21535 
21536 	subtest = inTest->subtest;
21537 	inTest->subtest = NULL;
21538 	_RegistrationSubtestStop( subtest );
21539 
21540 	missing			= NULL;
21541 	subtestReport	= NULL;
21542 	txtStr			= NULL;
21543 	if( subtest->txtPtr )
21544 	{
21545 		err = DNSRecordDataToString( subtest->txtPtr, subtest->txtLen, kDNSServiceType_TXT, &txtStr );
21546 		require_noerr( err, exit );
21547 	}
21548 	_NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
21549 	_NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
21550 	err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &subtestReport,
21551 		"{"
21552 			"%kO=%s"	// description
21553 			"%kO=%s"	// startTime
21554 			"%kO=%s"	// endTime
21555 			"%kO=%s"	// serviceFQDN
21556 			"%kO=%lli"	// ifIndex
21557 			"%kO=%s"	// ifName
21558 			"%kO=%lli"	// port
21559 			"%kO=%s"	// txt
21560 			"%kO=%b"	// registered
21561 			"%kO=%b"	// usedDefaultName
21562 			"%kO=%b"	// usedLODiscovery
21563 		"}",
21564 		kRegistrationTestReportKey_Description,		subtest->description,
21565 		kRegistrationTestReportKey_StartTime,		startTime,
21566 		kRegistrationTestReportKey_EndTime,			endTime,
21567 		kRegistrationTestReportKey_ServiceFQDN,		subtest->serviceFQDN,
21568 		kRegistrationTestReportKey_InterfaceIndex,	(int64_t) subtest->ifIndex,
21569 		kRegistrationTestReportKey_InterfaceName,	if_indextoname( subtest->ifIndex, ifNameBuf ),
21570 		kRegistrationTestReportKey_Port,			(int64_t) subtest->port,
21571 		kRegistrationTestReportKey_TXT,				txtStr,
21572 		kRegistrationTestReportKey_Registered,		(int) subtest->registered,
21573 		kRegistrationTestReportKey_UsedDefaultName,	(int) subtest->useDefaultName,
21574 		kRegistrationTestReportKey_UsedLODiscovery,	(int) subtest->useLODiscovery );
21575 	ForgetMem( &txtStr );
21576 	require_noerr( err, exit );
21577 
21578 	if( !subtest->skipped && subtest->registered )
21579 	{
21580 		missing = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
21581 		require_action( missing, exit, err = kNoMemoryErr );
21582 
21583 		if( subtest->ifList )
21584 		{
21585 			RegistrationInterfaceItem *		item;
21586 
21587 			for( item = subtest->ifList; item; item = (RegistrationInterfaceItem *) item->base.next )
21588 			{
21589 			#if( TARGET_OS_WATCH )
21590 				if( inTest->forBATS && item->base.isWiFi ) continue;
21591 			#endif
21592 				err = _RegistrationTestAppendMissingResults( missing, &item->times, item->base.ifIndex, item->base.ifName );
21593 				require_noerr( err, exit );
21594 			}
21595 		}
21596 		else
21597 		{
21598 			err = _RegistrationTestAppendMissingResults( missing, &subtest->ifTimes, subtest->ifIndex, NULL );
21599 			require_noerr( err, exit );
21600 		}
21601 
21602 		subtestFailed = false;
21603 		if( CFArrayGetCount( missing ) > 0 )
21604 		{
21605 			subtestFailed = true;
21606 			CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_MissingResults, missing );
21607 		}
21608 		if( CFArrayGetCount( subtest->unexpected ) > 0 )
21609 		{
21610 			subtestFailed = true;
21611 			CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_UnexpectedResults, subtest->unexpected );
21612 		}
21613 	#if( TARGET_OS_WATCH )
21614 		if( CFArrayGetCount( subtest->ignored ) > 0 )
21615 		{
21616 			CFDictionarySetValue( subtestReport, kRegistrationTestReportKey_IgnoredResults, subtest->ignored );
21617 		}
21618 	#endif
21619 	}
21620 	else
21621 	{
21622 		subtestFailed = true;
21623 	}
21624 
21625 	CFDictionarySetBoolean( subtestReport, kRegistrationTestReportKey_Pass, subtestFailed ? false : true );
21626 	if( subtestFailed )
21627 	{
21628 		CFDictionarySetBoolean( subtestReport, kRegistrationTestReportKey_Skipped, subtest->skipped );
21629 		if( !subtest->skipped ) inTest->failed = true;
21630 	}
21631 	CFArrayAppendValue( inTest->subtestReports, subtestReport );
21632 
21633 exit:
21634 	CFReleaseNullSafe( missing );
21635 	CFReleaseNullSafe( subtestReport );
21636 	_RegistrationSubtestFree( subtest );
21637 	return( err );
21638 }
21639 
21640 static OSStatus
21641 	_RegistrationTestAppendMissingResult(
21642 		CFMutableArrayRef	inMissingResults,
21643 		CFStringRef			inType,
21644 		uint32_t			inIfIndex,
21645 		const char *		inIfName );
21646 
21647 static OSStatus
_RegistrationTestAppendMissingResults(CFMutableArrayRef inMissingResults,const RegistrationResultTimes * inTimes,uint32_t inIfIndex,const char * inIfName)21648 	_RegistrationTestAppendMissingResults(
21649 		CFMutableArrayRef				inMissingResults,
21650 		const RegistrationResultTimes *	inTimes,
21651 		uint32_t						inIfIndex,
21652 		const char *					inIfName )
21653 {
21654 	OSStatus		err;
21655 
21656 	if( !inTimes->browseResultTime )
21657 	{
21658 		err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_Browse,
21659 			inIfIndex, inIfName );
21660 		require_noerr( err, exit );
21661 	}
21662 	if( !inTimes->querySRVResultTime )
21663 	{
21664 		err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_QuerySRV,
21665 			inIfIndex, inIfName );
21666 		require_noerr( err, exit );
21667 	}
21668 	if( !inTimes->queryTXTResultTime )
21669 	{
21670 		err = _RegistrationTestAppendMissingResult( inMissingResults, kRegistrationTestResultType_QueryTXT,
21671 			inIfIndex, inIfName );
21672 		require_noerr( err, exit );
21673 	}
21674 	err = kNoErr;
21675 
21676 exit:
21677 	return( err );
21678 }
21679 
21680 static OSStatus
_RegistrationTestAppendMissingResult(CFMutableArrayRef inMissingResults,CFStringRef inType,uint32_t inIfIndex,const char * inIfName)21681 	_RegistrationTestAppendMissingResult(
21682 		CFMutableArrayRef	inMissingResults,
21683 		CFStringRef			inType,
21684 		uint32_t			inIfIndex,
21685 		const char *		inIfName )
21686 {
21687 	OSStatus		err;
21688 	char			ifName[ IF_NAMESIZE + 1 ];
21689 
21690 	err = CFPropertyListAppendFormatted( kCFAllocatorDefault, inMissingResults,
21691 		"{"
21692 			"%kO=%O"	// resultType
21693 			"%kO=%lli"	// ifIndex
21694 			"%kO=%s"	// ifName
21695 		"}",
21696 		kRegistrationTestReportKey_ResultType,		inType,
21697 		kRegistrationTestReportKey_InterfaceIndex,	(int64_t) inIfIndex,
21698 		kRegistrationTestReportKey_InterfaceName,	inIfName ? inIfName : if_indextoname( inIfIndex, ifName ) );
21699 	return( err );
21700 }
21701 
21702 //===========================================================================================================================
21703 
_RegistrationTestEnd(RegistrationTest * inTest)21704 static void	_RegistrationTestEnd( RegistrationTest *inTest )
21705 {
21706 	OSStatus				err;
21707 	NanoTime64				now;
21708 	CFPropertyListRef		plist;
21709 	char					startTime[ 32 ];
21710 	char					endTime[ 32 ];
21711 
21712 	now = NanoTimeGetCurrent();
21713 	_NanoTime64ToTimestamp( inTest->startTime, startTime, sizeof( startTime ) );
21714 	_NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
21715 
21716 	err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
21717 		"{"
21718 			"%kO=%s"	// startTime
21719 			"%kO=%s"	// endTime
21720 			"%kO=%s"	// computerName
21721 			"%kO=%s"	// localHostName
21722 			"%kO=%O"	// subtests
21723 			"%kO=%b"	// pass
21724 		"}",
21725 		kRegistrationTestReportKey_StartTime,		startTime,
21726 		kRegistrationTestReportKey_EndTime,			endTime,
21727 		kRegistrationTestReportKey_ComputerName,	inTest->computerName,
21728 		kRegistrationTestReportKey_LocalHostName,	inTest->localHostName,
21729 		kRegistrationTestReportKey_Subtests,		inTest->subtestReports,
21730 		kRegistrationTestReportKey_Pass,			inTest->failed ? false : true );
21731 	require_noerr( err, exit );
21732 
21733 	err = OutputPropertyList( plist, inTest->outputFormat, inTest->outputFilePath );
21734 	CFRelease( plist );
21735 	require_noerr( err, exit );
21736 
21737 exit:
21738 	_RegistrationTestExit( inTest, err );
21739 }
21740 
21741 //===========================================================================================================================
21742 
_RegistrationTestExit(RegistrationTest * inTest,OSStatus inError)21743 static void	_RegistrationTestExit( RegistrationTest *inTest, OSStatus inError )
21744 {
21745 	int		exitCode;
21746 
21747 	if( inError )
21748 	{
21749 		FPrintF( stderr, "error: %#m\n", inError );
21750 		exitCode = 1;
21751 	}
21752 	else
21753 	{
21754 		exitCode = inTest->failed ? 2 : 0;
21755 	}
21756 	_RegistrationTestForget( &inTest );
21757 	exit( exitCode );
21758 }
21759 
21760 //===========================================================================================================================
21761 
_RegistrationSubtestCreate(RegistrationSubtest ** outSubtest)21762 static OSStatus	_RegistrationSubtestCreate( RegistrationSubtest **outSubtest )
21763 {
21764 	OSStatus					err;
21765 	RegistrationSubtest *		obj;
21766 
21767 	obj = (RegistrationSubtest *) calloc( 1, sizeof( *obj ) );
21768 	require_action( obj, exit, err = kNoMemoryErr );
21769 
21770 	obj->unexpected = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
21771 	require_action( obj->unexpected, exit, err = kNoMemoryErr );
21772 
21773 #if( TARGET_OS_WATCH )
21774 	obj->ignored = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
21775 	require_action( obj->ignored, exit, err = kNoMemoryErr );
21776 #endif
21777 
21778 	*outSubtest = obj;
21779 	obj = NULL;
21780 	err = kNoErr;
21781 
21782 exit:
21783 	if( obj ) _RegistrationSubtestFree( obj );
21784 	return( err );
21785 }
21786 
21787 //===========================================================================================================================
21788 
_RegistrationSubtestFree(RegistrationSubtest * inSubtest)21789 static void	_RegistrationSubtestFree( RegistrationSubtest *inSubtest )
21790 {
21791 	check( !inSubtest->registration );
21792 	check( !inSubtest->browse );
21793 	check( !inSubtest->querySRV );
21794 	check( !inSubtest->queryTXT );
21795 	check( !inSubtest->connection );
21796 	ForgetMem( &inSubtest->serviceNameCustom );
21797 	ForgetMem( &inSubtest->serviceType );
21798 	ForgetMem( &inSubtest->serviceFQDN );
21799 	ForgetMem( &inSubtest->txtPtr );
21800 	ForgetCF( &inSubtest->unexpected );
21801 #if( TARGET_OS_WATCH )
21802 	ForgetCF( &inSubtest->ignored );
21803 #endif
21804 	_MDNSInterfaceListForget( (MDNSInterfaceItem **) &inSubtest->ifList );
21805 	ForgetMem( &inSubtest->description );
21806 	free( inSubtest );
21807 }
21808 
21809 //===========================================================================================================================
21810 
_RegistrationSubtestStop(RegistrationSubtest * inSubtest)21811 static void	_RegistrationSubtestStop( RegistrationSubtest *inSubtest )
21812 {
21813 	DNSServiceForget( &inSubtest->registration );
21814 	DNSServiceForget( &inSubtest->browse );
21815 	DNSServiceForget( &inSubtest->querySRV );
21816 	DNSServiceForget( &inSubtest->queryTXT );
21817 	DNSServiceForget( &inSubtest->connection );
21818 }
21819 
21820 //===========================================================================================================================
21821 
_RegistrationTestInterfaceListCreate(Boolean inIncludeAWDL,RegistrationInterfaceItem ** outList)21822 static OSStatus	_RegistrationTestInterfaceListCreate( Boolean inIncludeAWDL, RegistrationInterfaceItem **outList )
21823 {
21824 	OSStatus						err;
21825 	RegistrationInterfaceItem *		list;
21826 	const MDNSInterfaceSubset		subset = inIncludeAWDL ? kMDNSInterfaceSubset_All : kMDNSInterfaceSubset_NonAWDL;
21827 
21828 	err = _MDNSInterfaceListCreate( subset, sizeof( *list ), (MDNSInterfaceItem **) &list );
21829 	require_noerr( err, exit );
21830 
21831 	*outList = list;
21832 
21833 exit:
21834 	return( err );
21835 }
21836 
21837 //===========================================================================================================================
21838 
21839 static OSStatus
_RegistrationTestCreateRandomTXTRecord(size_t inMinLen,size_t inMaxLen,uint8_t ** outTXTPtr,size_t * outTXTLen)21840 	_RegistrationTestCreateRandomTXTRecord(
21841 		size_t		inMinLen,
21842 		size_t		inMaxLen,
21843 		uint8_t **	outTXTPtr,
21844 		size_t *	outTXTLen )
21845 {
21846 	OSStatus			err;
21847 	uint8_t *			ptr;
21848 	const uint8_t *		txtEnd;
21849 	uint8_t *			txtPtr = NULL;
21850 	size_t				txtLen;
21851 
21852 	require_action_quiet( inMinLen <= inMaxLen, exit, err = kSizeErr );
21853 
21854 	txtLen = RandomRange( inMinLen, inMaxLen );
21855 	txtPtr = (uint8_t *) malloc( txtLen + 1 );
21856 	require_action( txtPtr, exit, err = kNoMemoryErr );
21857 
21858 	_RandomStringExact( kAlphaNumericCharSet, sizeof_string( kAlphaNumericCharSet ), txtLen, (char *)txtPtr );
21859 
21860 	ptr		= txtPtr;
21861 	txtEnd	= &txtPtr[ txtLen ];
21862 	while( ptr < txtEnd )
21863 	{
21864 		size_t		maxLen, len;
21865 
21866 		maxLen = ( (size_t)( txtEnd - ptr ) ) - 1;
21867 		len = RandomRange( 1, 255 );
21868 		if( len > maxLen ) len = maxLen;
21869 
21870 		*ptr = (uint8_t) len;
21871 		ptr += ( 1 + len );
21872 	}
21873 	check( ptr == txtEnd );
21874 
21875 	if( outTXTPtr )
21876 	{
21877 		*outTXTPtr = txtPtr;
21878 		txtPtr = NULL;
21879 	}
21880 	if( outTXTLen ) *outTXTLen = txtLen;
21881 	err = kNoErr;
21882 
21883 exit:
21884 	FreeNullSafe( txtPtr );
21885 	return( err );
21886 }
21887 
21888 //===========================================================================================================================
21889 
21890 static void DNSSD_API
_RegistrationSubtestRegisterCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,DNSServiceErrorType inError,const char * inServiceName,const char * inServiceType,const char * inDomain,void * inContext)21891 	_RegistrationSubtestRegisterCallback(
21892 		DNSServiceRef		inSDRef,
21893 		DNSServiceFlags		inFlags,
21894 		DNSServiceErrorType	inError,
21895 		const char *		inServiceName,
21896 		const char *		inServiceType,
21897 		const char *		inDomain,
21898 		void *				inContext )
21899 {
21900 	OSStatus						err;
21901 	const NanoTime64				now		= NanoTimeGetCurrent();
21902 	RegistrationSubtest * const		subtest	= (RegistrationSubtest *) inContext;
21903 
21904 	Unused( inSDRef );
21905 
21906 	if( ( inFlags & kDNSServiceFlagsAdd ) && !inError &&
21907 		( strcasecmp( inServiceName, subtest->serviceName ) == 0 ) &&
21908 		_RegistrationSubtestValidServiceType( subtest, inServiceType ) &&
21909 		( strcasecmp( inDomain, "local." ) == 0 ) )
21910 	{
21911 		if( !subtest->registered )
21912 		{
21913 			DNSServiceRef				sdRef;
21914 			const DNSServiceFlags		flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsIncludeAWDL;
21915 
21916 			subtest->registered = true;
21917 
21918 			// Create shared connection.
21919 
21920 			check( !subtest->connection );
21921 			err = DNSServiceCreateConnection( &subtest->connection );
21922 			require_noerr( err, exit );
21923 
21924 			err = DNSServiceSetDispatchQueue( subtest->connection, dispatch_get_main_queue() );
21925 			require_noerr( err, exit );
21926 
21927 			// Start browse.
21928 
21929 			check( !subtest->browse );
21930 			sdRef = subtest->connection;
21931 			err = DNSServiceBrowse( &sdRef, flags,
21932 				subtest->useLODiscovery ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny,
21933 				subtest->serviceType, "local.", _RegistrationSubtestBrowseCallback, subtest );
21934 			require_noerr( err, exit );
21935 
21936 			subtest->browse = sdRef;
21937 		}
21938 	}
21939 	else
21940 	{
21941 		char		timestamp[ 32 ];
21942 
21943 		err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtest->unexpected,
21944 			"{"
21945 				"%kO=%O"	// resultType
21946 				"%kO=%s"	// timestamp
21947 				"%kO=%lli"	// flags
21948 				"%kO=%lli"	// error
21949 				"%kO=%s"	// serviceName
21950 				"%kO=%s"	// serviceType
21951 				"%kO=%s"	// domain
21952 			"}",
21953 			kRegistrationTestReportKey_ResultType,	kRegistrationTestResultType_Registration,
21954 			kRegistrationTestReportKey_Timestamp,	_NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
21955 			kRegistrationTestReportKey_Flags,		(int64_t) inFlags,
21956 			kRegistrationTestReportKey_Error,		(int64_t) inError,
21957 			kRegistrationTestReportKey_ServiceName,	inServiceName,
21958 			kRegistrationTestReportKey_ServiceType,	inServiceType,
21959 			kRegistrationTestReportKey_Domain,		inDomain );
21960 		require_noerr( err, exit );
21961 	}
21962 	err = kNoErr;
21963 
21964 exit:
21965 	if( err ) _RegistrationTestExit( subtest->test, err );
21966 }
21967 
21968 //===========================================================================================================================
21969 
21970 static void DNSSD_API
_RegistrationSubtestBrowseCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inIfIndex,DNSServiceErrorType inError,const char * inServiceName,const char * inServiceType,const char * inDomain,void * inContext)21971 	_RegistrationSubtestBrowseCallback(
21972 		DNSServiceRef		inSDRef,
21973 		DNSServiceFlags		inFlags,
21974 		uint32_t			inIfIndex,
21975 		DNSServiceErrorType	inError,
21976 		const char *		inServiceName,
21977 		const char *		inServiceType,
21978 		const char *		inDomain,
21979 		void *				inContext )
21980 {
21981 	OSStatus						err;
21982 	NanoTime64						now;
21983 	RegistrationSubtest * const		subtest = (RegistrationSubtest *) inContext;
21984 	Boolean							serviceIsCorrect, resultIsExpected;
21985 
21986 	Unused( inSDRef );
21987 
21988 	now = NanoTimeGetCurrent();
21989 	if( !inError && ( strcasecmp( inServiceName, subtest->serviceName ) == 0 ) &&
21990 		_RegistrationSubtestValidServiceType( subtest, inServiceType ) && ( strcasecmp( inDomain, "local." ) == 0 ) )
21991 	{
21992 		serviceIsCorrect = true;
21993 	}
21994 	else
21995 	{
21996 		serviceIsCorrect = false;
21997 	}
21998 
21999 	resultIsExpected = false;
22000 	if( serviceIsCorrect && ( inFlags & kDNSServiceFlagsAdd ) )
22001 	{
22002 		RegistrationResultTimes *		times;
22003 
22004 		times = _RegistrationSubtestGetInterfaceResultTimes( subtest, inIfIndex, NULL );
22005 		if( times )
22006 		{
22007 			DNSServiceRef				sdRef;
22008 			const DNSServiceFlags		flags = kDNSServiceFlagsShareConnection | kDNSServiceFlagsIncludeAWDL;
22009 			uint32_t					ifIndex;
22010 
22011 			resultIsExpected = true;
22012 			if( !times->browseResultTime ) times->browseResultTime = now;
22013 
22014 			ifIndex = subtest->useLODiscovery ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny;
22015 			if( !subtest->querySRV )
22016 			{
22017 				// Start SRV record query.
22018 
22019 				sdRef = subtest->connection;
22020 				err = DNSServiceQueryRecord( &sdRef, flags, ifIndex, subtest->serviceFQDN, kDNSServiceType_SRV,
22021 					kDNSServiceClass_IN, _RegistrationSubtestQueryCallback, subtest );
22022 				require_noerr( err, exit );
22023 
22024 				subtest->querySRV = sdRef;
22025 			}
22026 			if( !subtest->queryTXT )
22027 			{
22028 				// Start TXT record query.
22029 
22030 				sdRef = subtest->connection;
22031 				err = DNSServiceQueryRecord( &sdRef, flags, ifIndex, subtest->serviceFQDN, kDNSServiceType_TXT,
22032 					kDNSServiceClass_IN, _RegistrationSubtestQueryCallback, subtest );
22033 				require_noerr( err, exit );
22034 
22035 				subtest->queryTXT = sdRef;
22036 			}
22037 		}
22038 	}
22039 
22040 	if( !resultIsExpected )
22041 	{
22042 		CFMutableArrayRef		resultArray;
22043 		char					timestamp[ 32 ];
22044 		const char *			ifNamePtr;
22045 		char					ifNameBuf[ IF_NAMESIZE + 1 ];
22046 
22047 		ifNamePtr = if_indextoname( inIfIndex, ifNameBuf );
22048 		resultArray = subtest->unexpected;
22049 	#if( TARGET_OS_WATCH )
22050 		if( subtest->test->forBATS && ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) &&
22051 			ifNamePtr && _RegistrationTestInterfaceIsWiFi( ifNamePtr ) && serviceIsCorrect )
22052 		{
22053 			resultArray = subtest->ignored;
22054 		}
22055 	#endif
22056 		err = CFPropertyListAppendFormatted( kCFAllocatorDefault, resultArray,
22057 			"{"
22058 				"%kO=%O"	// resultType
22059 				"%kO=%s"	// timestamp
22060 				"%kO=%lli"	// flags
22061 				"%kO=%lli"	// ifIndex
22062 				"%kO=%s"	// ifName
22063 				"%kO=%lli"	// error
22064 				"%kO=%s"	// serviceName
22065 				"%kO=%s"	// serviceType
22066 				"%kO=%s"	// domain
22067 			"}",
22068 			kRegistrationTestReportKey_ResultType,		kRegistrationTestResultType_Browse,
22069 			kRegistrationTestReportKey_Timestamp,		_NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
22070 			kRegistrationTestReportKey_Flags,			(int64_t) inFlags,
22071 			kRegistrationTestReportKey_InterfaceIndex,	(int64_t) inIfIndex,
22072 			kRegistrationTestReportKey_InterfaceName,	ifNamePtr,
22073 			kRegistrationTestReportKey_Error,			(int64_t) inError,
22074 			kRegistrationTestReportKey_ServiceName,		inServiceName,
22075 			kRegistrationTestReportKey_ServiceType,		inServiceType,
22076 			kRegistrationTestReportKey_Domain,			inDomain );
22077 		require_noerr( err, exit );
22078 	}
22079 	err = kNoErr;
22080 
22081 exit:
22082 	if( err ) _RegistrationTestExit( subtest->test, err );
22083 }
22084 
22085 //===========================================================================================================================
22086 
22087 static Boolean
22088 	_RegistrationSubtestIsSRVRecordDataValid(
22089 		RegistrationSubtest *	inSubtest,
22090 		const uint8_t *			inRDataPtr,
22091 		size_t					inRDataLen,
22092 		Boolean					inExpectRandHostname );
22093 
22094 static void DNSSD_API
_RegistrationSubtestQueryCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inIfIndex,DNSServiceErrorType inError,const char * inName,uint16_t inType,uint16_t inClass,uint16_t inRDataLen,const void * inRDataPtr,uint32_t inTTL,void * inContext)22095 	_RegistrationSubtestQueryCallback(
22096 		DNSServiceRef			inSDRef,
22097 		DNSServiceFlags			inFlags,
22098 		uint32_t				inIfIndex,
22099 		DNSServiceErrorType		inError,
22100 		const char *			inName,
22101 		uint16_t				inType,
22102 		uint16_t				inClass,
22103 		uint16_t				inRDataLen,
22104 		const void *			inRDataPtr,
22105 		uint32_t				inTTL,
22106 		void *					inContext )
22107 {
22108 	OSStatus						err;
22109 	NanoTime64						now;
22110 	RegistrationSubtest * const		subtest = (RegistrationSubtest *) inContext;
22111 	Boolean							resultIsExpected;
22112 
22113 	Unused( inSDRef );
22114 	Unused( inTTL );
22115 
22116 	now = NanoTimeGetCurrent();
22117 	resultIsExpected = false;
22118 	if( ( inFlags & kDNSServiceFlagsAdd ) && !inError && ( strcasecmp( inName, subtest->serviceFQDN ) == 0 ) &&
22119 		( inClass == kDNSServiceClass_IN ) )
22120 	{
22121 		RegistrationResultTimes *		times;
22122 		Boolean							isAWDL;
22123 
22124 		times = _RegistrationSubtestGetInterfaceResultTimes( subtest, inIfIndex, &isAWDL );
22125 		if( times )
22126 		{
22127 			if( inType == kDNSServiceType_SRV )
22128 			{
22129 				Boolean		expectRandHostname;
22130 
22131 				if( isAWDL || ( ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) && subtest->includeAWDL ) )
22132 				{
22133 					expectRandHostname = true;
22134 				}
22135 				else
22136 				{
22137 					expectRandHostname = false;
22138 				}
22139 				if( _RegistrationSubtestIsSRVRecordDataValid( subtest, inRDataPtr, inRDataLen, expectRandHostname ) )
22140 				{
22141 					resultIsExpected = true;
22142 					if( !times->querySRVResultTime ) times->querySRVResultTime = now;
22143 				}
22144 			}
22145 			else if( inType == kDNSServiceType_TXT )
22146 			{
22147 				if( MemEqual( inRDataPtr, inRDataLen, subtest->txtPtr, subtest->txtLen ) )
22148 				{
22149 					resultIsExpected = true;
22150 					if( !times->queryTXTResultTime ) times->queryTXTResultTime = now;
22151 				}
22152 			}
22153 		}
22154 	}
22155 
22156 	if( !resultIsExpected )
22157 	{
22158 		CFMutableArrayRef			resultArray;
22159 		CFMutableDictionaryRef		resultDict;
22160 		CFStringRef					rdataKey;
22161 		char *						rdataStr;
22162 		const char *				ifNamePtr;
22163 		char						timestamp[ 32 ];
22164 		char						ifNameBuf[ IF_NAMESIZE + 1 ];
22165 
22166 		ifNamePtr = if_indextoname( inIfIndex, ifNameBuf );
22167 		resultArray = subtest->unexpected;
22168 	#if( TARGET_OS_WATCH )
22169 		if( subtest->test->forBATS && ( subtest->ifIndex == kDNSServiceInterfaceIndexAny ) &&
22170 			ifNamePtr && _RegistrationTestInterfaceIsWiFi( ifNamePtr ) && !inError &&
22171 			( strcasecmp( inName, subtest->serviceFQDN ) == 0 ) )
22172 		{
22173 			if( inType == kDNSServiceType_SRV )
22174 			{
22175 				const Boolean		expectRandHostname = subtest->includeAWDL ? true : false;
22176 
22177 				if( _RegistrationSubtestIsSRVRecordDataValid( subtest, inRDataPtr, inRDataLen, expectRandHostname ) )
22178 				{
22179 					resultArray = subtest->ignored;
22180 				}
22181 			}
22182 			else if( inType == kDNSServiceType_TXT )
22183 			{
22184 				if( MemEqual( inRDataPtr, inRDataLen, subtest->txtPtr, subtest->txtLen ) )
22185 				{
22186 					resultArray = subtest->ignored;
22187 				}
22188 			}
22189 		}
22190 	#endif
22191 		err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &resultDict,
22192 			"{"
22193 				"%kO=%O"	// resultType
22194 				"%kO=%s"	// timestamp
22195 				"%kO=%lli"	// flags
22196 				"%kO=%lli"	// ifIndex
22197 				"%kO=%s"	// ifName
22198 				"%kO=%lli"	// error
22199 				"%kO=%s"	// serviceFQDN
22200 				"%kO=%lli"	// recordType
22201 				"%kO=%lli"	// class
22202 			"}",
22203 			kRegistrationTestReportKey_ResultType,		kRegistrationTestResultType_Query,
22204 			kRegistrationTestReportKey_Timestamp,		_NanoTime64ToTimestamp( now, timestamp, sizeof( timestamp ) ),
22205 			kRegistrationTestReportKey_Flags,			(int64_t) inFlags,
22206 			kRegistrationTestReportKey_InterfaceIndex,	(int64_t) inIfIndex,
22207 			kRegistrationTestReportKey_InterfaceName,	ifNamePtr,
22208 			kRegistrationTestReportKey_Error,			(int64_t) inError,
22209 			kRegistrationTestReportKey_ServiceFQDN,		inName,
22210 			kRegistrationTestReportKey_RecordType,		(int64_t) inType,
22211 			kRegistrationTestReportKey_RecordClass,		(int64_t) inClass );
22212 		require_noerr( err, exit );
22213 
22214 		rdataStr = NULL;
22215 		DNSRecordDataToString( inRDataPtr, inRDataLen, inType, &rdataStr );
22216 		if( rdataStr )
22217 		{
22218 			rdataKey = kRegistrationTestReportKey_RDataFormatted;
22219 		}
22220 		else
22221 		{
22222 			ASPrintF( &rdataStr, "%#H", inRDataPtr, inRDataLen, inRDataLen );
22223 			require_action( rdataStr, exit, err = kNoMemoryErr );
22224 
22225 			rdataKey = kRegistrationTestReportKey_RDataHexString;
22226 		}
22227 		err = CFDictionarySetCString( resultDict, rdataKey, rdataStr, kSizeCString );
22228 		ForgetMem( &rdataStr );
22229 		if( err ) CFRelease( resultDict );
22230 		require_noerr( err, exit );
22231 
22232 		CFArrayAppendValue( resultArray, resultDict );
22233 		CFRelease( resultDict );
22234 	}
22235 	err = kNoErr;
22236 
22237 exit:
22238 	if( err ) _RegistrationTestExit( subtest->test, err );
22239 }
22240 
22241 static Boolean
_RegistrationSubtestIsSRVRecordDataValid(RegistrationSubtest * inSubtest,const uint8_t * inRDataPtr,size_t inRDataLen,Boolean inExpectRandHostname)22242 	_RegistrationSubtestIsSRVRecordDataValid(
22243 		RegistrationSubtest *	inSubtest,
22244 		const uint8_t *			inRDataPtr,
22245 		size_t					inRDataLen,
22246 		Boolean					inExpectRandHostname )
22247 {
22248 	const dns_fixed_fields_srv *		fields;
22249 	const uint8_t * const				end		= &inRDataPtr[ inRDataLen ];
22250 	const uint8_t *						label;
22251 	size_t								len;
22252 	uint16_t							port;
22253 	Boolean								isValid;
22254 
22255 	isValid = false;
22256 	require_quiet( inRDataLen >= sizeof( dns_fixed_fields_srv ), exit );
22257 
22258 	fields	= (const dns_fixed_fields_srv *) inRDataPtr;
22259 	port	= dns_fixed_fields_srv_get_port( fields );
22260 	require_quiet( port == inSubtest->port, exit );
22261 
22262 	// First target label should be a UUID string for the AWDL interface.
22263 
22264 	label = (const uint8_t *) &fields[ 1 ];
22265 	require_quiet( ( end - label ) >= 1, exit );
22266 
22267 	len = label[ 0 ];
22268 	require_quiet( ( (size_t)( end - label ) ) >= ( 1 + len ), exit );
22269 
22270 	if( inExpectRandHostname )
22271 	{
22272 		if( StringToUUID( (const char *) &label[ 1 ], len, false, NULL ) != kNoErr ) goto exit;
22273 	}
22274 	else
22275 	{
22276 		if( strnicmpx( &label[ 1 ], len, inSubtest->test->localHostName ) != 0 ) goto exit;
22277 	}
22278 
22279 	// Second target label should be "local".
22280 
22281 	label = &label[ 1 + len ];
22282 	require_quiet( ( end - label ) >= 1, exit );
22283 
22284 	len = label[ 0 ];
22285 	require_quiet( ( (size_t)( end - label ) ) >= ( 1 + len ), exit );
22286 
22287 	if( ( len != kLocalLabel[ 0 ] ) || ( _memicmp( &label[ 1 ], &kLocalLabel[ 1 ], kLocalLabel[ 0 ] ) != 0 ) ) goto exit;
22288 
22289 	// Third target label should be the root label.
22290 
22291 	label = &label[ 1 + len ];
22292 	require_quiet( ( end - label ) >= 1, exit );
22293 
22294 	len = label[ 0 ];
22295 	if( len != 0 ) goto exit;
22296 
22297 	isValid = true;
22298 
22299 exit:
22300 	return( isValid );
22301 }
22302 
22303 //===========================================================================================================================
22304 
_RegistrationSubtestValidServiceType(const RegistrationSubtest * inSubtest,const char * inServiceType)22305 static Boolean	_RegistrationSubtestValidServiceType( const RegistrationSubtest *inSubtest, const char *inServiceType )
22306 {
22307 	if( stricmp_prefix( inServiceType, inSubtest->serviceType ) == 0 )
22308 	{
22309 		const char * const		ptr = &inServiceType[ inSubtest->serviceTypeLen ];
22310 
22311 		if( ( ptr[ 0 ] == '\0' ) || ( ( ptr[ 0 ] == '.' ) && ( ptr[ 1 ] == '\0' ) ) ) return( true );
22312 	}
22313 	return( false );
22314 }
22315 
22316 //===========================================================================================================================
22317 
22318 static RegistrationResultTimes *
_RegistrationSubtestGetInterfaceResultTimes(RegistrationSubtest * inSubtest,uint32_t inIfIndex,Boolean * outIsAWDL)22319 	_RegistrationSubtestGetInterfaceResultTimes(
22320 		RegistrationSubtest *	inSubtest,
22321 		uint32_t				inIfIndex,
22322 		Boolean *				outIsAWDL )
22323 {
22324 	if( inSubtest->ifList )
22325 	{
22326 		RegistrationInterfaceItem *		item;
22327 
22328 		for( item = inSubtest->ifList; item; item = (RegistrationInterfaceItem *) item->base.next )
22329 		{
22330 			if( inIfIndex == item->base.ifIndex )
22331 			{
22332 				if( outIsAWDL ) *outIsAWDL = item->base.isAWDL ? true : false;
22333 				return( &item->times );
22334 			}
22335 		}
22336 	}
22337 	else
22338 	{
22339 		if( inIfIndex == inSubtest->ifIndex )
22340 		{
22341 			if( outIsAWDL ) *outIsAWDL = inSubtest->ifIsAWDL ? true : false;
22342 			return( &inSubtest->ifTimes );
22343 		}
22344 	}
22345 	return( NULL );
22346 }
22347 
22348 //===========================================================================================================================
22349 
_RegistrationTestTimerHandler(void * inContext)22350 static void	_RegistrationTestTimerHandler( void *inContext )
22351 {
22352 	RegistrationTest * const		test = (RegistrationTest *) inContext;
22353 
22354 	dispatch_source_forget( &test->timer );
22355 	_RegistrationTestProceed( test );
22356 }
22357 
22358 //===========================================================================================================================
22359 
22360 #if( TARGET_OS_WATCH )
_RegistrationTestInterfaceIsWiFi(const char * inIfName)22361 static Boolean	_RegistrationTestInterfaceIsWiFi( const char *inIfName )
22362 {
22363 	NetTransportType		type = kNetTransportType_Undefined;
22364 
22365 	SocketGetInterfaceInfo( kInvalidSocketRef, inIfName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &type );
22366 	return( ( type == kNetTransportType_WiFi ) ? true : false );
22367 }
22368 #endif
22369 
22370 #if( TARGET_OS_DARWIN )
22371 //===========================================================================================================================
22372 //	KeepAliveTestCmd
22373 //===========================================================================================================================
22374 
22375 typedef enum
22376 {
22377 	kKeepAliveCallVariant_Null				= 0,
22378 	kKeepAliveCallVariant_TakesSocket		= 1, // DNSServiceSleepKeepalive(), which takes a connected socket.
22379 	kKeepAliveCallVariant_TakesSockAddrs	= 2, // DNSServiceSleepKeepalive_sockaddr(), which takes connection's sockaddrs.
22380 
22381 }	KeepAliveCallVariant;
22382 
22383 typedef struct
22384 {
22385 	int							family;			// TCP connection's address family.
22386 	KeepAliveCallVariant		callVariant;	// Describes which DNSServiceSleepKeepalive* call to use.
22387 	const char *				description;	// Subtest description.
22388 
22389 }	KeepAliveSubtestParams;
22390 
22391 const KeepAliveSubtestParams		kKeepAliveSubtestParams[] =
22392 {
22393 	{ AF_INET,  kKeepAliveCallVariant_TakesSocket,    "Calls DNSServiceSleepKeepalive() for IPv4 TCP connection." },
22394 	{ AF_INET,  kKeepAliveCallVariant_TakesSockAddrs, "Calls DNSServiceSleepKeepalive_sockaddr() for IPv4 TCP connection." },
22395 	{ AF_INET6, kKeepAliveCallVariant_TakesSocket,    "Calls DNSServiceSleepKeepalive() for IPv6 TCP connection." },
22396 	{ AF_INET6, kKeepAliveCallVariant_TakesSockAddrs, "Calls DNSServiceSleepKeepalive_sockaddr() for IPv6 TCP connection." }
22397 };
22398 
22399 typedef struct
22400 {
22401 	sockaddr_ip			local;			// TCP connection's local address and port.
22402 	sockaddr_ip			remote;			// TCP connection's remote address and port.
22403 	NanoTime64			startTime;		// Subtest's start time.
22404 	NanoTime64			endTime;		// Subtest's end time.
22405 	SocketRef			clientSock;		// Socket for client-side of TCP connection.
22406 	SocketRef			serverSock;		// Socket for server-side of TCP connection.
22407 	char *				recordName;		// Keepalive record's name.
22408 	char *				dataStr;		// Data expected to be contained in keepalive record's data.
22409 	const char *		description;	// Subtests's description.
22410 	unsigned int		timeoutKA;		// Randomly-generated timeout value that gets put in keepalive record's rdata.
22411 	OSStatus			error;			// Subtest's error.
22412 
22413 }	KeepAliveSubtest;
22414 
22415 typedef struct KeepAliveTest *		KeepAliveTestRef;
22416 
22417 typedef struct
22418 {
22419 	KeepAliveTestRef		test;	// Weak back pointer to test.
22420 
22421 }	KeepAliveTestConnectionContext;
22422 
22423 struct KeepAliveTest
22424 {
22425 	dispatch_queue_t						queue;			// Serial queue for test events.
22426 	dispatch_semaphore_t					doneSem;		// Semaphore to signal when the test is done.
22427 	dispatch_source_t						readSource;		// Read source for TCP listener socket.
22428 	DNSServiceRef							keepalive;		// DNSServiceSleepKeepalive{,sockaddr} operation.
22429 	DNSServiceRef							query;			// Query to verify registered keepalive record.
22430 	dispatch_source_t						timer;			// Timer to put time limit on query.
22431 	AsyncConnectionRef						connection;		// Establishes current subtest's TCP connection.
22432 	KeepAliveTestConnectionContext *		connectionCtx;	// Weak pointer to connection's context.
22433 	NanoTime64								startTime;		// Test's start time.
22434 	NanoTime64								endTime;		// Test's end time.
22435 	OSStatus								error;			// Test's error.
22436 	size_t									subtestIdx;		// Index of current subtest.
22437 	KeepAliveSubtest						subtests[ 4 ];	// Subtest array.
22438 };
22439 check_compile_time( countof_field( struct KeepAliveTest, subtests ) == countof( kKeepAliveSubtestParams ) );
22440 
22441 ulog_define_ex( kDNSSDUtilIdentifier, KeepAliveTest, kLogLevelInfo, kLogFlags_None, "KeepAliveTest", NULL );
22442 #define kat_ulog( LEVEL, ... )		ulog( &log_category_from_name( KeepAliveTest ), (LEVEL), __VA_ARGS__ )
22443 
22444 static OSStatus	_KeepAliveTestCreate( KeepAliveTestRef *outTest );
22445 static OSStatus	_KeepAliveTestRun( KeepAliveTestRef inTest );
22446 static void		_KeepAliveTestFree( KeepAliveTestRef inTest );
22447 
KeepAliveTestCmd(void)22448 static void	KeepAliveTestCmd( void )
22449 {
22450 	OSStatus				err;
22451 	OutputFormatType		outputFormat;
22452 	KeepAliveTestRef		test		= NULL;
22453 	CFPropertyListRef		plist		= NULL;
22454 	CFMutableArrayRef		subtests;
22455 	size_t					i;
22456 	size_t					subtestFailCount;
22457 	Boolean					testPassed	= false;
22458 	char					startTime[ 32 ];
22459 	char					endTime[ 32 ];
22460 
22461 	err = OutputFormatFromArgString( gKeepAliveTest_OutputFormat, &outputFormat );
22462 	require_noerr_quiet( err, exit );
22463 
22464 	err = _KeepAliveTestCreate( &test );
22465 	require_noerr( err, exit );
22466 
22467 	err = _KeepAliveTestRun( test );
22468 	require_noerr( err, exit );
22469 
22470 	_NanoTime64ToTimestamp( test->startTime, startTime, sizeof( startTime ) );
22471 	_NanoTime64ToTimestamp( test->endTime, endTime, sizeof( endTime ) );
22472 	err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
22473 		"{"
22474 			"%kO=%s"	// startTime
22475 			"%kO=%s"	// endTime
22476 			"%kO=[%@]"	// subtests
22477 		"}",
22478 		CFSTR( "startTime" ),	startTime,
22479 		CFSTR( "endTime" ),		endTime,
22480 		CFSTR( "subtests" ),	&subtests );
22481 	require_noerr( err, exit );
22482 
22483 	subtestFailCount = 0;
22484 	check( test->subtestIdx == countof( test->subtests ) );
22485 	for( i = 0; i < countof( test->subtests ); ++i )
22486 	{
22487 		KeepAliveSubtest * const		subtest = &test->subtests[ i ];
22488 		char							errorDesc[ 128 ];
22489 
22490 		_NanoTime64ToTimestamp( subtest->startTime, startTime, sizeof( startTime ) );
22491 		_NanoTime64ToTimestamp( subtest->endTime, endTime, sizeof( endTime ) );
22492 		SNPrintF( errorDesc, sizeof( errorDesc ), "%m", subtest->error );
22493 		err = CFPropertyListAppendFormatted( kCFAllocatorDefault, subtests,
22494 			"{"
22495 				"%kO=%s"		// startTime
22496 				"%kO=%s"		// endTime
22497 				"%kO=%s"		// description
22498 				"%kO=%##a"		// localAddr
22499 				"%kO=%##a"		// remoteAddr
22500 				"%kO=%s"		// recordName
22501 				"%kO=%s"		// expectedRData
22502 				"%kO="			// error
22503 				"{"
22504 					"%kO=%lli"	// code
22505 					"%kO=%s"	// description
22506 				"}"
22507 			"}",
22508 			CFSTR( "startTime" ),		startTime,
22509 			CFSTR( "endTime" ),			endTime,
22510 			CFSTR( "description" ),		subtest->description,
22511 			CFSTR( "localAddr" ),		&subtest->local.sa,
22512 			CFSTR( "remoteAddr" ),		&subtest->remote.sa,
22513 			CFSTR( "recordName" ),		subtest->recordName,
22514 			CFSTR( "expectedRData" ),	subtest->dataStr,
22515 			CFSTR( "error" ),
22516 			CFSTR( "code" ),			(int64_t) subtest->error,
22517 			CFSTR( "description" ),		errorDesc
22518 		);
22519 		require_noerr( err, exit );
22520 		if( subtest->error ) ++subtestFailCount;
22521 	}
22522 	if( subtestFailCount == 0 ) testPassed = true;
22523 	CFPropertyListAppendFormatted( kCFAllocatorDefault, plist, "%kO=%b", CFSTR( "pass" ), testPassed );
22524 
22525 	err = OutputPropertyList( plist, outputFormat, gKeepAliveTest_OutputFilePath );
22526 	require_noerr( err, exit );
22527 
22528 exit:
22529 	if( test ) _KeepAliveTestFree( test );
22530 	CFReleaseNullSafe( plist );
22531     gExitCode = err ? 1 : ( testPassed ? 0 : 2 );
22532 }
22533 
22534 //===========================================================================================================================
22535 
22536 static void					_KeepAliveTestStart( void *inCtx );
22537 static void					_KeepAliveTestStop( KeepAliveTestRef inTest, OSStatus inError );
22538 static OSStatus				_KeepAliveTestStartSubtest( KeepAliveTestRef inTest );
22539 static void					_KeepAliveTestStopSubtest( KeepAliveTestRef inTest );
22540 static KeepAliveSubtest *	_KeepAliveTestGetSubtest( KeepAliveTestRef inTest );
22541 static const char *			_KeepAliveTestGetSubtestLogPrefix( KeepAliveTestRef inTest, char *inBufPtr, size_t inBufLen );
22542 static OSStatus				_KeepAliveTestContinue( KeepAliveTestRef inTest, OSStatus inSubtestError, Boolean *outDone );
22543 static void					_KeepAliveTestTCPAcceptHandler( void *inCtx );
22544 static void					_KeepAliveTestConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg );
22545 static void					_KeepAliveTestHandleConnection( KeepAliveTestRef inTest, SocketRef inSock, OSStatus inError );
22546 static void					_KeepAliveTestForgetConnection( KeepAliveTestRef inTest );
22547 static void DNSSD_API		_KeepAliveTestKeepaliveCallback( DNSServiceRef inSDRef, DNSServiceErrorType inErr, void *inCtx );
22548 static void					_KeepAliveTestQueryTimerHandler( void *inCtx );
22549 static void DNSSD_API
22550 	_KeepAliveTestQueryRecordCallback(
22551 		DNSServiceRef		inSDRef,
22552 		DNSServiceFlags		inFlags,
22553 		uint32_t			inInterfaceIndex,
22554 		DNSServiceErrorType	inError,
22555 		const char *		inFullName,
22556 		uint16_t			inType,
22557 		uint16_t			inClass,
22558 		uint16_t			inRDataLen,
22559 		const void *		inRDataPtr,
22560 		uint32_t			inTTL,
22561 		void *				inCtx );
22562 
_KeepAliveTestCreate(KeepAliveTestRef * outTest)22563 static OSStatus	_KeepAliveTestCreate( KeepAliveTestRef *outTest )
22564 {
22565 	OSStatus				err;
22566 	KeepAliveTestRef		test;
22567 	size_t					i;
22568 
22569 	test = (KeepAliveTestRef) calloc( 1, sizeof( *test ) );
22570 	require_action( test, exit, err = kNoMemoryErr );
22571 
22572 	test->error = kInProgressErr;
22573 	for( i = 0; i < countof( test->subtests ); ++i )
22574 	{
22575 		KeepAliveSubtest * const		subtest = &test->subtests[ i ];
22576 
22577 		subtest->local.sa.sa_family		= AF_UNSPEC;
22578 		subtest->remote.sa.sa_family	= AF_UNSPEC;
22579 		subtest->clientSock				= kInvalidSocketRef;
22580 		subtest->serverSock				= kInvalidSocketRef;
22581 	}
22582 	test->queue = dispatch_queue_create( "com.apple.dnssdutil.keepalive-test", DISPATCH_QUEUE_SERIAL );
22583 	require_action( test->queue, exit, err = kNoResourcesErr );
22584 
22585 	test->doneSem = dispatch_semaphore_create( 0 );
22586 	require_action( test->doneSem, exit, err = kNoResourcesErr );
22587 
22588 	*outTest = test;
22589 	test = NULL;
22590 	err = kNoErr;
22591 
22592 exit:
22593 	if( test ) _KeepAliveTestFree( test );
22594 	return( err );
22595 }
22596 
22597 //===========================================================================================================================
22598 
_KeepAliveTestRun(KeepAliveTestRef inTest)22599 static OSStatus	_KeepAliveTestRun( KeepAliveTestRef inTest )
22600 {
22601 	dispatch_async_f( inTest->queue, inTest, _KeepAliveTestStart );
22602 	dispatch_semaphore_wait( inTest->doneSem, DISPATCH_TIME_FOREVER );
22603 	return( inTest->error );
22604 }
22605 
22606 //===========================================================================================================================
22607 
_KeepAliveTestFree(KeepAliveTestRef inTest)22608 static void	_KeepAliveTestFree( KeepAliveTestRef inTest )
22609 {
22610 	size_t		i;
22611 
22612 	check( !inTest->readSource );
22613 	check( !inTest->query );
22614 	check( !inTest->timer );
22615 	check( !inTest->keepalive );
22616 	check( !inTest->connection );
22617 	check( !inTest->connectionCtx );
22618 	dispatch_forget( &inTest->queue );
22619 	dispatch_forget( &inTest->doneSem );
22620 	for( i = 0; i < countof( inTest->subtests ); ++i )
22621 	{
22622 		KeepAliveSubtest * const		subtest = &inTest->subtests[ i ];
22623 
22624 		check( !IsValidSocket( subtest->clientSock ) );
22625 		check( !IsValidSocket( subtest->serverSock ) );
22626 		ForgetMem( &subtest->recordName );
22627 		ForgetMem( &subtest->dataStr );
22628 	}
22629 	free( inTest );
22630 }
22631 
22632 //===========================================================================================================================
22633 
_KeepAliveTestStart(void * inCtx)22634 static void _KeepAliveTestStart( void *inCtx )
22635 {
22636 	OSStatus					err;
22637 	const KeepAliveTestRef		test = (KeepAliveTestRef) inCtx;
22638 
22639 	test->error		= kInProgressErr;
22640 	test->startTime	= NanoTimeGetCurrent();
22641 	err = _KeepAliveTestStartSubtest( test );
22642 	require_noerr( err, exit );
22643 
22644 exit:
22645 	if( err ) _KeepAliveTestStop( test, err );
22646 }
22647 
22648 //===========================================================================================================================
22649 
_KeepAliveTestStop(KeepAliveTestRef inTest,OSStatus inError)22650 static void	_KeepAliveTestStop( KeepAliveTestRef inTest, OSStatus inError )
22651 {
22652 	size_t		i;
22653 
22654 	inTest->error	= inError;
22655 	inTest->endTime	= NanoTimeGetCurrent();
22656 	_KeepAliveTestStopSubtest( inTest );
22657 	for( i = 0; i < countof( inTest->subtests ); ++i )
22658 	{
22659 		KeepAliveSubtest * const		subtest = &inTest->subtests[ i ];
22660 
22661 		ForgetSocket( &subtest->clientSock );
22662 		ForgetSocket( &subtest->serverSock );
22663 	}
22664 	dispatch_semaphore_signal( inTest->doneSem );
22665 }
22666 
22667 //===========================================================================================================================
22668 
_KeepAliveTestStartSubtest(KeepAliveTestRef inTest)22669 static OSStatus	_KeepAliveTestStartSubtest( KeepAliveTestRef inTest )
22670 {
22671 	OSStatus									err;
22672 	KeepAliveSubtest * const					subtest		= _KeepAliveTestGetSubtest( inTest );
22673 	const KeepAliveSubtestParams * const		params		= &kKeepAliveSubtestParams[ inTest->subtestIdx ];
22674 	int											port;
22675 	SocketRef									sock		= kInvalidSocketRef;
22676 	const uint32_t								loopbackV4	= htonl( INADDR_LOOPBACK );
22677 	SocketContext *								sockCtx		= NULL;
22678 	KeepAliveTestConnectionContext *			cnxCtx		= NULL;
22679 	Boolean										useIPv4;
22680 	char										serverAddrStr[ 64 ];
22681 	char										prefix[ 64 ];
22682 
22683 	subtest->error			= kInProgressErr;
22684 	subtest->startTime		= NanoTimeGetCurrent();
22685 	subtest->description	= params->description;
22686 
22687 	require_action( ( params->family == AF_INET ) || ( params->family == AF_INET6 ), exit, err = kInternalErr );
22688 
22689 	// Create TCP listener socket.
22690 
22691 	useIPv4 = ( params->family == AF_INET ) ? true : false;
22692 	err = ServerSocketOpenEx( params->family, SOCK_STREAM, IPPROTO_TCP,
22693 		useIPv4 ? ( (const void *) &loopbackV4 ) : ( (const void *) &in6addr_loopback ), kSocketPort_Auto, &port,
22694 		kSocketBufferSize_DontSet, &sock );
22695 	require_noerr( err, exit );
22696 
22697 	if( useIPv4 )	SNPrintF( serverAddrStr, sizeof( serverAddrStr ), "%.4a:%d", &loopbackV4, port );
22698 	else			SNPrintF( serverAddrStr, sizeof( serverAddrStr ), "[%.16a]:%d", in6addr_loopback.s6_addr, port );
22699 	_KeepAliveTestGetSubtestLogPrefix( inTest, prefix, sizeof( prefix ) );
22700 	kat_ulog( kLogLevelInfo, "%s: Will listen for connections on %s\n", prefix, serverAddrStr );
22701 
22702 	sockCtx = SocketContextCreate( sock, inTest, &err );
22703 	require_noerr( err, exit );
22704 	sock = kInvalidSocketRef;
22705 
22706 	// Create read source for TCP listener socket.
22707 
22708 	check( !inTest->readSource );
22709 	err = DispatchReadSourceCreate( sockCtx->sock, inTest->queue, _KeepAliveTestTCPAcceptHandler,
22710 		SocketContextCancelHandler, sockCtx, &inTest->readSource );
22711 	require_noerr( err, exit );
22712 	sockCtx = NULL;
22713 	dispatch_resume( inTest->readSource );
22714 
22715 	cnxCtx = (KeepAliveTestConnectionContext *) calloc( 1, sizeof( *cnxCtx ) );
22716 	require_action( cnxCtx, exit, err = kNoMemoryErr );
22717 
22718 	// Start asynchronous connection to listener socket.
22719 
22720 	kat_ulog( kLogLevelInfo, "%s: Will connect to %s\n", prefix, serverAddrStr );
22721 
22722 	check( !inTest->connection );
22723 	err = AsyncConnection_Connect( &inTest->connection, serverAddrStr, 0, kAsyncConnectionFlags_None,
22724 		5 * UINT64_C_safe( kNanosecondsPerSecond ), kSocketBufferSize_DontSet, kSocketBufferSize_DontSet,
22725 		NULL, NULL, _KeepAliveTestConnectionHandler, cnxCtx, inTest->queue );
22726 	require_noerr( err, exit );
22727 
22728 	cnxCtx->test = inTest;
22729 	check( !inTest->connectionCtx );
22730 	inTest->connectionCtx = cnxCtx;
22731 	cnxCtx = NULL;
22732 
22733 exit:
22734 	ForgetSocket( &sock );
22735 	if( sockCtx ) SocketContextRelease( sockCtx );
22736 	FreeNullSafe( cnxCtx );
22737 	return( err );
22738 }
22739 
22740 //===========================================================================================================================
22741 
_KeepAliveTestStopSubtest(KeepAliveTestRef inTest)22742 static void	_KeepAliveTestStopSubtest( KeepAliveTestRef inTest )
22743 {
22744 	dispatch_source_forget( &inTest->readSource );
22745 	DNSServiceForget( &inTest->keepalive );
22746 	DNSServiceForget( &inTest->query );
22747 	dispatch_source_forget( &inTest->timer );
22748 	_KeepAliveTestForgetConnection( inTest );
22749 }
22750 
22751 //===========================================================================================================================
22752 
_KeepAliveTestGetSubtest(KeepAliveTestRef inTest)22753 static KeepAliveSubtest *	_KeepAliveTestGetSubtest( KeepAliveTestRef inTest )
22754 {
22755 	return( ( inTest->subtestIdx < countof( inTest->subtests ) ) ? &inTest->subtests[ inTest->subtestIdx ] : NULL );
22756 }
22757 
22758 //===========================================================================================================================
22759 
_KeepAliveTestGetSubtestLogPrefix(KeepAliveTestRef inTest,char * inBufPtr,size_t inBufLen)22760 static const char *	_KeepAliveTestGetSubtestLogPrefix( KeepAliveTestRef inTest, char *inBufPtr, size_t inBufLen )
22761 {
22762 	SNPrintF( inBufPtr, inBufLen, "Subtest %zu/%zu", inTest->subtestIdx + 1, countof( inTest->subtests ) );
22763 	return( inBufPtr );
22764 }
22765 
22766 //===========================================================================================================================
22767 
_KeepAliveTestContinue(KeepAliveTestRef inTest,OSStatus inSubtestError,Boolean * outDone)22768 static OSStatus	_KeepAliveTestContinue( KeepAliveTestRef inTest, OSStatus inSubtestError, Boolean *outDone )
22769 {
22770 	OSStatus				err;
22771 	KeepAliveSubtest *		subtest;
22772 
22773 	require_action( inTest->subtestIdx <= countof( inTest->subtests ), exit, err = kInternalErr );
22774 
22775 	if( inTest->subtestIdx < countof( inTest->subtests ) )
22776 	{
22777 		subtest = _KeepAliveTestGetSubtest( inTest );
22778 		_KeepAliveTestStopSubtest( inTest );
22779 		subtest->endTime	= NanoTimeGetCurrent();
22780 		subtest->error		= inSubtestError;
22781 		if( ++inTest->subtestIdx < countof( inTest->subtests ) )
22782 		{
22783 			err = _KeepAliveTestStartSubtest( inTest );
22784 			require_noerr_quiet( err, exit );
22785 		}
22786 	}
22787 	err = kNoErr;
22788 
22789 exit:
22790 	if( outDone ) *outDone = ( !err && ( inTest->subtestIdx == countof( inTest->subtests ) ) ) ? true : false;
22791 	return( err );
22792 }
22793 
22794 //===========================================================================================================================
22795 
_KeepAliveTestTCPAcceptHandler(void * inCtx)22796 static void	_KeepAliveTestTCPAcceptHandler( void *inCtx )
22797 {
22798 	OSStatus						err;
22799 	const SocketContext * const		sockCtx	= (SocketContext *) inCtx;
22800 	const KeepAliveTestRef			test	= (KeepAliveTestRef) sockCtx->userContext;
22801 	KeepAliveSubtest * const		subtest	= _KeepAliveTestGetSubtest( test );
22802 	sockaddr_ip						peer;
22803 	socklen_t						len;
22804 	char							prefix[ 64 ];
22805 
22806 	check( !IsValidSocket( subtest->serverSock ) );
22807 	len = (socklen_t) sizeof( peer );
22808 	subtest->serverSock = accept( sockCtx->sock, &peer.sa, &len );
22809 	err = map_socket_creation_errno( subtest->serverSock );
22810 	require_noerr( err, exit );
22811 
22812 	_KeepAliveTestGetSubtestLogPrefix( test, prefix, sizeof( prefix ) );
22813 	kat_ulog( kLogLevelInfo, "%s: Accepted connection from %##a\n", prefix, &peer.sa );
22814 
22815 	dispatch_source_forget( &test->readSource );
22816 
22817 exit:
22818 	if( err ) _KeepAliveTestStop( test, err );
22819 }
22820 
22821 //===========================================================================================================================
22822 
_KeepAliveTestConnectionHandler(SocketRef inSock,OSStatus inError,void * inArg)22823 static void	_KeepAliveTestConnectionHandler( SocketRef inSock, OSStatus inError, void *inArg )
22824 {
22825 	KeepAliveTestConnectionContext *		ctx		= (KeepAliveTestConnectionContext *) inArg;
22826 	const KeepAliveTestRef					test	= ctx->test;
22827 
22828 	if( test )
22829 	{
22830 		_KeepAliveTestForgetConnection( test );
22831 		_KeepAliveTestHandleConnection( test, inSock, inError );
22832 		inSock = kInvalidSocketRef;
22833 	}
22834 	ForgetSocket( &inSock );
22835 	free( ctx );
22836 }
22837 
22838 //===========================================================================================================================
22839 
22840 #define kKeepAliveTestQueryTimeoutSecs		5
22841 
_KeepAliveTestHandleConnection(KeepAliveTestRef inTest,SocketRef inSock,OSStatus inError)22842 static void	_KeepAliveTestHandleConnection( KeepAliveTestRef inTest, SocketRef inSock, OSStatus inError )
22843 {
22844 	OSStatus								err;
22845 	KeepAliveSubtest * const				subtest			= _KeepAliveTestGetSubtest( inTest );
22846 	const KeepAliveSubtestParams * const	params			= &kKeepAliveSubtestParams[ inTest->subtestIdx ];
22847 	socklen_t								len;
22848     uint32_t								value;
22849 	int										family, i;
22850 	Boolean									subtestFailed	= false;
22851 	Boolean									done;
22852 	char									prefix[ 64 ];
22853 
22854 	require_noerr_action( inError, exit, err = inError );
22855 
22856 	check( !IsValidSocket( subtest->clientSock ) );
22857 	subtest->clientSock = inSock;
22858 	inSock = kInvalidSocketRef;
22859 
22860 	// Get local and remote IP addresses.
22861 
22862 	len = (socklen_t) sizeof( subtest->local );
22863 	err = getsockname( subtest->clientSock, &subtest->local.sa, &len );
22864 	err = map_global_noerr_errno( err );
22865 	require_noerr( err, exit );
22866 
22867 	len = (socklen_t) sizeof( subtest->remote );
22868 	err = getpeername( subtest->clientSock, &subtest->remote.sa, &len );
22869 	err = map_global_noerr_errno( err );
22870 	require_noerr( err, exit );
22871 
22872 	_KeepAliveTestGetSubtestLogPrefix( inTest, prefix, sizeof( prefix ) );
22873 	kat_ulog( kLogLevelInfo, "%s: Connection established: %##a <-> %##a\n",
22874 		prefix, &subtest->local.sa, &subtest->remote.sa );
22875 
22876 	// Call either DNSServiceSleepKeepalive() or DNSServiceSleepKeepalive_sockaddr().
22877 
22878 	check( subtest->timeoutKA == 0 );
22879 	subtest->timeoutKA = (unsigned int) RandomRange( 1, UINT_MAX );
22880 
22881     switch( params->callVariant )
22882 	{
22883 		case kKeepAliveCallVariant_TakesSocket:
22884 			kat_ulog( kLogLevelInfo, "%s: Will call DNSServiceSleepKeepalive() for client-side socket\n", prefix );
22885 			check( !inTest->keepalive );
22886 			err = DNSServiceSleepKeepalive( &inTest->keepalive, 0, subtest->clientSock,
22887 				subtest->timeoutKA, _KeepAliveTestKeepaliveCallback, inTest );
22888 			require_noerr( err, exit );
22889 
22890 			err = DNSServiceSetDispatchQueue( inTest->keepalive, inTest->queue );
22891 			require_noerr( err, exit );
22892 			break;
22893 
22894 		case kKeepAliveCallVariant_TakesSockAddrs:
22895 			kat_ulog( kLogLevelInfo,
22896 				"%s: Will call DNSServiceSleepKeepalive_sockaddr() for local and remote sockaddrs\n", prefix );
22897 			check( !inTest->keepalive );
22898 			if( __builtin_available( macOS 10.15.4, iOS 13.2.2, watchOS 6.2, tvOS 13.2, * ) )
22899 			{
22900 				err = DNSServiceSleepKeepalive_sockaddr( &inTest->keepalive, 0, &subtest->local.sa, &subtest->remote.sa,
22901 					subtest->timeoutKA, _KeepAliveTestKeepaliveCallback, inTest );
22902 				require_noerr( err, exit );
22903 			}
22904 			else
22905 			{
22906 				kat_ulog( kLogLevelError, "DNSServiceSleepKeepalive_sockaddr() is not available on this OS.\n" );
22907 				subtestFailed = true;
22908 				err = kUnsupportedErr;
22909 				goto exit;
22910 			}
22911 			err = DNSServiceSetDispatchQueue( inTest->keepalive, inTest->queue );
22912 			require_noerr( err, exit );
22913 			break;
22914 
22915 		default:
22916 			kat_ulog( kLogLevelError, "%s: Invalid KeepAliveCallVariant value %d\n", prefix, (int) params->callVariant );
22917 			err = kInternalErr;
22918 			goto exit;
22919 	}
22920 	// Use the same logic that the DNSServiceSleepKeepalive functions use to derive a record name and rdata.
22921 
22922 	value = 0;
22923 	family = subtest->local.sa.sa_family;
22924 	if( family == AF_INET )
22925 	{
22926 		const struct sockaddr_in * const		sin = &subtest->local.v4;
22927 		const uint8_t *							ptr;
22928 
22929 		check_compile_time_code( sizeof( sin->sin_addr.s_addr ) == 4 );
22930 		ptr = (const uint8_t *) &sin->sin_addr.s_addr;
22931 		for( i = 0; i < 4; ++i ) value += ptr[ i ];
22932 		value += sin->sin_port;	// Note: No ntohl(). This is what DNSServiceSleepKeepalive does.
22933 
22934 		check( subtest->remote.sa.sa_family == AF_INET );
22935 		ASPrintF( &subtest->dataStr, "t=%u h=%.4a d=%.4a l=%u r=%u",
22936 			subtest->timeoutKA, &subtest->local.v4.sin_addr.s_addr, &subtest->remote.v4.sin_addr.s_addr,
22937 			ntohs( subtest->local.v4.sin_port ), ntohs( subtest->remote.v4.sin_port ) );
22938 		require_action( subtest->dataStr, exit, err = kNoMemoryErr );
22939 	}
22940 	else if( family == AF_INET6 )
22941 	{
22942 		const struct sockaddr_in6 * const		sin6 = &subtest->local.v6;
22943 
22944 		check_compile_time_code( countof( sin6->sin6_addr.s6_addr ) == 16 );
22945 		for( i = 0; i < 16; ++i ) value += sin6->sin6_addr.s6_addr[ i ];
22946 		value += sin6->sin6_port; // Note: No ntohl(). This is what DNSServiceSleepKeepalive does.
22947 
22948 		check( subtest->remote.sa.sa_family == AF_INET6 );
22949 		ASPrintF( &subtest->dataStr, "t=%u H=%.16a D=%.16a l=%u r=%u",
22950 			subtest->timeoutKA, subtest->local.v6.sin6_addr.s6_addr, subtest->remote.v6.sin6_addr.s6_addr,
22951 			ntohs( subtest->local.v6.sin6_port ), ntohs( subtest->remote.v6.sin6_port ) );
22952 		require_action( subtest->dataStr, exit, err = kNoMemoryErr );
22953 	}
22954 	else
22955 	{
22956 		kat_ulog( kLogLevelError, "%s: Unexpected local address family %d\n", prefix, family );
22957 		err = kInternalErr;
22958 		goto exit;
22959 	}
22960 
22961 	// Start query for the new keepalive record.
22962 
22963 	check( !subtest->recordName );
22964 	ASPrintF( &subtest->recordName, "%u._keepalive._dns-sd._udp.local.", value );
22965 	require_action( subtest->recordName, exit, err = kNoMemoryErr );
22966 
22967 	kat_ulog( kLogLevelInfo, "%s: Will query for %s NULL record\n", prefix, subtest->recordName );
22968 	check( !inTest->query );
22969 	err = DNSServiceQueryRecord( &inTest->query, 0, kDNSServiceInterfaceIndexLocalOnly, subtest->recordName,
22970 		kDNSServiceType_NULL, kDNSServiceClass_IN, _KeepAliveTestQueryRecordCallback, inTest );
22971 	require_noerr( err, exit );
22972 
22973 	err = DNSServiceSetDispatchQueue( inTest->query, inTest->queue );
22974 	require_noerr( err, exit );
22975 
22976 	// Start timer to enforce a time limit on the query.
22977 
22978 	check( !inTest->timer );
22979 	err = DispatchTimerOneShotCreate( dispatch_time_seconds( kKeepAliveTestQueryTimeoutSecs ),
22980 		kKeepAliveTestQueryTimeoutSecs * ( INT64_C_safe( kNanosecondsPerSecond ) / 20 ), inTest->queue,
22981 		_KeepAliveTestQueryTimerHandler, inTest, &inTest->timer );
22982 	require_noerr( err, exit );
22983 	dispatch_resume( inTest->timer );
22984 
22985 exit:
22986 	ForgetSocket( &inSock );
22987 	if( subtestFailed )
22988 	{
22989 		err = _KeepAliveTestContinue( inTest, err, &done );
22990 		check_noerr( err );
22991 	}
22992 	else
22993 	{
22994 		done = false;
22995 	}
22996 	if( err || done ) _KeepAliveTestStop( inTest, err );
22997 }
22998 
22999 //===========================================================================================================================
23000 
_KeepAliveTestForgetConnection(KeepAliveTestRef inTest)23001 static void	_KeepAliveTestForgetConnection( KeepAliveTestRef inTest )
23002 {
23003 	if( inTest->connection )
23004 	{
23005 		check( inTest->connectionCtx );
23006 		inTest->connectionCtx->test	= NULL; // Unset the connection's back pointer to test.
23007 		inTest->connectionCtx		= NULL; // Context will be freed by the connection's handler.
23008 		AsyncConnection_Forget( &inTest->connection );
23009 	}
23010 }
23011 
23012 //===========================================================================================================================
23013 
_KeepAliveTestKeepaliveCallback(DNSServiceRef inSDRef,DNSServiceErrorType inError,void * inCtx)23014 static void DNSSD_API	_KeepAliveTestKeepaliveCallback( DNSServiceRef inSDRef, DNSServiceErrorType inError, void *inCtx )
23015 {
23016 	OSStatus					err;
23017 	const KeepAliveTestRef		test	= (KeepAliveTestRef) inCtx;
23018 	char						prefix[ 64 ];
23019 
23020 	Unused( inSDRef );
23021 
23022 	_KeepAliveTestGetSubtestLogPrefix( test, prefix, sizeof( prefix ) );
23023 	kat_ulog( kLogLevelInfo, "%s: Keepalive callback error: %#m\n", prefix, inError );
23024 
23025 	if( inError )
23026 	{
23027 		Boolean		done;
23028 
23029 		err = _KeepAliveTestContinue( test, inError, &done );
23030 		check_noerr( err );
23031 		if( err || done ) _KeepAliveTestStop( test, err );
23032 	}
23033 }
23034 
23035 //===========================================================================================================================
23036 
_KeepAliveTestQueryTimerHandler(void * inCtx)23037 static void	_KeepAliveTestQueryTimerHandler( void *inCtx )
23038 {
23039 	OSStatus						err;
23040 	const KeepAliveTestRef			test	= (KeepAliveTestRef) inCtx;
23041 	KeepAliveSubtest * const		subtest	= _KeepAliveTestGetSubtest( test );
23042 	Boolean							done;
23043 	char							prefix[ 64 ];
23044 
23045 	_KeepAliveTestGetSubtestLogPrefix( test, prefix, sizeof( prefix ) );
23046 	kat_ulog( kLogLevelInfo, "%s: Query for \"%s\" timed out.\n", prefix, subtest->recordName );
23047 
23048 	err = _KeepAliveTestContinue( test, kTimeoutErr, &done );
23049 	check_noerr( err );
23050 	if( err || done ) _KeepAliveTestStop( test, err );
23051 }
23052 
23053 //===========================================================================================================================
23054 
23055 static void DNSSD_API
_KeepAliveTestQueryRecordCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inFullName,uint16_t inType,uint16_t inClass,uint16_t inRDataLen,const void * inRDataPtr,uint32_t inTTL,void * inCtx)23056 	_KeepAliveTestQueryRecordCallback(
23057 		DNSServiceRef		inSDRef,
23058 		DNSServiceFlags		inFlags,
23059 		uint32_t			inInterfaceIndex,
23060 		DNSServiceErrorType	inError,
23061 		const char *		inFullName,
23062 		uint16_t			inType,
23063 		uint16_t			inClass,
23064 		uint16_t			inRDataLen,
23065 		const void *		inRDataPtr,
23066 		uint32_t			inTTL,
23067 		void *				inCtx )
23068 {
23069 	OSStatus						err;
23070 	const KeepAliveTestRef			test	= (KeepAliveTestRef) inCtx;
23071 	KeepAliveSubtest * const		subtest	= _KeepAliveTestGetSubtest( test );
23072 	const uint8_t *					ptr;
23073 	size_t							dataStrLen, minLen;
23074 	Boolean							done;
23075 	char							prefix[ 64 ];
23076 
23077 	Unused( inSDRef );
23078 	Unused( inInterfaceIndex );
23079 	Unused( inTTL );
23080 
23081 	_KeepAliveTestGetSubtestLogPrefix( test, prefix, sizeof( prefix ) );
23082 	if( strcasecmp( inFullName, subtest->recordName ) != 0 )
23083 	{
23084 		kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: Got unexpected record name \"%s\".\n",
23085 			prefix, subtest->recordName, inFullName );
23086 		err = kUnexpectedErr;
23087 		goto exit;
23088 	}
23089 	if( inType != kDNSServiceType_NULL )
23090 	{
23091 		kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: Got unexpected record type %d (%s) != %d (NULL).\n",
23092 			prefix, subtest->recordName, inType, RecordTypeToString( inType ), kDNSServiceType_NULL );
23093 		err = kUnexpectedErr;
23094 		goto exit;
23095 	}
23096 	if( inClass != kDNSServiceClass_IN )
23097 	{
23098 		kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: Got unexpected record class %d != %d (IN).\n",
23099 			prefix, subtest->recordName, inClass, kDNSServiceClass_IN );
23100 		err = kUnexpectedErr;
23101 		goto exit;
23102 	}
23103 	if( inError )
23104 	{
23105 		kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: Got unexpected error %#m.\n",
23106 			prefix, subtest->recordName, inError );
23107 		err = inError;
23108 		goto exit;
23109 	}
23110 	if( ( inFlags & kDNSServiceFlagsAdd ) == 0 )
23111 	{
23112 		kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: Missing Add flag.\n", prefix, subtest->recordName );
23113 		err = kUnexpectedErr;
23114 		goto exit;
23115 	}
23116 	kat_ulog( kLogLevelInfo, "%s: QueryRecord(%s) result rdata: %#H\n",
23117 		prefix, subtest->recordName, inRDataPtr, inRDataLen, inRDataLen );
23118 
23119 	dataStrLen	= strlen( subtest->dataStr ) + 1;	// There's a NUL terminator at the end of the rdata.
23120 	minLen		= 1 + dataStrLen;					// The first byte of the rdata is a length byte.
23121 	if( inRDataLen < minLen )
23122 	{
23123 		kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: rdata length (%d) is less than expected minimum (%zu).\n",
23124 			prefix, subtest->recordName, inRDataLen, minLen );
23125 		err = kUnexpectedErr;
23126 		goto exit;
23127 	}
23128 	ptr = (const uint8_t *) inRDataPtr;
23129 	if( ptr[ 0 ] < dataStrLen )
23130 	{
23131 		kat_ulog( kLogLevelError,
23132 			"%s: QueryRecord(%s) result: rdata length byte value (%d) is less than expected minimum (%zu).\n",
23133 			prefix, subtest->recordName, ptr[ 0 ], dataStrLen );
23134 		err = kUnexpectedErr;
23135 		goto exit;
23136 	}
23137 	if( memcmp( &ptr[ 1 ], subtest->dataStr, dataStrLen - 1 ) != 0 )
23138 	{
23139 		kat_ulog( kLogLevelError, "%s: QueryRecord(%s) result: rdata body doesn't contain '%s'.\n",
23140 			prefix, subtest->recordName, subtest->dataStr );
23141 	}
23142 	err = kNoErr;
23143 
23144 exit:
23145 	err = _KeepAliveTestContinue( test, err, &done );
23146 	check_noerr( err );
23147 	if( err || done ) _KeepAliveTestStop( test, kNoErr );
23148 }
23149 #endif	// TARGET_OS_DARWIN
23150 
23151 #if ( ENABLE_DNSSDUTIL_DNSSEC_TEST == 1 )
23152 //===========================================================================================================================
23153 //	DNSSECTestCmd
23154 //===========================================================================================================================
23155 
23156 #define kDNSSECTestQueryTimeoutSecs 4
23157 
23158 typedef struct DNSSECTest_BasicValidationContext
23159 {
23160 	uint32_t number_of_answers;		// The number of answers expected to receive in the "basic validation" test
23161 
23162 }	DNSSECTest_BasicValidationContext;
23163 
23164 //===========================================================================================================================
23165 
23166 typedef struct
23167 {
23168 	DNSServiceRef		query;
23169 	dispatch_source_t	queryTimer;			// Used to setup timeout timer, to prevent from waiting forever.
23170 	Boolean				testStarted;
23171 	const char *		testName;			// The name of the curreent running test case.
23172 	const char *		testCaseName;		// The query name in the subtest of the test case.
23173 	union {
23174 		DNSSECTest_BasicValidationContext	basicValidation;
23175 	} testCaseContext;						// Contains different customized context pointer, which can be used by different test cases.
23176 	const char *		subtestQueryName;	// The query name that is passed to mDNSResponder API
23177 	int					subtestIndex;		// The index of the current case in the test input array.
23178 	pid_t				localServerPID;		// The pid of the dnssdutil server
23179 	NanoTime64			startTime;			// The time when the DNSSEC test case starts.
23180 	CFMutableArrayRef	subtestReport;		// The reference to the CFMutableArrayRef, which contains subtest reports for different subtests
23181 	NanoTime64			subtestStartTime;	// The time when the subtest starts
23182 	Boolean				subtestFailed;		// Indicate if any subtest failed before.
23183 
23184 	char *				outputFilePath;		// File to write test results to. If NULL, then write to stdout. (malloced)
23185 	OutputFormatType	outputFormat;		// Format of test report output.
23186 } DNSSECTestContext;
23187 
23188 //===========================================================================================================================
23189 
23190 typedef struct
23191 {
23192 	const char *	testCaseName;										// The name of the test case that will be run.
23193 	void			(*testCaseHandler)( DNSSECTestContext * context );	// The main function of the test case.
23194 } DNSSECTestTestCase;
23195 
23196 //===========================================================================================================================
23197 
23198 static void DNSSECTestSetupLocalDNSServer( DNSSECTestContext *context );
23199 static void DNSSECTestStartTestCase( DNSSECTestContext *context );
23200 
23201 //===========================================================================================================================
23202 
23203 // DNSSEC test cases
23204 static void DNSSECTest_BasicValidation( DNSSECTestContext *context );
23205 
23206 static DNSSECTestTestCase DNSSECTestTestCases[] =
23207 {
23208 	{ "basic validation", DNSSECTest_BasicValidation }
23209 };
23210 
23211 //===========================================================================================================================
23212 
23213 // The main function to start the DNSSEC test
23214 static void
DNSSECTestCmd(void)23215 	DNSSECTestCmd( void )
23216 {
23217 	OSStatus			err;
23218 	dispatch_source_t	signalSource	= NULL;
23219 	DNSSECTestContext *	context			= NULL;
23220 
23221 	// Set up SIGINT handler.
23222     signal( SIGINT, SIG_IGN );
23223     err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
23224     require_noerr( err, exit );
23225     dispatch_resume( signalSource );
23226 
23227 	// Create the test context.
23228 	context = (DNSSECTestContext *) calloc( 1, sizeof( *context ) );
23229 	require_action( context, exit, err = kNoMemoryErr );
23230 
23231 	context->testStarted = false;
23232 
23233 	// Get the command line option.
23234 	context->testCaseName = strdup( gDNSSECTest_TestCaseName );
23235 	require( context->testCaseName , exit );
23236 
23237 	// Start the subtest from index 0
23238 	context->subtestIndex = 0;
23239 
23240 	// Get the output format.
23241 	err = OutputFormatFromArgString( gDNSSECTest_OutputFormat, &context->outputFormat );
23242 	require_noerr_quiet( err, exit );
23243 
23244 	// Get the output file path.
23245 	if( gDNSSECTest_OutputFilePath )
23246 	{
23247 		context->outputFilePath = strdup( gDNSSECTest_OutputFilePath );
23248 		require_noerr_quiet( context->outputFilePath , exit );
23249 	}
23250 
23251 	// Initialize the CFArray to store the test result.
23252 	context->subtestReport	= CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks );
23253 	context->startTime 		= NanoTimeGetCurrent();
23254 
23255 	// Start the local dnssdutil server.
23256 	DNSSECTestSetupLocalDNSServer( context );
23257 
23258 	// Start the test.
23259 	DNSSECTestStartTestCase( context );
23260 
23261 exit:
23262 	exit( 1 );
23263 }
23264 
23265 //===========================================================================================================================
23266 
23267 // Start the local DNS server with dnssdutil server command.
23268 static void
DNSSECTestSetupLocalDNSServer(DNSSECTestContext * context)23269 	DNSSECTestSetupLocalDNSServer( DNSSECTestContext *context )
23270 {
23271 	Unused( context );
23272 	pid_t pid = getpid();
23273 
23274 	OSStatus err = _SpawnCommand( &context->localServerPID, NULL, NULL, "dnssdutil server --loopback --follow %lld",
23275 		(int64_t) pid );
23276 	require_noerr_action( err, exit,
23277 		FPrintF( stderr, "dnssdutil server --loopback --follow %lld failed, error: %d\n", (int64_t) pid, err ) );
23278 
23279 	// Wait long enough to allow the DNS server being setup.
23280 	sleep( 2 );
23281 
23282 	return;
23283 exit:
23284 	exit( 1 );
23285 }
23286 
23287 //===========================================================================================================================
23288 
23289 // Start the test case that user specifies.
23290 static void
DNSSECTestStartTestCase(DNSSECTestContext * context)23291 	DNSSECTestStartTestCase( DNSSECTestContext *context )
23292 {
23293 	Boolean findTestCase = false;
23294 
23295 	for ( int i = 0; i < (int)countof( DNSSECTestTestCases ); i++ )
23296 	{
23297 		if( strcmp( context->testCaseName, DNSSECTestTestCases[i].testCaseName ) == 0 )
23298 		{
23299 			DNSSECTestTestCases[ i ].testCaseHandler( context );
23300 			findTestCase = true;
23301 		}
23302 	}
23303 
23304 	if( !findTestCase )
23305 	{
23306 		FPrintF( stdout, "Unknown test case \"%s\"\n", context->testCaseName );
23307 		exit( 1 );
23308 	}
23309 }
23310 
23311 //===========================================================================================================================
23312 
23313 // Write the current subtest status into the report list. When this function is called, either some error occurs or the
23314 // subtest finishes.
23315 static void
_DNSSECTestQueryWriteSubtestReport(DNSSECTestContext * inContext,const char * errorDescription)23316 	_DNSSECTestQueryWriteSubtestReport( DNSSECTestContext *inContext, const char *errorDescription )
23317 {
23318 	OSStatus	err;
23319 	NanoTime64	now;
23320 	char		startTime[ 32 ];
23321 	char		endTime[ 32 ];
23322 
23323 	now = NanoTimeGetCurrent();
23324 	_NanoTime64ToTimestamp( inContext->subtestStartTime, startTime, sizeof( startTime ) );
23325 	_NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
23326 
23327 	err = CFPropertyListAppendFormatted( kCFAllocatorDefault, inContext->subtestReport,
23328 		"{"
23329 			"%kO=%s"
23330 			"%kO=%s"
23331 			"%kO=%s"
23332 			"%kO=%s"
23333 			"%kO=%s"
23334 		"}",
23335 		CFSTR( "Start Time" ),			startTime,
23336 		CFSTR( "End Time" ),			endTime,
23337 		CFSTR( "Subtest Query Name" ),	inContext->subtestQueryName,
23338 		CFSTR( "Result" ),				errorDescription ? "Fail" : "Pass",
23339 		CFSTR( "Error Description" ),	errorDescription ? errorDescription : "No Error"
23340 	);
23341 
23342 	require_noerr( err, exit );
23343 	return;
23344 
23345 exit:
23346 	ErrQuit( 1, "error: %#m\n", err );
23347 }
23348 
23349 //===========================================================================================================================
23350 
23351 // Output the final test result to the file (path specified by the user) in the disk.
23352 static void
_DNSSECTestQueryOutputFinalReport(DNSSECTestContext * inContext)23353 	_DNSSECTestQueryOutputFinalReport( DNSSECTestContext *inContext )
23354 {
23355 	OSStatus			err;
23356 	CFPropertyListRef	plist;
23357 	NanoTime64			now;
23358 	char				startTime[ 32 ];
23359 	char				endTime[ 32 ];
23360 
23361 	now = NanoTimeGetCurrent();
23362 	_NanoTime64ToTimestamp( inContext->startTime, startTime, sizeof( startTime ) );
23363 	_NanoTime64ToTimestamp( now, endTime, sizeof( endTime ) );
23364 
23365 	err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
23366 		"{"
23367 			"%kO=%s"
23368 			"%kO=%s"
23369 			"%kO=%s"
23370 			"%kO=%b"
23371 			"%kO=%O"
23372 		"}",
23373 		CFSTR( "Start Time" ),		startTime,
23374 		CFSTR( "End Time" ),		endTime,
23375 		CFSTR( "Test Case Name" ),	inContext->testCaseName,
23376 		CFSTR( "All Passed" ),		!inContext->subtestFailed,
23377 		CFSTR( "Subtest Reports" ),	inContext->subtestReport
23378 	);
23379 	require_noerr( err, exit );
23380 	ForgetCF( &inContext->subtestReport );
23381 
23382 	err = OutputPropertyList( plist, inContext->outputFormat, inContext->outputFilePath );
23383 	CFRelease( plist );
23384 	require_noerr( err, exit );
23385 
23386 	return;
23387 exit:
23388 	ErrQuit( 1, "error: %#m\n", err );
23389 }
23390 
23391 //===========================================================================================================================
23392 
23393 // The handler that will be called when timeout happens. When timeout happens it always means something bad happen, and
23394 // it might be possible that all the remaning tests timeout too, so the function exits directly instead of continuing.
23395 static void
_DNSSECTestQueryTimeoutHandler(void * inContext)23396 	_DNSSECTestQueryTimeoutHandler( void *inContext )
23397 {
23398 	DNSSECTestContext * const context = (DNSSECTestContext *) inContext;
23399 
23400 	DNSServiceForget( &context->query );
23401 	dispatch_source_forget( &context->queryTimer );
23402 
23403 	context->subtestFailed = true;
23404 	_DNSSECTestQueryWriteSubtestReport( context, "Query for DNSSEC-related records timed out" );
23405 	_DNSSECTestQueryOutputFinalReport( context );
23406 
23407 	exit( 1 );
23408 }
23409 
23410 //===========================================================================================================================
23411 #pragma mark - Test case "basic validation"
23412 
23413 //===========================================================================================================================
23414 
23415 typedef struct DNSSECTest_BasicValidationTestCase
23416 {
23417 	const char *	queryName;		// The query name that controls the behavior of the local DNS server.
23418 	uint16_t		queryType;		// The DNS record being queried.
23419 	uint32_t		num_of_answers;	// How many anwers are expected to be returned.
23420 
23421 }	DNSSECTest_BasicValidationTestCase;
23422 
23423 //===========================================================================================================================
23424 
23425 DNSSECTest_BasicValidationTestCase DNSSECTest_BasicValidationTestCases[] =
23426 {
23427 	// kDNSSECAlgorithm_RSASHA1 5
23428 	{ "count-1.z-5-1.z-5-2.z-5-3.dnssec.test.",		kDNSServiceType_A,		1 },
23429 	{ "count-2.z-5-1.z-5-2.z-5-3.dnssec.test.",		kDNSServiceType_A,		2 },
23430 	{ "count-1.z-5-1.z-5-2.z-5-3.dnssec.test.",		kDNSServiceType_AAAA,	1 },
23431 	{ "count-2.z-5-1.z-5-2.z-5-3.dnssec.test.",		kDNSServiceType_AAAA,	2 },
23432 	{ "count-10.z-5-1.z-5-2.z-5-3.dnssec.test.",	kDNSServiceType_A,		10 },
23433 	// kDNSSECAlgorithm_RSASHA256 8
23434 	{ "count-1.z-8-1.z-8-2.z-8-3.dnssec.test.",		kDNSServiceType_A,		1 },
23435 	{ "count-2.z-8-1.z-8-2.z-8-3.dnssec.test.",		kDNSServiceType_A,		2 },
23436 	{ "count-1.z-8-1.z-8-2.z-8-3.dnssec.test.",		kDNSServiceType_AAAA,	1 },
23437 	{ "count-2.z-8-1.z-8-2.z-8-3.dnssec.test.",		kDNSServiceType_AAAA,	2 },
23438 	{ "count-10.z-8-1.z-8-2.z-8-3.dnssec.test.",	kDNSServiceType_A,		10 },
23439 	// kDNSSECAlgorithm_RSASHA512 10
23440 	{ "count-1.z-10-1.z-10-2.z-10-3.dnssec.test.",	kDNSServiceType_A,		1 },
23441 	{ "count-2.z-10-1.z-10-2.z-10-3.dnssec.test.",	kDNSServiceType_A,		2 },
23442 	{ "count-1.z-10-1.z-10-2.z-10-3.dnssec.test.",	kDNSServiceType_AAAA,	1 },
23443 	{ "count-2.z-10-1.z-10-2.z-10-3.dnssec.test.",	kDNSServiceType_AAAA,	2 },
23444 	{ "count-10.z-10-1.z-10-2.z-10-3.dnssec.test.",	kDNSServiceType_A,		10 },
23445 	// kDNSSECAlgorithm_ECDSAP256SHA256 13
23446 	{ "count-1.z-13-1.z-13-2.z-13-3.dnssec.test.",	kDNSServiceType_A,		1 },
23447 	{ "count-2.z-13-1.z-13-2.z-13-3.dnssec.test.",	kDNSServiceType_A,		2 },
23448 	{ "count-1.z-13-1.z-13-2.z-13-3.dnssec.test.",	kDNSServiceType_AAAA,	1 },
23449 	{ "count-2.z-13-1.z-13-2.z-13-3.dnssec.test.",	kDNSServiceType_AAAA,	2 },
23450 	{ "count-10.z-13-1.z-13-2.z-13-3.dnssec.test.",	kDNSServiceType_A,		10 },
23451 	// kDNSSECAlgorithm_ECDSAP384SHA384 14
23452 	{ "count-1.z-14-1.z-14-2.z-14-3.dnssec.test.",	kDNSServiceType_A,		1 },
23453 	{ "count-2.z-14-1.z-14-2.z-14-3.dnssec.test.",	kDNSServiceType_A,		2 },
23454 	{ "count-1.z-14-1.z-14-2.z-14-3.dnssec.test.",	kDNSServiceType_AAAA,	1 },
23455 	{ "count-2.z-14-1.z-14-2.z-14-3.dnssec.test.",	kDNSServiceType_AAAA,	2 },
23456 	{ "count-10.z-14-1.z-14-2.z-14-3.dnssec.test.",	kDNSServiceType_A,		10 },
23457 	// Mixed use of mutiple DNSKEY algorithms
23458 	{ "count-1.z-5-1.z-8-2.z-10-3.dnssec.test.",	kDNSServiceType_A,		1 },
23459 	{ "count-2.z-5-1.z-5-2.z-8-3.dnssec.test.",		kDNSServiceType_A,		2 },
23460 	{ "count-1.z-8-1.z-5-2.z-5-3.dnssec.test.",		kDNSServiceType_AAAA,	1 },
23461 	{ "count-2.z-10-1.z-8-2.z-8-3.dnssec.test.",	kDNSServiceType_AAAA,	2 },
23462 	{ "count-10.z-10-1.z-8-2.z-5-3.dnssec.test.",	kDNSServiceType_A,		10 },
23463 	{ "count-1.z-13-1.z-14-2.z-14-3.dnssec.test.",	kDNSServiceType_A,		1 },
23464 	{ "count-2.z-14-1.z-13-2.z-8-3.dnssec.test.",	kDNSServiceType_A,		2 },
23465 	{ "count-1.z-5-1.z-14-2.z-8-3.dnssec.test.",	kDNSServiceType_AAAA,	1 },
23466 	{ "count-2.z-10-1.z-8-2.z-13-3.dnssec.test.",	kDNSServiceType_AAAA,	2 },
23467 	{ "count-10.z-14-1.z-13-2.z-5-3.dnssec.test.",	kDNSServiceType_A,		10 }
23468 };
23469 
23470 //===========================================================================================================================
23471 
23472 static void DNSSD_API
23473 	_DNSSECTest_BasicValidationCallBack(
23474 		DNSServiceRef			inSDRef,
23475 		DNSServiceFlags			inFlags,
23476 		uint32_t				inInterfaceIndex,
23477 		DNSServiceErrorType		inError,
23478 		const char *			inFullName,
23479 		uint16_t				inType,
23480 		uint16_t				inClass,
23481 		uint16_t				inRDataLen,
23482 		const void *			inRDataPtr,
23483 		uint32_t				inTTL,
23484 		void *					inContext );
23485 
23486 //===========================================================================================================================
23487 
23488 static Boolean
23489 	_DNSSECTest_BasicValidationVerifyTheResponse(
23490 		DNSSECTestContext *	inContext,
23491 		DNSServiceFlags		inFlags,
23492 		uint32_t			inInterfaceIndex,
23493 		DNSServiceErrorType	inError,
23494 		const char *		inFullName,
23495 		uint16_t			inType,
23496 		uint16_t			inClass,
23497 		uint16_t			inRDataLen,
23498 		const void *		inRDataPtr,
23499 		uint32_t			inTTL,
23500 		char *				inStrBuffer,
23501 		uint32_t			inBufferLen,
23502 		Boolean *			outExpectMore );
23503 
23504 //===========================================================================================================================
23505 
DNSSECTest_BasicValidation(DNSSECTestContext * inContext)23506 static void DNSSECTest_BasicValidation( DNSSECTestContext *inContext )
23507 {
23508 	OSStatus		err;
23509 	const char *	error_description = NULL;
23510 
23511 	// Get the current subtest
23512 	DNSSECTest_BasicValidationTestCase * testCases = &DNSSECTest_BasicValidationTestCases[ inContext->subtestIndex ];
23513 	inContext->subtestQueryName = testCases->queryName;
23514 	inContext->testCaseContext.basicValidation.number_of_answers = testCases->num_of_answers;
23515 
23516 	// Issue the query.
23517 	err = DNSServiceQueryRecord( &inContext->query,
23518 		kDNSServiceFlagsEnableDNSSEC, 0, testCases->queryName, testCases->queryType, kDNSServiceClass_IN,
23519 		_DNSSECTest_BasicValidationCallBack, inContext );
23520 	require_noerr_action( err, exit, error_description = "DNSServiceQueryRecord failed" );
23521 
23522 	// Set the dispatch queue.
23523 	err = DNSServiceSetDispatchQueue( inContext->query,
23524 		dispatch_get_main_queue() );
23525 	require_noerr_action( err, exit, error_description = "DNSServiceSetDispatchQueue failed" );
23526 
23527 	// Create a timer to handle timeout.
23528 	err = DispatchTimerCreate( dispatch_time_seconds( kDNSSECTestQueryTimeoutSecs ), DISPATCH_TIME_FOREVER,
23529 		UINT64_C_safe( kDNSSECTestQueryTimeoutSecs ) * kNanosecondsPerSecond / 10, NULL, _DNSSECTestQueryTimeoutHandler,
23530 		NULL, inContext, &inContext->queryTimer );
23531 	require_noerr_action( err, exit, error_description = "DispatchTimerCreate failed" );
23532 	dispatch_resume( inContext->queryTimer );
23533 
23534 	// start the current subtest, and record the start time.
23535 	inContext->subtestStartTime	= NanoTimeGetCurrent();
23536 	if( !inContext->testStarted )
23537 	{
23538 		// Only call dispatch_main once, when we first start the test.
23539 		inContext->testStarted = true;
23540 		dispatch_main();
23541 	}
23542 
23543 	return;
23544 exit:
23545 	_DNSSECTestQueryWriteSubtestReport( inContext, error_description );
23546 	_DNSSECTestQueryOutputFinalReport( inContext );
23547 	exit( 1 );
23548 }
23549 
23550 //===========================================================================================================================
23551 
23552 static void DNSSD_API
_DNSSECTest_BasicValidationCallBack(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inFullName,uint16_t inType,uint16_t inClass,uint16_t inRDataLen,const void * inRDataPtr,uint32_t inTTL,void * inContext)23553 	_DNSSECTest_BasicValidationCallBack(
23554 		DNSServiceRef			inSDRef,
23555 		DNSServiceFlags			inFlags,
23556 		uint32_t				inInterfaceIndex,
23557 		DNSServiceErrorType		inError,
23558 		const char *			inFullName,
23559 		uint16_t				inType,
23560 		uint16_t				inClass,
23561 		uint16_t				inRDataLen,
23562 		const void *			inRDataPtr,
23563 		uint32_t				inTTL,
23564 		void *					inContext )
23565 {
23566 	char						err_desp_str[1024];
23567 	Boolean						valid;
23568 	Boolean						expectMore;
23569 	DNSSECTestContext * const	context = (DNSSECTestContext *)inContext;
23570 
23571 	Unused( inSDRef );
23572 
23573 	err_desp_str[0] = '\0';
23574 
23575 	// Verify if the response is expected
23576 	valid = _DNSSECTest_BasicValidationVerifyTheResponse(context, inFlags, inInterfaceIndex, inError, inFullName, inType,
23577 		inClass, inRDataLen, inRDataPtr, inTTL, err_desp_str, sizeof( err_desp_str ), &expectMore);
23578 
23579 	// If more answers are expected to be returned.
23580 	if( expectMore )
23581 	{
23582 		return;
23583 	}
23584 
23585 	// Cancel the current request and its corresponding timer.
23586 	DNSServiceForget( &context->query );
23587 	dispatch_source_forget( &context->queryTimer );
23588 
23589 	// Check if the test fails.
23590 	if( !valid )
23591 	{
23592 		context->subtestFailed = true;
23593 	}
23594 	_DNSSECTestQueryWriteSubtestReport( context, valid ? NULL : err_desp_str );
23595 
23596 	// Increment the index.
23597 	context->subtestIndex++;
23598 	if( context->subtestIndex == countof( DNSSECTest_BasicValidationTestCases ) )
23599 	{
23600 		// All test cases have been run
23601 		_DNSSECTestQueryOutputFinalReport( context );
23602 		exit( context->subtestFailed ? 2 : 0 );
23603 	}
23604 
23605 	// Start the next subtest.
23606 	DNSSECTest_BasicValidation( context );
23607 }
23608 
23609 //===========================================================================================================================
23610 
23611 // Check if the response is expected. Return true if the response is expected, otherwise return false.
23612 static Boolean
_DNSSECTest_BasicValidationVerifyTheResponse(DNSSECTestContext * inContext,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inFullName,uint16_t inType,uint16_t inClass,uint16_t inRDataLen,const void * inRDataPtr,uint32_t inTTL,char * inStrBuffer,uint32_t inBufferLen,Boolean * outExpectMore)23613 _DNSSECTest_BasicValidationVerifyTheResponse(
23614 	DNSSECTestContext *	inContext,
23615 	DNSServiceFlags		inFlags,
23616 	uint32_t			inInterfaceIndex,
23617 	DNSServiceErrorType	inError,
23618 	const char *		inFullName,
23619 	uint16_t			inType,
23620 	uint16_t			inClass,
23621 	uint16_t			inRDataLen,
23622 	const void *		inRDataPtr,
23623 	uint32_t			inTTL,
23624 	char *				inStrBuffer,
23625 	uint32_t			inBufferLen,
23626 	Boolean *			outExpectMore )
23627 {
23628 	const char * const						queryName	= inContext->subtestQueryName;
23629 	DNSSECTest_BasicValidationTestCase *	testCases 	= &DNSSECTest_BasicValidationTestCases[ inContext->subtestIndex ];
23630 	DNSSECTest_BasicValidationContext *		subContext	= &inContext->testCaseContext.basicValidation;
23631 	Boolean									expectMore	= false;
23632 
23633 	Unused( inClass );
23634 	Unused( inRDataLen );
23635 	Unused( inRDataPtr );
23636 	Unused( inTTL );
23637 
23638 	require_noerr_action( inError, exit, SNPrintF( inStrBuffer, inBufferLen, "Unexpected DNSServiceErrorType: %d", inError ) );
23639 
23640 	require_action( inFlags & kDNSServiceFlagsAdd, exit,
23641 		SNPrintF( inStrBuffer, inBufferLen, "Unexpected add/remove event: %#{flags}", inFlags, kDNSServiceFlagsDescriptors )
23642 	);
23643 
23644 	require_action( inInterfaceIndex == kDNSServiceInterfaceIndexAny, exit,
23645 		SNPrintF( inStrBuffer, inBufferLen, "Non-local-only interface is used: %u", inInterfaceIndex ) );
23646 
23647 	require_action( strcmp( queryName, inFullName ) == 0, exit,
23648 		SNPrintF( inStrBuffer, inBufferLen, "Mismatched name: %s", inFullName ) );
23649 
23650 	require_action( inType == testCases->queryType, exit,
23651 		SNPrintF( inStrBuffer, inBufferLen, "Mismatched query type: %s", RecordTypeToString( inType ) ) );
23652 
23653 	require_action( (inFlags & kDNSServiceFlagsSecure) == kDNSServiceFlagsSecure, exit,
23654 		SNPrintF( inStrBuffer, inBufferLen, "Unexpected DNSSEC result: %#{flags}", (inFlags & kDNSServiceFlagsSecure),
23655 			kDNSServiceFlagsDescriptors )
23656 	);
23657 
23658 	if( --subContext->number_of_answers != 0 )
23659 	{
23660 		expectMore = true;
23661 	}
23662 
23663 
23664 exit:
23665 	if( outExpectMore )
23666 	{
23667 		*outExpectMore = expectMore;
23668 	}
23669 
23670 	return inStrBuffer[0] != '\0' ? false : true;
23671 }
23672 #else
DNSSECTestCmd(void)23673 static void DNSSECTestCmd( void )
23674 {
23675 	exit( 0 );
23676 }
23677 #endif // #if ( ENABLE_DNSSDUTIL_DNSSEC_TEST == 1 )
23678 
23679 //===========================================================================================================================
23680 //	SSDPDiscoverCmd
23681 //===========================================================================================================================
23682 
23683 #define kSSDPPort		1900
23684 
23685 typedef struct
23686 {
23687 	HTTPHeader				header;			// HTTP header object for sending and receiving.
23688 	dispatch_source_t		readSourceV4;	// Read dispatch source for IPv4 socket.
23689 	dispatch_source_t		readSourceV6;	// Read dispatch source for IPv6 socket.
23690 	int						receiveSecs;	// After send, the amount of time to spend receiving.
23691 	uint32_t				ifindex;		// Index of the interface over which to send the query.
23692 	Boolean					useIPv4;		// True if the query should be sent via IPv4 multicast.
23693 	Boolean					useIPv6;		// True if the query should be sent via IPv6 multicast.
23694 
23695 }	SSDPDiscoverContext;
23696 
23697 static void		SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext );
23698 static void		SSDPDiscoverReadHandler( void *inContext );
23699 static int		SocketToPortNumber( SocketRef inSock );
23700 static OSStatus	WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST );
23701 
SSDPDiscoverCmd(void)23702 static void	SSDPDiscoverCmd( void )
23703 {
23704 	OSStatus					err;
23705 	struct timeval				now;
23706 	SSDPDiscoverContext *		context;
23707 	dispatch_source_t			signalSource	= NULL;
23708 	SocketRef					sockV4			= kInvalidSocketRef;
23709 	SocketRef					sockV6			= kInvalidSocketRef;
23710 	ssize_t						n;
23711 	int							sendCount;
23712 
23713 	// Set up SIGINT handler.
23714 
23715 	signal( SIGINT, SIG_IGN );
23716 	err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), Exit, kExitReason_SIGINT, &signalSource );
23717 	require_noerr( err, exit );
23718 	dispatch_resume( signalSource );
23719 
23720 	// Check command parameters.
23721 
23722 	if( gSSDPDiscover_ReceiveSecs < -1 )
23723 	{
23724 		FPrintF( stdout, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs );
23725 		err = kParamErr;
23726 		goto exit;
23727 	}
23728 
23729 	// Create context.
23730 
23731 	context = (SSDPDiscoverContext *) calloc( 1, sizeof( *context ) );
23732 	require_action( context, exit, err = kNoMemoryErr );
23733 
23734 	context->receiveSecs	= gSSDPDiscover_ReceiveSecs;
23735 	context->useIPv4		= ( gSSDPDiscover_UseIPv4 || !gSSDPDiscover_UseIPv6 ) ? true : false;
23736 	context->useIPv6		= ( gSSDPDiscover_UseIPv6 || !gSSDPDiscover_UseIPv4 ) ? true : false;
23737 
23738 	err = InterfaceIndexFromArgString( gInterface, &context->ifindex );
23739 	require_noerr_quiet( err, exit );
23740 
23741 	// Set up IPv4 socket.
23742 
23743 	if( context->useIPv4 )
23744 	{
23745 		int port;
23746 		err = UDPClientSocketOpen( AF_INET, NULL, 0, -1, &port, &sockV4 );
23747 		require_noerr( err, exit );
23748 
23749 		err = SocketSetMulticastInterface( sockV4, NULL, context->ifindex );
23750 		require_noerr( err, exit );
23751 
23752 		err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
23753 		err = map_socket_noerr_errno( sockV4, err );
23754 		require_noerr( err, exit );
23755 	}
23756 
23757 	// Set up IPv6 socket.
23758 
23759 	if( context->useIPv6 )
23760 	{
23761 		err = UDPClientSocketOpen( AF_INET6, NULL, 0, -1, NULL, &sockV6 );
23762 		require_noerr( err, exit );
23763 
23764 		err = SocketSetMulticastInterface( sockV6, NULL, context->ifindex );
23765 		require_noerr( err, exit );
23766 
23767 		err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
23768 		err = map_socket_noerr_errno( sockV6, err );
23769 		require_noerr( err, exit );
23770 	}
23771 
23772 	// Print prologue.
23773 
23774 	SSDPDiscoverPrintPrologue( context );
23775 
23776 	// Send mDNS query message.
23777 
23778 	sendCount = 0;
23779 	if( IsValidSocket( sockV4 ) )
23780 	{
23781 		struct sockaddr_in		mcastAddr4;
23782 
23783 		_SockAddrInitIPv4( &mcastAddr4, UINT32_C( 0xEFFFFFFA ), kSSDPPort );	// 239.255.255.250
23784 
23785 		err = WriteSSDPSearchRequest( &context->header, &mcastAddr4, gSSDPDiscover_MX, gSSDPDiscover_ST );
23786 		require_noerr( err, exit );
23787 
23788 		n = sendto( sockV4, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr4,
23789 			(socklen_t) sizeof( mcastAddr4 ) );
23790 		err = map_socket_value_errno( sockV4, n == (ssize_t) context->header.len, n );
23791 		if( err )
23792 		{
23793 			FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
23794 			ForgetSocket( &sockV4 );
23795 		}
23796 		else
23797 		{
23798 			if( gSSDPDiscover_Verbose )
23799 			{
23800 				gettimeofday( &now, NULL );
23801 				FPrintF( stdout, "---\n" );
23802 				FPrintF( stdout, "Send time:    %{du:time}\n",	&now );
23803 				FPrintF( stdout, "Source Port:  %d\n",			SocketToPortNumber( sockV4 ) );
23804 				FPrintF( stdout, "Destination:  %##a\n",		&mcastAddr4 );
23805 				FPrintF( stdout, "Message size: %zu\n",			context->header.len );
23806 				FPrintF( stdout, "HTTP header:\n%1{text}",		context->header.buf, context->header.len );
23807 			}
23808 			++sendCount;
23809 		}
23810 	}
23811 
23812 	if( IsValidSocket( sockV6 ) )
23813 	{
23814 		struct sockaddr_in6		mcastAddr6;
23815 
23816 		memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) );
23817 		SIN6_LEN_SET( &mcastAddr6 );
23818 		mcastAddr6.sin6_family				= AF_INET6;
23819 		mcastAddr6.sin6_port				= htons( kSSDPPort );
23820 		mcastAddr6.sin6_addr.s6_addr[  0 ]	= 0xFF;	// SSDP IPv6 link-local multicast address FF02::C
23821 		mcastAddr6.sin6_addr.s6_addr[  1 ]	= 0x02;
23822 		mcastAddr6.sin6_addr.s6_addr[ 15 ]	= 0x0C;
23823 
23824 		err = WriteSSDPSearchRequest( &context->header, &mcastAddr6, gSSDPDiscover_MX, gSSDPDiscover_ST );
23825 		require_noerr( err, exit );
23826 
23827 		n = sendto( sockV6, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr6,
23828 			(socklen_t) sizeof( mcastAddr6 ) );
23829 		err = map_socket_value_errno( sockV6, n == (ssize_t) context->header.len, n );
23830 		if( err )
23831 		{
23832 			FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
23833 			ForgetSocket( &sockV6 );
23834 		}
23835 		else
23836 		{
23837 			if( gSSDPDiscover_Verbose )
23838 			{
23839 				gettimeofday( &now, NULL );
23840 				FPrintF( stdout, "---\n" );
23841 				FPrintF( stdout, "Send time:    %{du:time}\n",	&now );
23842 				FPrintF( stdout, "Source Port:  %d\n",			SocketToPortNumber( sockV6 ) );
23843 				FPrintF( stdout, "Destination:  %##a\n",		&mcastAddr6 );
23844 				FPrintF( stdout, "Message size: %zu\n",			context->header.len );
23845 				FPrintF( stdout, "HTTP header:\n%1{text}",		context->header.buf, context->header.len );
23846 			}
23847 			++sendCount;
23848 		}
23849 	}
23850 	require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
23851 
23852 	// If there's no wait period after the send, then exit.
23853 
23854 	if( context->receiveSecs == 0 ) goto exit;
23855 
23856 	// Create dispatch read sources for socket(s).
23857 
23858 	if( IsValidSocket( sockV4 ) )
23859 	{
23860 		SocketContext *		sockCtx;
23861 
23862 		sockCtx = SocketContextCreate( sockV4, context, &err );
23863 		require_noerr( err, exit );
23864 		sockV4 = kInvalidSocketRef;
23865 
23866 		err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx,
23867 			&context->readSourceV4 );
23868 		if( err ) ForgetSocketContext( &sockCtx );
23869 		require_noerr( err, exit );
23870 
23871 		dispatch_resume( context->readSourceV4 );
23872 	}
23873 
23874 	if( IsValidSocket( sockV6 ) )
23875 	{
23876 		SocketContext *		sockCtx;
23877 
23878 		sockCtx = SocketContextCreate( sockV6, context, &err );
23879 		require_noerr( err, exit );
23880 		sockV6 = kInvalidSocketRef;
23881 
23882 		err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx,
23883 			&context->readSourceV6 );
23884 		if( err ) ForgetSocketContext( &sockCtx );
23885 		require_noerr( err, exit );
23886 
23887 		dispatch_resume( context->readSourceV6 );
23888 	}
23889 
23890 	if( context->receiveSecs > 0 )
23891 	{
23892 		dispatch_after_f( dispatch_time_seconds( context->receiveSecs ), dispatch_get_main_queue(), kExitReason_Timeout,
23893 			Exit );
23894 	}
23895 	dispatch_main();
23896 
23897 exit:
23898 	ForgetSocket( &sockV4 );
23899 	ForgetSocket( &sockV6 );
23900 	dispatch_source_forget( &signalSource );
23901 	exit( err ? 1 : 0 );
23902 }
23903 
SocketToPortNumber(SocketRef inSock)23904 static int	SocketToPortNumber( SocketRef inSock )
23905 {
23906 	OSStatus		err;
23907 	sockaddr_ip		sip;
23908 	socklen_t		len;
23909 
23910 	len = (socklen_t) sizeof( sip );
23911 	err = getsockname( inSock, &sip.sa, &len );
23912 	err = map_socket_noerr_errno( inSock, err );
23913 	check_noerr( err );
23914 	return( err ? -1 : SockAddrGetPort( &sip ) );
23915 }
23916 
WriteSSDPSearchRequest(HTTPHeader * inHeader,const void * inHostSA,int inMX,const char * inST)23917 static OSStatus	WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST )
23918 {
23919 	OSStatus		err;
23920 
23921 	err = HTTPHeader_InitRequest( inHeader, "M-SEARCH", "*", "HTTP/1.1" );
23922 	require_noerr( err, exit );
23923 
23924 	err = HTTPHeader_SetField( inHeader, "Host", "%##a", inHostSA );
23925 	require_noerr( err, exit );
23926 
23927 	err = HTTPHeader_SetField( inHeader, "ST", "%s", inST ? inST : "ssdp:all" );
23928 	require_noerr( err, exit );
23929 
23930 	err = HTTPHeader_SetField( inHeader, "Man", "\"ssdp:discover\"" );
23931 	require_noerr( err, exit );
23932 
23933 	err = HTTPHeader_SetField( inHeader, "MX", "%d", inMX );
23934 	require_noerr( err, exit );
23935 
23936 	err = HTTPHeader_Commit( inHeader );
23937 	require_noerr( err, exit );
23938 
23939 exit:
23940 	return( err );
23941 }
23942 
23943 //===========================================================================================================================
23944 //	SSDPDiscoverPrintPrologue
23945 //===========================================================================================================================
23946 
SSDPDiscoverPrintPrologue(const SSDPDiscoverContext * inContext)23947 static void	SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext )
23948 {
23949 	const int				receiveSecs = inContext->receiveSecs;
23950 	const char *			ifName;
23951 	char					ifNameBuf[ IF_NAMESIZE + 1 ];
23952 	NetTransportType		ifType;
23953 
23954 	ifName = if_indextoname( inContext->ifindex, ifNameBuf );
23955 
23956 	ifType = kNetTransportType_Undefined;
23957 	if( ifName ) SocketGetInterfaceInfo( kInvalidSocketRef, ifName, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &ifType );
23958 
23959 	FPrintF( stdout, "Interface:        %s/%d/%s\n",
23960 		ifName ? ifName : "?", inContext->ifindex, NetTransportTypeToString( ifType ) );
23961 	FPrintF( stdout, "IP protocols:     %?s%?s%?s\n",
23962 		inContext->useIPv4, "IPv4", ( inContext->useIPv4 && inContext->useIPv6 ), ", ", inContext->useIPv6, "IPv6" );
23963 	FPrintF( stdout, "Receive duration: " );
23964 	if( receiveSecs >= 0 )	FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
23965 	else					FPrintF( stdout, "∞\n" );
23966 	FPrintF( stdout, "Start time:       %{du:time}\n", NULL );
23967 }
23968 
23969 //===========================================================================================================================
23970 //	SSDPDiscoverReadHandler
23971 //===========================================================================================================================
23972 
23973 static Boolean	_HTTPHeader_Validate( HTTPHeader *inHeader );
23974 
SSDPDiscoverReadHandler(void * inContext)23975 static void	SSDPDiscoverReadHandler( void *inContext )
23976 {
23977 	OSStatus						err;
23978 	struct timeval					now;
23979 	SocketContext * const			sockCtx	= (SocketContext *) inContext;
23980 	SSDPDiscoverContext * const		context	= (SSDPDiscoverContext *) sockCtx->userContext;
23981 	HTTPHeader * const				header	= &context->header;
23982 	sockaddr_ip						fromAddr;
23983 	size_t							msgLen;
23984 
23985 	gettimeofday( &now, NULL );
23986 
23987 	err = SocketRecvFrom( sockCtx->sock, header->buf, sizeof( header->buf ), &msgLen, &fromAddr, sizeof( fromAddr ),
23988 		NULL, NULL, NULL, NULL );
23989 	require_noerr( err, exit );
23990 
23991 	FPrintF( stdout, "---\n" );
23992 	FPrintF( stdout, "Receive time: %{du:time}\n",	&now );
23993 	FPrintF( stdout, "Source:       %##a\n", 		&fromAddr );
23994 	FPrintF( stdout, "Message size: %zu\n",			msgLen );
23995 	header->len = msgLen;
23996 	if( _HTTPHeader_Validate( header ) )
23997 	{
23998 		FPrintF( stdout, "HTTP header:\n%1{text}", header->buf, header->len );
23999 		if( header->extraDataLen > 0 )
24000 		{
24001 			FPrintF( stdout, "HTTP body: %1.1H", header->extraDataPtr, (int) header->extraDataLen, INT_MAX );
24002 		}
24003 	}
24004 	else
24005 	{
24006 		FPrintF( stdout, "Invalid HTTP message:\n%1.1H", header->buf, (int) msgLen, INT_MAX );
24007 		goto exit;
24008 	}
24009 
24010 exit:
24011 	if( err ) exit( 1 );
24012 }
24013 
24014 //===========================================================================================================================
24015 //	_HTTPHeader_Validate
24016 //
24017 //	Parses for the end of an HTTP header and updates the HTTPHeader structure so it's ready to parse. Returns true if valid.
24018 //	This assumes the "buf" and "len" fields are set. The other fields are set by this function.
24019 //
24020 //	Note: This was copied from CoreUtils because the HTTPHeader_Validate function is currently not exported in the framework.
24021 //===========================================================================================================================
24022 
_HTTPHeader_Validate(HTTPHeader * inHeader)24023 static Boolean	_HTTPHeader_Validate( HTTPHeader *inHeader )
24024 {
24025 	const char *		src;
24026 	const char *		end;
24027 
24028 	// Check for interleaved binary data (4 byte header that begins with $). See RFC 2326 section 10.12.
24029 
24030 	require( inHeader->len < sizeof( inHeader->buf ), exit );
24031 	src = inHeader->buf;
24032 	end = src + inHeader->len;
24033 	if( ( ( end - src ) >= 4 ) && ( src[ 0 ] == '$' ) )
24034 	{
24035 		src += 4;
24036 	}
24037 	else
24038 	{
24039 		// Search for an empty line (HTTP-style header/body separator). CRLFCRLF, LFCRLF, or LFLF accepted.
24040 		// $$$ TO DO: Start from the last search location to avoid re-searching the same data over and over.
24041 
24042 		for( ;; )
24043 		{
24044 			while( ( src < end ) && ( src[ 0 ] != '\n' ) ) ++src;
24045 			if( src >= end ) goto exit;
24046 			++src;
24047 			if( ( ( end - src ) >= 2 ) && ( src[ 0 ] == '\r' ) && ( src[ 1 ] == '\n' ) ) // CFLFCRLF or LFCRLF
24048 			{
24049 				src += 2;
24050 				break;
24051 			}
24052 			else if( ( ( end - src ) >= 1 ) && ( src[ 0 ] == '\n' ) ) // LFLF
24053 			{
24054 				src += 1;
24055 				break;
24056 			}
24057 		}
24058 	}
24059 	inHeader->extraDataPtr	= src;
24060 	inHeader->extraDataLen	= (size_t)( end - src );
24061 	inHeader->len			= (size_t)( src - inHeader->buf );
24062 	return( true );
24063 
24064 exit:
24065 	return( false );
24066 }
24067 
24068 #if( TARGET_OS_DARWIN )
24069 //===========================================================================================================================
24070 //	ResQueryCmd
24071 //===========================================================================================================================
24072 
24073 // res_query() from libresolv is actually called res_9_query (see /usr/include/resolv.h).
24074 
24075 SOFT_LINK_LIBRARY_EX( "/usr/lib", resolv );
24076 SOFT_LINK_FUNCTION_EX( resolv, res_9_query,
24077 	int,
24078 	( const char *dname, int class, int type, u_char *answer, int anslen ),
24079 	( dname, class, type, answer, anslen ) );
24080 
24081 // res_query() from libinfo
24082 
24083 SOFT_LINK_LIBRARY_EX( "/usr/lib", info );
24084 SOFT_LINK_FUNCTION_EX( info, res_query,
24085 	int,
24086 	( const char *dname, int class, int type, u_char *answer, int anslen ),
24087 	( dname, class, type, answer, anslen ) );
24088 
24089 typedef int ( *res_query_f )( const char *dname, int class, int type, u_char *answer, int anslen );
24090 
ResQueryCmd(void)24091 static void	ResQueryCmd( void )
24092 {
24093 	OSStatus		err;
24094 	res_query_f		res_query_ptr;
24095 	int				n;
24096 	uint16_t		type, class;
24097 	uint8_t			answer[ 1024 ];
24098 
24099 	// Get pointer to one of the res_query() functions.
24100 
24101 	if( gResQuery_UseLibInfo )
24102 	{
24103 		if( !SOFT_LINK_HAS_FUNCTION( info, res_query ) )
24104 		{
24105 			FPrintF( stderr, "Failed to soft link res_query from libinfo.\n" );
24106 			err = kNotFoundErr;
24107 			goto exit;
24108 		}
24109 		res_query_ptr = soft_res_query;
24110 	}
24111 	else
24112 	{
24113 		if( !SOFT_LINK_HAS_FUNCTION( resolv, res_9_query ) )
24114 		{
24115 			FPrintF( stderr, "Failed to soft link res_query from libresolv.\n" );
24116 			err = kNotFoundErr;
24117 			goto exit;
24118 		}
24119 		res_query_ptr = soft_res_9_query;
24120 	}
24121 
24122 	// Get record type.
24123 
24124 	err = RecordTypeFromArgString( gResQuery_Type, &type );
24125 	require_noerr( err, exit );
24126 
24127 	// Get record class.
24128 
24129 	if( gResQuery_Class )
24130 	{
24131 		err = RecordClassFromArgString( gResQuery_Class, &class );
24132 		require_noerr( err, exit );
24133 	}
24134 	else
24135 	{
24136 		class = kDNSServiceClass_IN;
24137 	}
24138 
24139 	// Print prologue.
24140 
24141 	FPrintF( stdout, "Name:       %s\n",			gResQuery_Name );
24142 	FPrintF( stdout, "Type:       %s (%u)\n",		RecordTypeToString( type ), type );
24143 	FPrintF( stdout, "Class:      %s (%u)\n",		( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
24144 	FPrintF( stdout, "Start time: %{du:time}\n",	NULL );
24145 	FPrintF( stdout, "---\n" );
24146 
24147 	// Call res_query().
24148 
24149 	n = res_query_ptr( gResQuery_Name, class, type, (u_char *) answer, (int) sizeof( answer ) );
24150 	if( n < 0 )
24151 	{
24152 		FPrintF( stderr, "res_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
24153 		err = kUnknownErr;
24154 		goto exit;
24155 	}
24156 
24157 	// Print result.
24158 
24159 	FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}\n", n, answer, (size_t) n );
24160 
24161 exit:
24162 	if( err ) exit( 1 );
24163 }
24164 
24165 //===========================================================================================================================
24166 //	ResolvDNSQueryCmd
24167 //===========================================================================================================================
24168 
24169 // dns_handle_t is defined as a pointer to a privately-defined struct in /usr/include/dns.h. It's defined as a void * here to
24170 // avoid including the header file.
24171 
24172 typedef void *		dns_handle_t;
24173 
24174 SOFT_LINK_FUNCTION_EX( resolv, dns_open, dns_handle_t, ( const char *path ), ( path ) );
24175 SOFT_LINK_FUNCTION_VOID_RETURN_EX( resolv, dns_free, ( dns_handle_t *dns ), ( dns ) );
24176 SOFT_LINK_FUNCTION_EX( resolv, dns_query,
24177 	int32_t, (
24178 		dns_handle_t		dns,
24179 		const char *		name,
24180 		uint32_t			dnsclass,
24181 		uint32_t			dnstype,
24182 		char *				buf,
24183 		uint32_t			len,
24184 		struct sockaddr *	from,
24185 		uint32_t *			fromlen ),
24186 	( dns, name, dnsclass, dnstype, buf, len, from, fromlen ) );
24187 
ResolvDNSQueryCmd(void)24188 static void	ResolvDNSQueryCmd( void )
24189 {
24190 	OSStatus			err;
24191 	int					n;
24192 	dns_handle_t		dns = NULL;
24193 	uint16_t			type, class;
24194 	sockaddr_ip			from;
24195 	uint32_t			fromLen;
24196 	uint8_t				answer[ 1024 ];
24197 
24198 	// Make sure that the required symbols are available.
24199 
24200 	if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_open ) )
24201 	{
24202 		FPrintF( stderr, "Failed to soft link dns_open from libresolv.\n" );
24203 		err = kNotFoundErr;
24204 		goto exit;
24205 	}
24206 
24207 	if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_free ) )
24208 	{
24209 		FPrintF( stderr, "Failed to soft link dns_free from libresolv.\n" );
24210 		err = kNotFoundErr;
24211 		goto exit;
24212 	}
24213 
24214 	if( !SOFT_LINK_HAS_FUNCTION( resolv, dns_query ) )
24215 	{
24216 		FPrintF( stderr, "Failed to soft link dns_query from libresolv.\n" );
24217 		err = kNotFoundErr;
24218 		goto exit;
24219 	}
24220 
24221 	// Get record type.
24222 
24223 	err = RecordTypeFromArgString( gResolvDNSQuery_Type, &type );
24224 	require_noerr( err, exit );
24225 
24226 	// Get record class.
24227 
24228 	if( gResolvDNSQuery_Class )
24229 	{
24230 		err = RecordClassFromArgString( gResolvDNSQuery_Class, &class );
24231 		require_noerr( err, exit );
24232 	}
24233 	else
24234 	{
24235 		class = kDNSServiceClass_IN;
24236 	}
24237 
24238 	// Get dns handle.
24239 
24240 	dns = soft_dns_open( gResolvDNSQuery_Path );
24241 	if( !dns )
24242 	{
24243 		FPrintF( stderr, "dns_open( %s ) failed.\n", gResolvDNSQuery_Path );
24244 		err = kUnknownErr;
24245 		goto exit;
24246 	}
24247 
24248 	// Print prologue.
24249 
24250 	FPrintF( stdout, "Name:       %s\n",			gResolvDNSQuery_Name );
24251 	FPrintF( stdout, "Type:       %s (%u)\n",		RecordTypeToString( type ), type );
24252 	FPrintF( stdout, "Class:      %s (%u)\n",		( class == kDNSServiceClass_IN ) ? "IN" : "???", class );
24253 	FPrintF( stdout, "Path:       %s\n",			gResolvDNSQuery_Path ? gResolvDNSQuery_Name : "<NULL>" );
24254 	FPrintF( stdout, "Start time: %{du:time}\n",	NULL );
24255 	FPrintF( stdout, "---\n" );
24256 
24257 	// Call dns_query().
24258 
24259 	memset( &from, 0, sizeof( from ) );
24260 	fromLen = (uint32_t) sizeof( from );
24261 	n = soft_dns_query( dns, gResolvDNSQuery_Name, class, type, (char *) answer, (uint32_t) sizeof( answer ), &from.sa,
24262 		&fromLen );
24263 	if( n < 0 )
24264 	{
24265 		FPrintF( stderr, "dns_query() failed with error: %d (%s).\n", h_errno, hstrerror( h_errno ) );
24266 		err = kUnknownErr;
24267 		goto exit;
24268 	}
24269 
24270 	// Print result.
24271 
24272 	FPrintF( stdout, "From:         %##a\n", &from );
24273 	FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}\n", n, answer, (size_t) n );
24274 
24275 exit:
24276 	if( dns ) soft_dns_free( dns );
24277 	if( err ) exit( 1 );
24278 }
24279 
24280 //===========================================================================================================================
24281 //	CFHostCmd
24282 //===========================================================================================================================
24283 
24284 static void
24285 	_CFHostResolveCallback(
24286 		CFHostRef				inHost,
24287 		CFHostInfoType			inInfoType,
24288 		const CFStreamError *	inError,
24289 		void *					inInfo );
24290 
CFHostCmd(void)24291 static void	CFHostCmd( void )
24292 {
24293 	OSStatus				err;
24294 	CFStringRef				name;
24295 	Boolean					success;
24296 	CFHostRef				host = NULL;
24297 	CFHostClientContext		context;
24298 	CFStreamError			streamErr;
24299 
24300 	name = CFStringCreateWithCString( kCFAllocatorDefault, gCFHost_Name, kCFStringEncodingUTF8 );
24301 	require_action( name, exit, err = kUnknownErr );
24302 
24303 	host = CFHostCreateWithName( kCFAllocatorDefault, name );
24304 	ForgetCF( &name );
24305 	require_action( host, exit, err = kUnknownErr );
24306 
24307 	memset( &context, 0, sizeof( context ) );
24308 	success = CFHostSetClient( host, _CFHostResolveCallback, &context );
24309 	require_action( success, exit, err = kUnknownErr );
24310 
24311 	CFHostScheduleWithRunLoop( host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
24312 
24313 	// Print prologue.
24314 
24315 	FPrintF( stdout, "Hostname:   %s\n",			gCFHost_Name );
24316 	FPrintF( stdout, "Start time: %{du:time}\n",	NULL );
24317 	FPrintF( stdout, "---\n" );
24318 
24319 	success = CFHostStartInfoResolution( host, kCFHostAddresses, &streamErr );
24320 	require_action( success, exit, err = kUnknownErr );
24321 	err = kNoErr;
24322 
24323 	CFRunLoopRun();
24324 
24325 exit:
24326 	CFReleaseNullSafe( host );
24327 	if( err ) exit( 1 );
24328 }
24329 
_CFHostResolveCallback(CFHostRef inHost,CFHostInfoType inInfoType,const CFStreamError * inError,void * inInfo)24330 static void	_CFHostResolveCallback( CFHostRef inHost, CFHostInfoType inInfoType, const CFStreamError *inError, void *inInfo )
24331 {
24332 	OSStatus			err;
24333 	struct timeval		now;
24334 
24335 	gettimeofday( &now, NULL );
24336 
24337 	Unused( inInfoType );
24338 	Unused( inInfo );
24339 
24340 	if( inError && ( inError->domain != 0 ) && ( inError->error ) )
24341 	{
24342 		err = inError->error;
24343 		if( inError->domain == kCFStreamErrorDomainNetDB )
24344 		{
24345 			FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
24346 		}
24347 		else
24348 		{
24349 			FPrintF( stderr, "Error %#m\n", err );
24350 		}
24351 	}
24352 	else
24353 	{
24354 		CFArrayRef					addresses;
24355 		CFIndex						count, i;
24356 		CFDataRef					addrData;
24357 		const struct sockaddr *		sockAddr;
24358 		Boolean						wasResolved = false;
24359 
24360 		addresses = CFHostGetAddressing( inHost, &wasResolved );
24361 		check( wasResolved );
24362 
24363 		if( addresses )
24364 		{
24365 			count = CFArrayGetCount( addresses );
24366 			for( i = 0; i < count; ++i )
24367 			{
24368 				addrData = CFArrayGetCFDataAtIndex( addresses, i, &err );
24369 				require_noerr( err, exit );
24370 
24371 				sockAddr = (const struct sockaddr *) CFDataGetBytePtr( addrData );
24372 				FPrintF( stdout, "%##a\n", sockAddr );
24373 			}
24374 		}
24375 		err = kNoErr;
24376 	}
24377 
24378 	FPrintF( stdout, "---\n" );
24379 	FPrintF( stdout, "End time:   %{du:time}\n", &now );
24380 
24381 	if( gCFHost_WaitSecs > 0 ) sleep( (unsigned int) gCFHost_WaitSecs );
24382 
24383 exit:
24384 	exit( err ? 1 : 0 );
24385 }
24386 
24387 //===========================================================================================================================
24388 //	DNSConfigAddCmd
24389 //
24390 //	Note: Based on ajn's supplemental test tool.
24391 //===========================================================================================================================
24392 
DNSConfigAddCmd(void)24393 static void	DNSConfigAddCmd( void )
24394 {
24395 	OSStatus					err;
24396 	CFMutableDictionaryRef		dict	= NULL;
24397 	CFMutableArrayRef			array	= NULL;
24398 	size_t						i;
24399 	SCDynamicStoreRef			store	= NULL;
24400 	CFStringRef					key		= NULL;
24401 	Boolean						success;
24402 
24403 	// Create dictionary.
24404 
24405 	dict = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
24406 	require_action( dict, exit, err = kNoMemoryErr );
24407 
24408 	// Add DNS server IP addresses.
24409 
24410 	array = CFArrayCreateMutable( NULL, (CFIndex) gDNSConfigAdd_IPAddrCount, &kCFTypeArrayCallBacks );
24411 	require_action( array, exit, err = kNoMemoryErr );
24412 
24413 	for( i = 0; i < gDNSConfigAdd_IPAddrCount; ++i )
24414 	{
24415 		CFStringRef		addrStr;
24416 
24417 		addrStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_IPAddrArray[ i ], kCFStringEncodingUTF8 );
24418 		require_action( addrStr, exit, err = kUnknownErr );
24419 
24420 		CFArrayAppendValue( array, addrStr );
24421 		CFRelease( addrStr );
24422 	}
24423 
24424 	CFDictionarySetValue( dict, kSCPropNetDNSServerAddresses, array );
24425 	ForgetCF( &array );
24426 
24427 	// Add domains, if any.
24428 
24429 	array = CFArrayCreateMutable( NULL, (CFIndex) Min( gDNSConfigAdd_DomainCount, 1 ), &kCFTypeArrayCallBacks );
24430 	require_action( array, exit, err = kNoMemoryErr );
24431 
24432 	if( gDNSConfigAdd_DomainCount > 0 )
24433 	{
24434 		for( i = 0; i < gDNSConfigAdd_DomainCount; ++i )
24435 		{
24436 			CFStringRef		domainStr;
24437 
24438 			domainStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_DomainArray[ i ], kCFStringEncodingUTF8 );
24439 			require_action( domainStr, exit, err = kUnknownErr );
24440 
24441 			CFArrayAppendValue( array, domainStr );
24442 			CFRelease( domainStr );
24443 		}
24444 	}
24445 	else
24446 	{
24447 		// There are no domains, but the domain array needs to be non-empty, so add a zero-length string to the array.
24448 
24449 		CFArrayAppendValue( array, CFSTR( "" ) );
24450 	}
24451 	CFDictionarySetValue( dict, kSCPropNetDNSSupplementalMatchDomains, array );
24452 	ForgetCF( &array );
24453 
24454 	// Set search orders.
24455 
24456 	if( gDNSConfigAdd_SearchOrder >= 0 )
24457 	{
24458 		const size_t		n = Min( gDNSConfigAdd_DomainCount, 1 );
24459 
24460 		array = CFArrayCreateMutable( NULL, (CFIndex) n, &kCFTypeArrayCallBacks );
24461 		require_action( array, exit, err = kNoMemoryErr );
24462 
24463 		for( i = 0; i < n; ++i )
24464 		{
24465 			err = CFArrayAppendInt64( array, gDNSConfigAdd_SearchOrder );
24466 			require_noerr( err, exit );
24467 		}
24468 		CFDictionarySetValue( dict, kSCPropNetDNSSupplementalMatchOrders, array );
24469 		ForgetCF( &array );
24470 	}
24471 
24472 	// Add interface, if any.
24473 
24474 	if( gDNSConfigAdd_Interface )
24475 	{
24476 		err = CFDictionarySetCString( dict, kSCPropInterfaceName, gDNSConfigAdd_Interface, kSizeCString );
24477 		require_noerr( err, exit );
24478 
24479 		CFDictionarySetValue( dict, kSCPropNetDNSConfirmedServiceID, gDNSConfigAdd_ID );
24480 	}
24481 
24482 	// Set dictionary in dynamic store.
24483 
24484 	store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
24485 	err = map_scerror( store );
24486 	require_noerr( err, exit );
24487 
24488 	key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigAdd_ID, kSCEntNetDNS );
24489 	require_action( key, exit, err = kUnknownErr );
24490 
24491 	success = SCDynamicStoreSetValue( store, key, dict );
24492 	require_action( success, exit, err = kUnknownErr );
24493 
24494 exit:
24495 	CFReleaseNullSafe( dict );
24496 	CFReleaseNullSafe( array );
24497 	CFReleaseNullSafe( store );
24498 	CFReleaseNullSafe( key );
24499 	gExitCode = err ? 1 : 0;
24500 }
24501 
24502 //===========================================================================================================================
24503 //	DNSConfigRemoveCmd
24504 //===========================================================================================================================
24505 
DNSConfigRemoveCmd(void)24506 static void	DNSConfigRemoveCmd( void )
24507 {
24508 	OSStatus				err;
24509 	SCDynamicStoreRef		store	= NULL;
24510 	CFStringRef				key		= NULL;
24511 	Boolean					success;
24512 
24513 	store = SCDynamicStoreCreate( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
24514 	err = map_scerror( store );
24515 	require_noerr( err, exit );
24516 
24517 	key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigRemove_ID, kSCEntNetDNS );
24518 	require_action( key, exit, err = kUnknownErr );
24519 
24520 	success = SCDynamicStoreRemoveValue( store, key );
24521 	require_action( success, exit, err = kUnknownErr );
24522 
24523 exit:
24524 	CFReleaseNullSafe( store );
24525 	CFReleaseNullSafe( key );
24526 	gExitCode = err ? 1 : 0;
24527 }
24528 
24529 //===========================================================================================================================
24530 //	XPCSendCmd
24531 //===========================================================================================================================
24532 
24533 static OSStatus	_XPCDictionaryCreateFromString( const char *inString, xpc_object_t *outDict );
24534 
XPCSendCmd(void)24535 static void	XPCSendCmd( void )
24536 {
24537 	OSStatus			err;
24538 	xpc_object_t		msg, reply;
24539 
24540 	err = _XPCDictionaryCreateFromString( gXPCSend_MessageStr, &msg );
24541 	require_noerr_quiet( err, exit );
24542 
24543 	FPrintF( stdout, "Service:    %s\n", gXPCSend_ServiceName );
24544 	FPrintF( stdout, "Message:    %s\n", gXPCSend_MessageStr );
24545 	FPrintF( stdout, "Start time: %{du:time}\n", NULL );
24546 	FPrintF( stdout, "---\n" );
24547 	FPrintF( stdout, "XPC Message:\n%{xpc}\n", msg );
24548 
24549 	err = xpc_send_message_sync( gXPCSend_ServiceName, 0, 0, msg, &reply );
24550 	xpc_forget( &msg );
24551 	require_noerr_quiet( err, exit );
24552 
24553 	FPrintF( stdout, "XPC Reply:\n%{xpc}\n", reply );
24554 	FPrintF( stdout, "---\n" );
24555 	FPrintF( stdout, "End time:   %{du:time}\n", NULL );
24556 	xpc_forget( &reply );
24557 
24558 exit:
24559 	if( err ) ErrQuit( 1, "error: %#m\n", err );
24560 }
24561 
24562 //===========================================================================================================================
24563 //	_XPCDictionaryCreateFromString
24564 //===========================================================================================================================
24565 
24566 #define kXPCObjectPrefix_Bool		"bool:"
24567 #define kXPCObjectPrefix_Data		"data:"
24568 #define kXPCObjectPrefix_Int64		"int:"
24569 #define kXPCObjectPrefix_String		"string:"
24570 #define kXPCObjectPrefix_UInt64		"uint:"
24571 #define kXPCObjectPrefix_UUID		"uuid:"
24572 
24573 typedef struct XPCListItem		XPCListItem;
24574 struct XPCListItem
24575 {
24576 	XPCListItem *		next;
24577 	xpc_object_t		obj;
24578 	char *				key;
24579 };
24580 
24581 static OSStatus	_XPCListItemCreate( xpc_object_t inObject, const char *inKey, XPCListItem **outItem );
24582 static void		_XPCListItemFree( XPCListItem *inItem );
24583 static void		_XPCListFree( XPCListItem *inList );
24584 
24585 static OSStatus	_XPCObjectFromString( const char *inString, xpc_object_t *outObject );
24586 
_XPCDictionaryCreateFromString(const char * inString,xpc_object_t * outDict)24587 static OSStatus	_XPCDictionaryCreateFromString( const char *inString, xpc_object_t *outDict )
24588 {
24589 	OSStatus				err;
24590 	xpc_object_t			container;
24591 	const char *			ptr		= inString;
24592 	const char * const		end		= inString + strlen( inString );
24593 	XPCListItem *			list	= NULL;
24594 
24595 	container = xpc_dictionary_create( NULL, NULL, 0 );
24596 	require_action( container, exit, err = kNoMemoryErr );
24597 
24598 	while( *ptr )
24599 	{
24600 		xpc_type_t				containerType;
24601 		xpc_object_t			value;
24602 		int						c;
24603 		char					keyStr[ 256 ];
24604 		char					valStr[ 256 ];
24605 
24606 		// At this point, zero or more of the current container's elements have been parsed.
24607 		// Skip the white space leading up to the container's next element, if any, or the container's end.
24608 
24609 		while( isspace_safe( *ptr ) ) ++ptr;
24610 
24611 		// Check if we're done with the current container.
24612 
24613 		c = *ptr;
24614 		if( c == '\0' ) break;
24615 
24616 		containerType = xpc_get_type( container );
24617 		if( ( ( containerType == XPC_TYPE_DICTIONARY ) && ( c == '}' ) ) ||
24618 			( ( containerType == XPC_TYPE_ARRAY )      && ( c == ']' ) ) )
24619 		{
24620 			XPCListItem *		item;
24621 
24622 			item = list;
24623 			require_action_quiet( item, exit, err = kMalformedErr );
24624 
24625 			++ptr;
24626 
24627 			// Add the current container to its parent container.
24628 
24629 			if( item->key )
24630 			{
24631 				xpc_dictionary_set_value( item->obj, item->key, container );
24632 			}
24633 			else
24634 			{
24635 				xpc_array_append_value( item->obj, container );
24636 			}
24637 
24638 			// Continue with the parent container.
24639 
24640 			xpc_release( container );
24641 			container = xpc_retain( item->obj );
24642 			list = item->next;
24643 			_XPCListItemFree( item );
24644 			continue;
24645 		}
24646 
24647 		// If the current container is a dictionary, parse the key string.
24648 
24649 		if( containerType == XPC_TYPE_DICTIONARY )
24650 		{
24651 			err = _ParseEscapedString( ptr, end, "={}[]" kWhiteSpaceCharSet, keyStr, sizeof( keyStr ), NULL, NULL, &ptr );
24652 			require_noerr_quiet( err, exit );
24653 
24654 			c = *ptr;
24655 			require_action_quiet( c == '=', exit, err = kMalformedErr );
24656 			++ptr;
24657 		}
24658 
24659 		// Check if the value is a dictionary ({...}) or an array ([...]).
24660 
24661 		c = *ptr;
24662 		if( ( c == '{' ) || ( c == '[' ) )
24663 		{
24664 			XPCListItem *		item;
24665 
24666 			++ptr;
24667 
24668 			// Save the current container.
24669 
24670 			err = _XPCListItemCreate( container, ( containerType == XPC_TYPE_DICTIONARY ) ? keyStr : NULL, &item );
24671 			require_noerr( err, exit );
24672 
24673 			item->next = list;
24674 			list = item;
24675 			item = NULL;
24676 
24677 			// Create and continue with the child container.
24678 
24679 			xpc_release( container );
24680 			if( c == '{' )
24681 			{
24682 				container = xpc_dictionary_create( NULL, NULL, 0 );
24683 				require_action( container, exit, err = kNoMemoryErr );
24684 			}
24685 			else
24686 			{
24687 				container = xpc_array_create( NULL, 0 );
24688 				require_action( container, exit, err = kNoMemoryErr );
24689 			}
24690 			continue;
24691 		}
24692 
24693 		// Parse the value string.
24694 
24695 		err = _ParseEscapedString( ptr, end, "{}[]" kWhiteSpaceCharSet, valStr, sizeof( valStr ), NULL, NULL, &ptr );
24696 		require_noerr_quiet( err, exit );
24697 
24698 		err = _XPCObjectFromString( valStr, &value );
24699 		require_noerr_quiet( err, exit );
24700 
24701 		if( containerType == XPC_TYPE_DICTIONARY )
24702 		{
24703 			xpc_dictionary_set_value( container, keyStr, value );
24704 		}
24705 		else
24706 		{
24707 			xpc_array_append_value( container, value );
24708 		}
24709 		xpc_forget( &value );
24710 	}
24711 	require_action_quiet( !list, exit, err = kMalformedErr );
24712 
24713 	check( container );
24714 	check( xpc_get_type( container ) == XPC_TYPE_DICTIONARY );
24715 
24716 	*outDict = container;
24717 	container = NULL;
24718 	err = kNoErr;
24719 
24720 exit:
24721 	xpc_release_null_safe( container );
24722 	if( list ) _XPCListFree( list );
24723 	return( err );
24724 }
24725 
_XPCObjectFromString(const char * inString,xpc_object_t * outObject)24726 static OSStatus	_XPCObjectFromString( const char *inString, xpc_object_t *outObject )
24727 {
24728 	OSStatus			err;
24729 	xpc_object_t		object;
24730 
24731 	if( 0 ) {}
24732 
24733 	// Bool
24734 
24735 	else if( stricmp_prefix( inString, kXPCObjectPrefix_Bool ) == 0 )
24736 	{
24737 		const char * const		str = inString + sizeof_string( kXPCObjectPrefix_Bool );
24738 		bool					value;
24739 
24740 		if( IsTrueString(  str, kSizeCString ) )
24741 		{
24742 			value = true;
24743 		}
24744 		else if( IsFalseString( str, kSizeCString ) )
24745 		{
24746 			value = false;
24747 		}
24748 		else
24749 		{
24750 			err = kValueErr;
24751 			goto exit;
24752 		}
24753 
24754 		object = xpc_bool_create( value );
24755 		require_action( object, exit, err = kNoMemoryErr );
24756 	}
24757 
24758 	// Data
24759 
24760 	else if( stricmp_prefix( inString, kXPCObjectPrefix_Data ) == 0 )
24761 	{
24762 		const char * const		str = inString + sizeof_string( kXPCObjectPrefix_Data );
24763 		uint8_t *				dataPtr;
24764 		size_t					dataLen;
24765 
24766 		err = HexToDataCopy( str, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL );
24767 		require_noerr( err, exit );
24768 
24769 		object = xpc_data_create( dataPtr, dataLen );
24770 		free( dataPtr );
24771 		require_action( object, exit, err = kNoMemoryErr );
24772 	}
24773 
24774 	// Int64
24775 
24776 	else if( stricmp_prefix( inString, kXPCObjectPrefix_Int64 ) == 0 )
24777 	{
24778 		const char * const		str = inString + sizeof_string( kXPCObjectPrefix_Int64 );
24779 		int64_t					i64;
24780 
24781 		i64 = _StringToInt64( str, &err );
24782 		require_noerr_quiet( err, exit );
24783 
24784 		object = xpc_int64_create( i64 );
24785 		require_action( object, exit, err = kNoMemoryErr );
24786 	}
24787 
24788 	// String
24789 
24790 	else if( stricmp_prefix( inString, kXPCObjectPrefix_String ) == 0 )
24791 	{
24792 		const char * const		str = inString + sizeof_string( kXPCObjectPrefix_String );
24793 
24794 		object = xpc_string_create( str );
24795 		require_action( object, exit, err = kNoMemoryErr );
24796 	}
24797 
24798 	// UInt64
24799 
24800 	else if( stricmp_prefix( inString, kXPCObjectPrefix_UInt64 ) == 0 )
24801 	{
24802 		const char * const		str = inString + sizeof_string( kXPCObjectPrefix_UInt64 );
24803 		uint64_t				u64;
24804 
24805 		u64 = _StringToUInt64( str, &err );
24806 		require_noerr_quiet( err, exit );
24807 
24808 		object = xpc_uint64_create( u64 );
24809 		require_action( object, exit, err = kNoMemoryErr );
24810 	}
24811 
24812 	// UUID
24813 
24814 	else if( stricmp_prefix( inString, kXPCObjectPrefix_UUID ) == 0 )
24815 	{
24816 		const char * const		str = inString + sizeof_string( kXPCObjectPrefix_UUID );
24817 		uuid_t					uuid;
24818 
24819 		err = uuid_parse( str, uuid );
24820 		require_noerr_action_quiet( err, exit, err = kValueErr );
24821 
24822 		object = xpc_uuid_create( uuid );
24823 		require_action( object, exit, err = kNoMemoryErr );
24824 	}
24825 
24826 	// Unsupported prefix
24827 
24828 	else
24829 	{
24830 		err = kValueErr;
24831 		goto exit;
24832 	}
24833 
24834 	*outObject = object;
24835 	err = kNoErr;
24836 
24837 exit:
24838 	return( err );
24839 }
24840 
_XPCListItemCreate(xpc_object_t inObject,const char * inKey,XPCListItem ** outItem)24841 static OSStatus	_XPCListItemCreate( xpc_object_t inObject, const char *inKey, XPCListItem **outItem )
24842 {
24843 	OSStatus			err;
24844 	XPCListItem *		item;
24845 
24846 	item = (XPCListItem *) calloc( 1, sizeof( *item ) );
24847 	require_action( item, exit, err = kNoMemoryErr );
24848 
24849 	item->obj = xpc_retain( inObject );
24850 	if( ( xpc_get_type( item->obj ) == XPC_TYPE_DICTIONARY ) && inKey )
24851 	{
24852 		item->key = strdup( inKey );
24853 		require_action( item->key, exit, err = kNoMemoryErr );
24854 	}
24855 
24856 	*outItem = item;
24857 	item = NULL;
24858 	err = kNoErr;
24859 
24860 exit:
24861 	if( item ) _XPCListItemFree( item );
24862 	return( err );
24863 }
24864 
_XPCListItemFree(XPCListItem * inItem)24865 static void	_XPCListItemFree( XPCListItem *inItem )
24866 {
24867 	xpc_forget( &inItem->obj );
24868 	ForgetMem( &inItem->key );
24869 	free( inItem );
24870 }
24871 
_XPCListFree(XPCListItem * inList)24872 static void _XPCListFree( XPCListItem *inList )
24873 {
24874 	XPCListItem *		item;
24875 
24876 	while( ( item = inList ) != NULL )
24877 	{
24878 		inList = item->next;
24879 		_XPCListItemFree( item );
24880 	}
24881 }
24882 #endif	// TARGET_OS_DARWIN
24883 
24884 #if( MDNSRESPONDER_PROJECT )
24885 //===========================================================================================================================
24886 //	InterfaceMonitorCmd
24887 //===========================================================================================================================
24888 
24889 static void	_InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor );
24890 static void	_InterfaceMonitorSignalHandler( void *inContext );
24891 
InterfaceMonitorCmd(void)24892 static void	InterfaceMonitorCmd( void )
24893 {
24894 	OSStatus						err;
24895 	mdns_interface_monitor_t		monitor;
24896 	dispatch_source_t				signalSource = NULL;
24897 	uint32_t						ifIndex;
24898 	__block int						exitCode;
24899 
24900 	err = InterfaceIndexFromArgString( gInterface, &ifIndex );
24901 	require_noerr_quiet( err, exit );
24902 
24903 	monitor = mdns_interface_monitor_create( ifIndex );
24904 	require_action( monitor, exit, err = kNoResourcesErr );
24905 
24906 	exitCode = 0;
24907 	mdns_interface_monitor_set_queue( monitor, dispatch_get_main_queue() );
24908 	mdns_interface_monitor_set_event_handler( monitor,
24909 	^( mdns_event_t inEvent, OSStatus inError )
24910 	{
24911 		switch( inEvent )
24912 		{
24913 			case mdns_event_error:
24914 				FPrintF( stderr, "error: Interface monitor failed: %#m\n", inError );
24915 				mdns_interface_monitor_invalidate( monitor );
24916 				exitCode = 1;
24917 				break;
24918 
24919 			case mdns_event_invalidated:
24920 				FPrintF( stdout, "Interface monitor invalidated.\n" );
24921 				mdns_release( monitor );
24922 				exit( exitCode );
24923 
24924 			default:
24925 				FPrintF( stdout, "Unhandled event '%s' (%ld)\n", mdns_event_to_string( inEvent ), (long) inEvent );
24926 				break;
24927 		}
24928 	} );
24929 	mdns_interface_monitor_set_update_handler( monitor,
24930 	^( __unused mdns_interface_flags_t inChangeFlags )
24931 	{
24932 		_InterfaceMonitorPrint( monitor );
24933 	} );
24934 
24935 	_InterfaceMonitorPrint( monitor );
24936 	mdns_interface_monitor_activate( monitor );
24937 
24938 	signal( SIGINT, SIG_IGN );
24939 	err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), _InterfaceMonitorSignalHandler, monitor,
24940 		&signalSource );
24941 	require_noerr( err, exit );
24942 	dispatch_resume( signalSource );
24943 
24944 	dispatch_main();
24945 
24946 exit:
24947 	if( err ) ErrQuit( 1, "error: %#m\n", err );
24948 }
24949 
_InterfaceMonitorPrint(mdns_interface_monitor_t inMonitor)24950 static void	_InterfaceMonitorPrint( mdns_interface_monitor_t inMonitor )
24951 {
24952 	FPrintF( stdout, "%{du:time} %@\n", NULL, inMonitor );
24953 }
24954 
_InterfaceMonitorSignalHandler(void * inContext)24955 static void	_InterfaceMonitorSignalHandler( void *inContext )
24956 {
24957 	mdns_interface_monitor_invalidate( (mdns_interface_monitor_t) inContext );
24958 }
24959 
24960 //===========================================================================================================================
24961 //	QuerierCommand
24962 //===========================================================================================================================
24963 
24964 typedef struct
24965 {
24966 	dispatch_queue_t				queue;					// Serial queue for command's events.
24967 	dispatch_semaphore_t			doneSem;				// Semaphore to signal when command is done.
24968 	uint8_t *						qname;					// Name of record to query for.
24969 	mdns_querier_t					querier;				// Querier.
24970 	dispatch_source_t				sourceSigInt;			// Dispatch source for SIGINT.
24971 	dispatch_source_t				sourceSigTerm;			// Dispatch source for SIGTERM.
24972 	int32_t							refCount;				// Reference count.
24973 	OSStatus						error;					// Command's error.
24974 	uint32_t						ifIndex;				// Interface index for scoping.
24975 	uint16_t						qtype;					// Type of record to query for.
24976 	uint16_t						qclass;					// Class of record to query for.
24977 	pid_t							delegatorPID;			// Delegator PID.
24978 	uint8_t							delegatorUUID[ 16 ];	// Delegator UUID.
24979 	Boolean							haveDelegatorPID;		// True if delegatorPID is set.
24980 	Boolean							haveDelegatorUUID;		// True if delegatorUUID is set.
24981 	Boolean							dnssecOK;				// True if queries need an OPT record with the DO bit set.
24982 	Boolean							checkingDisabled;		// True if queries need the CD bit set.
24983 	Boolean							done;					// True if the command is done.
24984 
24985 	// Variables for resolver.
24986 
24987 	mdns_resolver_type_t			resolverType;			// Type of resolver to use.
24988 	mdns_resolver_t					resolver;				// Resolver.
24989 	CFMutableArrayRef				serverAddrs;			// Server addresses to use for resolver.
24990 	char *							providerName;			// Provider name for resolver.
24991 	char *							urlPath;				// URL path for resolver.
24992 	Boolean							noConnectionReuse;		// True if connection reuse is to be disabled.
24993 	Boolean							squashCNAMEs;			// True if CNAMEs should be squashed.
24994 
24995 	// Variables for DNS service manager.
24996 
24997 	mdns_dns_service_manager_t		manager;				// DNS service manager.
24998 	mdns_dns_service_t				service;				// DNS service for query.
24999 
25000 }	QuerierCmd;
25001 
25002 static OSStatus	_QuerierCmdCreate( QuerierCmd **outCmd );
25003 static void		_QuerierCmdRetain( QuerierCmd *inCmd );
25004 static void		_QuerierCmdRelease( QuerierCmd *inCmd );
25005 static OSStatus	_QuerierCmdRun( QuerierCmd *inCmd );
25006 
QuerierCommand(void)25007 static void	QuerierCommand( void )
25008 {
25009 	OSStatus			err;
25010 	QuerierCmd *		cmd = NULL;
25011 	size_t				i;
25012 	uint8_t				qname[ kDomainNameLengthMax ];
25013 
25014 	err = _QuerierCmdCreate( &cmd );
25015 	require_noerr( err, exit );
25016 
25017 	if( gInterface )
25018 	{
25019 		err = InterfaceIndexFromArgString( gInterface, &cmd->ifIndex );
25020 		require_noerr_quiet( err, exit );
25021 	}
25022 	else
25023 	{
25024 		cmd->ifIndex = 0;
25025 	}
25026 	err = DomainNameFromString( qname, gQuerier_Name, NULL );
25027 	if( err )
25028 	{
25029 		FPrintF( stderr, "error: Invalid domain name: '%s'\n", gDNSQuery_Name );
25030 		goto exit;
25031 	}
25032 	err = DomainNameDup( qname, &cmd->qname, NULL );
25033 	require_noerr( err, exit );
25034 
25035 	err = RecordTypeFromArgString( gQuerier_Type, &cmd->qtype );
25036 	require_noerr_quiet( err, exit );
25037 
25038 	err = RecordClassFromArgString( gQuerier_Class, &cmd->qclass );
25039 	require_noerr( err, exit );
25040 
25041 	if( gQuerier_Delegator )
25042 	{
25043 		err = StringToUUID( gQuerier_Delegator, kSizeCString, false, cmd->delegatorUUID );
25044 		if( !err )
25045 		{
25046 			cmd->haveDelegatorUUID = true;
25047 		}
25048 		else
25049 		{
25050 			cmd->delegatorPID = _StringToPID( gQuerier_Delegator, &err );
25051 			if( err )
25052 			{
25053 				FPrintF( stderr, "error: Invalid delegator PID or UUID: %s\n", gQuerier_Delegator );
25054 				err = kParamErr;
25055 				goto exit;
25056 			}
25057 			cmd->haveDelegatorPID = true;
25058 		}
25059 	}
25060 	if( gQuerier_ResolverType )
25061 	{
25062 		cmd->resolverType = (mdns_resolver_type_t) CLIArgToValue( "resolverType", gQuerier_ResolverType, &err,
25063 			kMDNSResolverTypeStr_Normal,	(int) mdns_resolver_type_normal,
25064 			kMDNSResolverTypeStr_TCPOnly,	(int) mdns_resolver_type_tcp,
25065 			kMDNSResolverTypeStr_TLS,		(int) mdns_resolver_type_tls,
25066 			kMDNSResolverTypeStr_HTTPS,		(int) mdns_resolver_type_https,
25067 			NULL );
25068 		require_noerr_quiet( err, exit );
25069 
25070 		for( i = 0; i < gQuerier_ServerAddrCount; ++i )
25071 		{
25072 			const char * const		addrStr = gQuerier_ServerAddrs[ i ];
25073 			mdns_address_t			serverAddr;
25074 
25075 			serverAddr = mdns_address_create_from_ip_address_string( addrStr );
25076 			if( !serverAddr )
25077 			{
25078 				FPrintF( stderr, "error: Failed to create address for '%s'\n", addrStr );
25079 				err = kParamErr;
25080 				goto exit;
25081 			}
25082 			CFArrayAppendValue( cmd->serverAddrs, serverAddr );
25083 			mdns_release( serverAddr );
25084 		}
25085 		if( gQuerier_ProviderName )
25086 		{
25087 			cmd->providerName = strdup( gQuerier_ProviderName );
25088 			require_action( cmd->providerName, exit, err = kNoMemoryErr );
25089 		}
25090 		if( gQuerier_URLPath )
25091 		{
25092 			cmd->urlPath = strdup( gQuerier_URLPath );
25093 			require_action( cmd->urlPath, exit, err = kNoMemoryErr );
25094 		}
25095 		cmd->noConnectionReuse	= gQuerier_NoConnectionReuse ? true : false;
25096 		cmd->squashCNAMEs		= gQuerier_SquashCNAMEs		 ? true : false;
25097 	}
25098 	cmd->dnssecOK			= gQuerier_DNSSECOK			? true : false;
25099 	cmd->checkingDisabled	= gQuerier_CheckingDisabled	? true : false;
25100 	err = _QuerierCmdRun( cmd );
25101 	require_noerr( err, exit );
25102 
25103 exit:
25104 	if( cmd ) _QuerierCmdRelease( cmd );
25105 	gExitCode = err ? 1 : 0;
25106 }
25107 
25108 //===========================================================================================================================
25109 
_QuerierCmdCreate(QuerierCmd ** outCmd)25110 static OSStatus	_QuerierCmdCreate( QuerierCmd **outCmd )
25111 {
25112 	OSStatus			err;
25113 	QuerierCmd *		cmd;
25114 
25115 	cmd = (QuerierCmd *) calloc( 1, sizeof( *cmd ) );
25116 	require_action( cmd, exit, err = kNoResourcesErr );
25117 
25118 	cmd->refCount		= 1;
25119 	cmd->resolverType	= mdns_resolver_type_null;
25120 
25121 	cmd->queue = dispatch_queue_create( "com.apple.dnssdutil.querier-command", DISPATCH_QUEUE_SERIAL );
25122 	require_action( cmd->queue, exit, err = kNoResourcesErr );
25123 
25124 	cmd->doneSem = dispatch_semaphore_create( 0 );
25125 	require_action( cmd->doneSem, exit, err = kNoResourcesErr );
25126 
25127 	cmd->serverAddrs = CFArrayCreateMutable( kCFAllocatorDefault, 0, &mdns_cfarray_callbacks );
25128 	require_action( cmd->serverAddrs, exit, err = kNoResourcesErr );
25129 
25130 	*outCmd = cmd;
25131 	cmd = NULL;
25132 	err = kNoErr;
25133 
25134 exit:
25135 	if( cmd ) _QuerierCmdRelease( cmd );
25136 	return( err );
25137 }
25138 
25139 //===========================================================================================================================
25140 
_QuerierCmdRetain(QuerierCmd * inCmd)25141 static void	_QuerierCmdRetain( QuerierCmd *inCmd )
25142 {
25143 	atomic_add_32( &inCmd->refCount, 1 );
25144 }
25145 
25146 //===========================================================================================================================
25147 
_QuerierCmdRelease(QuerierCmd * inCmd)25148 static void	_QuerierCmdRelease( QuerierCmd *inCmd )
25149 {
25150 	if( atomic_add_and_fetch_32( &inCmd->refCount, -1 ) == 0 )
25151 	{
25152 		check( !inCmd->sourceSigInt );
25153 		check( !inCmd->sourceSigTerm );
25154 		check( !inCmd->resolver );
25155 		check( !inCmd->manager );
25156 		check( !inCmd->service );
25157 		check( !inCmd->querier );
25158 		dispatch_forget( &inCmd->queue );
25159 		dispatch_forget( &inCmd->doneSem );
25160 		ForgetMem( &inCmd->qname );
25161 		ForgetCF( &inCmd->serverAddrs );
25162 		ForgetMem( &inCmd->providerName );
25163 		ForgetMem( &inCmd->urlPath );
25164 		free( inCmd );
25165 	}
25166 }
25167 
25168 //===========================================================================================================================
25169 
25170 static void	_QuerierCmdStart( void *inCtx );
25171 static void	_QuerierCmdStop( QuerierCmd *inCmd, OSStatus inError );
25172 static void	_QuerierCmdSigIntHandler( void *inCtx );
25173 static void	_QuerierCmdSigTermHandler( void *inCtx );
25174 
_QuerierCmdRun(QuerierCmd * inCmd)25175 static OSStatus	_QuerierCmdRun( QuerierCmd *inCmd )
25176 {
25177 	dispatch_async_f( inCmd->queue, inCmd, _QuerierCmdStart );
25178     dispatch_semaphore_wait( inCmd->doneSem, DISPATCH_TIME_FOREVER );
25179 	return( inCmd->error );
25180 }
25181 
_QuerierCmdStart(void * inCtx)25182 static void	_QuerierCmdStart( void *inCtx )
25183 {
25184 	OSStatus				err;
25185 	QuerierCmd * const		cmd		= (QuerierCmd *) inCtx;
25186 	dns_config_t *			config	= NULL;
25187 	mdns_querier_t			querier	= NULL;
25188 	const char *			ifNamePtr;
25189 	char					ifNameBuf[ IF_NAMESIZE + 1 ];
25190 
25191 	signal( SIGINT, SIG_IGN );
25192 	err = DispatchSignalSourceCreate( SIGINT, cmd->queue, _QuerierCmdSigIntHandler, cmd, &cmd->sourceSigInt );
25193 	require_noerr( err, exit );
25194 	dispatch_resume( cmd->sourceSigInt );
25195 
25196 	signal( SIGTERM, SIG_IGN );
25197 	err = DispatchSignalSourceCreate( SIGTERM, cmd->queue, _QuerierCmdSigTermHandler, cmd, &cmd->sourceSigTerm );
25198 	require_noerr( err, exit );
25199 	dispatch_resume( cmd->sourceSigTerm );
25200 
25201 	ifNamePtr = if_indextoname( cmd->ifIndex, ifNameBuf );
25202 	FPrintF( stdout, "Interface:      %u (%s)\n",		cmd->ifIndex, ifNamePtr ? ifNamePtr : "?" );
25203 	FPrintF( stdout, "Name:           %{du:dname}\n",	cmd->qname );
25204 	FPrintF( stdout, "Type:           %s (%u)\n",		RecordTypeToString( cmd->qtype ), cmd->qtype );
25205 	FPrintF( stdout, "Class:          %s (%u)\n",		RecordClassToString( cmd->qclass ), cmd->qclass );
25206 	if( cmd->resolverType != mdns_resolver_type_null )
25207 	{
25208 		CFIndex		n, i;
25209 
25210 		FPrintF( stdout, "Resolver Type:  %s\n", mdns_resolver_type_to_string( cmd->resolverType ) );
25211 		if( cmd->providerName )	FPrintF( stdout, "Provider Name:  %s\n", cmd->providerName );
25212 		if( cmd->urlPath )		FPrintF( stdout, "URL path:       %s\n", cmd->urlPath );
25213 		FPrintF( stdout, "Server(s):      " );
25214 		n = CFArrayGetCount( cmd->serverAddrs );
25215 		for( i = 0; i < n; ++i )
25216 		{
25217 			FPrintF( stdout, "%s%@", ( i == 0 ) ? "" : ", ", CFArrayGetValueAtIndex( cmd->serverAddrs, i ) );
25218 		}
25219 		FPrintF( stdout, "\n" );
25220 		FPrintF( stdout, "Start time:     %{du:time}\n", NULL );
25221 		FPrintF( stdout, "---\n" );
25222 
25223 		cmd->resolver = mdns_resolver_create( cmd->resolverType, cmd->ifIndex, &err );
25224 		require_noerr( err, exit );
25225 
25226 		if( cmd->providerName )
25227 		{
25228 			err = mdns_resolver_set_provider_name( cmd->resolver, cmd->providerName );
25229 			require_noerr( err, exit );
25230 		}
25231 		if( cmd->urlPath )
25232 		{
25233 			err = mdns_resolver_set_url_path( cmd->resolver, cmd->urlPath );
25234 			require_noerr( err, exit );
25235 		}
25236 		if( cmd->noConnectionReuse ) mdns_resolver_disable_connection_reuse( cmd->resolver, true );
25237 		if( cmd->squashCNAMEs ) mdns_resolver_set_squash_cnames( cmd->resolver, true );
25238 		for( i = 0; i < n; ++i )
25239 		{
25240 			const mdns_address_t		addr = (mdns_address_t) CFArrayGetValueAtIndex( cmd->serverAddrs, i );
25241 
25242 			err = mdns_resolver_add_server_address( cmd->resolver, addr );
25243 			require_noerr( err, exit );
25244 		}
25245 		mdns_resolver_activate( cmd->resolver );
25246 
25247 		querier = mdns_resolver_create_querier( cmd->resolver, &err );
25248 		require_noerr( err, exit );
25249 	}
25250 	else
25251 	{
25252 		FPrintF( stdout, "Start time:     %{du:time}\n", NULL );
25253 		FPrintF( stdout, "---\n" );
25254 
25255 		config = dns_configuration_copy();
25256 		require_action( config, exit, err = kUnknownErr );
25257 
25258 		cmd->manager = mdns_dns_service_manager_create( cmd->queue, &err );
25259 		require_noerr( err, exit );
25260 
25261 		_QuerierCmdRetain( cmd );
25262 		mdns_dns_service_manager_set_event_handler( cmd->manager,
25263 		^( mdns_event_t inEvent, OSStatus inError )
25264 		{
25265 			switch( inEvent )
25266 			{
25267 				case mdns_event_error:
25268 					if( !cmd->done )
25269 					{
25270 						FPrintF( stderr, "error: DNS service manager failed: %#m\n", inError );
25271 						_QuerierCmdStop( cmd, inError );
25272 					}
25273 					break;
25274 
25275 				case mdns_event_invalidated:
25276 					_QuerierCmdRelease( cmd );
25277 					break;
25278 
25279 				default:
25280 					break;
25281 			}
25282 		} );
25283 		mdns_dns_service_manager_apply_dns_config( cmd->manager, config );
25284 
25285 		if( cmd->ifIndex == 0 )
25286 		{
25287 			cmd->service = mdns_dns_service_manager_get_unscoped_service( cmd->manager, cmd->qname );
25288 		}
25289 		else
25290 		{
25291 			cmd->service = mdns_dns_service_manager_get_interface_scoped_service( cmd->manager, cmd->qname, cmd->ifIndex );
25292 		}
25293 		if( !cmd->service )
25294 		{
25295 			FPrintF( stderr, "error: Failed to get DNS service for %{du:dname}\n", cmd->qname );
25296 			err = kNotFoundErr;
25297 			goto exit;
25298 		}
25299 		mdns_retain( cmd->service );
25300 		FPrintF( stdout, "Using DNS service: %@\n\n", cmd->service );
25301 
25302 		querier = mdns_dns_service_create_querier( cmd->service, &err );
25303 		require_noerr( err, exit );
25304 	}
25305 	err = mdns_querier_set_query( querier, cmd->qname, cmd->qtype, cmd->qclass );
25306 	require_noerr( err, exit );
25307 
25308 	if( cmd->dnssecOK )			mdns_querier_set_dnssec_ok( querier, true );
25309 	if( cmd->checkingDisabled )	mdns_querier_set_checking_disabled( querier, true );
25310 	cmd->querier = querier;
25311 	querier = NULL;
25312 
25313 	if( cmd->haveDelegatorPID ) mdns_querier_set_delegator_pid( cmd->querier, cmd->delegatorPID );
25314 	else if( cmd->haveDelegatorUUID ) mdns_querier_set_delegator_uuid( cmd->querier, cmd->delegatorUUID );
25315 
25316 	_QuerierCmdRetain( cmd );
25317 	mdns_querier_set_queue( cmd->querier, cmd->queue );
25318 	mdns_querier_set_result_handler( cmd->querier,
25319 	^ {
25320 		if( !cmd->done )
25321 		{
25322 			const mdns_querier_result_type_t		resultType = mdns_querier_get_result_type( cmd->querier );
25323 
25324 			if( resultType == mdns_querier_result_type_response )
25325 			{
25326 				const uint8_t * const		msgPtr = mdns_querier_get_response_ptr( cmd->querier );
25327 				const size_t				msgLen = mdns_querier_get_response_length( cmd->querier );
25328 
25329 				FPrintF( stdout, "Message size:     %zu bytes\n", msgLen );
25330 				FPrintF( stdout, "%{du:dnsmsg}\n", msgPtr, msgLen );
25331 				_QuerierCmdStop( cmd, kNoErr );
25332 			}
25333 			else
25334 			{
25335 				OSStatus		querierErr;
25336 
25337 				if( resultType == mdns_querier_result_type_error )
25338 				{
25339 					querierErr = mdns_querier_get_error( cmd->querier );
25340 					if( !querierErr ) querierErr = kUnknownErr;
25341 				}
25342 				else
25343 				{
25344 					querierErr = kUnexpectedErr;
25345 				}
25346 				FPrintF( stderr, "error: Unexpected querier result: %s, error: %#m\n",
25347 					mdns_querier_result_type_to_string( resultType ), querierErr );
25348 				_QuerierCmdStop( cmd, querierErr );
25349 			}
25350 		}
25351 		_QuerierCmdRelease( cmd );
25352 	} );
25353 	mdns_querier_activate( cmd->querier );
25354 
25355 exit:
25356 	if( config ) dns_configuration_free( config );
25357 	mdns_release_null_safe( querier );
25358 	if( err ) _QuerierCmdStop( cmd, err );
25359 }
25360 
25361 //===========================================================================================================================
25362 
25363 #define mdns_dns_service_manager_forget( X )		ForgetCustomEx( X, mdns_dns_service_manager_invalidate, mdns_release )
25364 
_QuerierCmdStop(QuerierCmd * inCmd,OSStatus inError)25365 static void	_QuerierCmdStop( QuerierCmd *inCmd, OSStatus inError )
25366 {
25367 	if( !inCmd->done )
25368 	{
25369 		inCmd->done		= true;
25370 		inCmd->error	= inError;
25371 		dispatch_source_forget( &inCmd->sourceSigInt );
25372 		dispatch_source_forget( &inCmd->sourceSigTerm );
25373 		mdns_querier_forget( &inCmd->querier );
25374 		mdns_resolver_forget( &inCmd->resolver );
25375 		mdns_dns_service_manager_forget( &inCmd->manager );
25376 		mdns_forget( &inCmd->service );
25377 		FPrintF( stdout, "---\n" );
25378 		FPrintF( stdout, "End time:       %{du:time}\n", NULL );
25379 		dispatch_semaphore_signal( inCmd->doneSem );
25380 	}
25381 }
25382 
25383 //===========================================================================================================================
25384 
_QuerierCmdSigIntHandler(void * inCtx)25385 static void	_QuerierCmdSigIntHandler( void *inCtx )
25386 {
25387 	FPrintF( stdout, "*** Got SIGINIT signal ***\n" );
25388 	_QuerierCmdStop( (QuerierCmd *) inCtx, kCanceledErr );
25389 }
25390 
25391 //===========================================================================================================================
25392 
_QuerierCmdSigTermHandler(void * inCtx)25393 static void	_QuerierCmdSigTermHandler( void *inCtx )
25394 {
25395 	FPrintF( stdout, "*** Got SIGTERM signal ***\n" );
25396 	_QuerierCmdStop( (QuerierCmd *) inCtx, kCanceledErr );
25397 }
25398 
25399 //===========================================================================================================================
25400 //	DNSProxyCmd
25401 //===========================================================================================================================
25402 
25403 static void	_DNSProxyCallback( DNSXConnRef inConnection, DNSXErrorType inError );
25404 static void	_DNSProxyCmdSignalHandler( void *inContext );
25405 
DNSProxyCmd(void)25406 static void	DNSProxyCmd( void )
25407 {
25408 	OSStatus				err;
25409 	size_t					i;
25410 	DNSXConnRef				connection;
25411 	IfIndex					inputIfIndexes[ MaxInputIf ];
25412 	dispatch_source_t		sigIntSource	= NULL;
25413 	dispatch_source_t		sigTermSource	= NULL;
25414 	uint32_t				outputIfIndex;
25415 	char					ifName[ kInterfaceNameBufLen ];
25416 	uint8_t					dns64Prefix[ 16 ];
25417 	int						dns64PrefixBitLen;
25418 
25419 	if( gDNSProxy_InputInterfaceCount > MaxInputIf )
25420 	{
25421 		FPrintF( stderr, "error: Invalid input interface count: %zu > %d (max).\n",
25422 			gDNSProxy_InputInterfaceCount, MaxInputIf );
25423 		err = kRangeErr;
25424 		goto exit;
25425 	}
25426 
25427 	for( i = 0; i < gDNSProxy_InputInterfaceCount; ++i )
25428 	{
25429 		uint32_t		ifIndex;
25430 
25431 		err = InterfaceIndexFromArgString( gDNSProxy_InputInterfaces[ i ], &ifIndex );
25432 		require_noerr_quiet( err, exit );
25433 
25434 		inputIfIndexes[ i ] = ifIndex;
25435 	}
25436 	while( i < MaxInputIf ) inputIfIndexes[ i++ ] = 0;	// Remaining interface indexes are required to be 0.
25437 
25438 	if( gDNSProxy_OutputInterface )
25439 	{
25440 		err = InterfaceIndexFromArgString( gDNSProxy_OutputInterface, &outputIfIndex );
25441 		require_noerr_quiet( err, exit );
25442 	}
25443 	else
25444 	{
25445 		outputIfIndex = kDNSIfindexAny;
25446 	}
25447 
25448 	dns64PrefixBitLen = 0;
25449 	if( gDNSProxy_DNS64IPv6Prefix )
25450 	{
25451 		const char *		end;
25452 
25453 		err = _StringToIPv6Address( gDNSProxy_DNS64IPv6Prefix,
25454 			kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoScope, dns64Prefix, NULL, NULL, &dns64PrefixBitLen,
25455 			&end );
25456 		if( !err && ( *end != '\0' ) ) err = kMalformedErr;
25457 		require_noerr_quiet( err, exit );
25458 	}
25459 	FPrintF( stdout, "Input Interfaces:" );
25460 	for( i = 0; i < gDNSProxy_InputInterfaceCount; ++i )
25461 	{
25462 		const uint32_t		ifIndex = (uint32_t) inputIfIndexes[ i ];
25463 
25464 		FPrintF( stdout, "%s %u (%s)", ( i == 0 ) ? "" : ",", ifIndex, InterfaceIndexToName( ifIndex, ifName ) );
25465 	}
25466 	FPrintF( stdout, "\n" );
25467 	FPrintF( stdout, "Output Interface: %u (%s)\n",		outputIfIndex, InterfaceIndexToName( outputIfIndex, ifName ) );
25468 	if( gDNSProxy_DNS64IPv6Prefix ) FPrintF( stdout, "DNS64 prefix:     %.16a/%d\n", dns64Prefix, dns64PrefixBitLen );
25469 	FPrintF( stdout, "Start time:       %{du:time}\n",	NULL );
25470 	FPrintF( stdout, "---\n" );
25471 
25472 	connection = NULL;
25473 	if( gDNSProxy_DNS64IPv6Prefix )
25474 	{
25475 		if( __builtin_available( macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, * ) )
25476 		{
25477 			err = DNSXEnableProxy64( &connection, kDNSProxyEnable, inputIfIndexes, outputIfIndex,
25478                 dns64Prefix, dns64PrefixBitLen, kDNSXProxyFlagNull, dispatch_get_main_queue(), _DNSProxyCallback );
25479 			require_noerr_quiet( err, exit );
25480 		}
25481 		else
25482 		{
25483 			FPrintF( stderr, "error: DNSXEnableProxy64() is not available on this OS.\n" );
25484 			err = kUnsupportedErr;
25485 			goto exit;
25486 		}
25487 	}
25488 	else
25489 	{
25490 		err = DNSXEnableProxy( &connection, kDNSProxyEnable, inputIfIndexes, outputIfIndex,
25491 			dispatch_get_main_queue(), _DNSProxyCallback );
25492 		require_noerr_quiet( err, exit );
25493 	}
25494 	signal( SIGINT, SIG_IGN );
25495 	err = DispatchSignalSourceCreate( SIGINT, dispatch_get_main_queue(), _DNSProxyCmdSignalHandler, connection,
25496 		&sigIntSource );
25497 	require_noerr( err, exit );
25498 	dispatch_activate( sigIntSource );
25499 
25500 	signal( SIGTERM, SIG_IGN );
25501 	err = DispatchSignalSourceCreate( SIGTERM, dispatch_get_main_queue(), _DNSProxyCmdSignalHandler, connection,
25502 		&sigTermSource );
25503 	require_noerr( err, exit );
25504 	dispatch_activate( sigTermSource );
25505 
25506 	dispatch_main();
25507 
25508 exit:
25509 	if( err ) ErrQuit( 1, "error: %#m\n", err );
25510 }
25511 
_DNSProxyCallback(DNSXConnRef inConnection,DNSXErrorType inError)25512 static void	_DNSProxyCallback( DNSXConnRef inConnection, DNSXErrorType inError )
25513 {
25514 	Unused( inConnection );
25515 
25516 	if( inError ) ErrQuit( 1, "error: DNS proxy failed: %#m\n", inError );
25517 }
25518 
_DNSProxyCmdSignalHandler(void * inContext)25519 static void	_DNSProxyCmdSignalHandler( void *inContext )
25520 {
25521 	DNSXConnRef const		connection = (DNSXConnRef) inContext;
25522 	struct timeval			now;
25523 
25524 	gettimeofday( &now, NULL );
25525 
25526 	DNSXRefDeAlloc( connection );
25527 
25528 	FPrintF( stdout, "---\n" );
25529 	FPrintF( stdout, "End time:         %{du:time}\n", &now );
25530 	exit( 0 );
25531 }
25532 
25533 //===========================================================================================================================
25534 //	GetAddrInfoNewCommand
25535 //===========================================================================================================================
25536 
25537 
25538 typedef enum
25539 {
25540 	kDelegationType_None		= 0,	// No delegation.
25541 	kDelegationType_PID			= 1,	// Delegation by PID.
25542 	kDelegationType_UUID		= 2,	// Delegation by UUID.
25543 	kDelegationType_AuditToken	= 3		// Delegation by audit token.
25544 
25545 }	DelegationType;
25546 
25547 typedef struct
25548 {
25549 	DelegationType			type;		// Type of delegation.
25550 	union
25551 	{
25552 		pid_t				pid;		// Delegator's PID if type is kDelegationType_PID.
25553 		uuid_t				uuid;		// Delegator's UUID if type is kDelegationType_UUID.
25554 		audit_token_t		auditToken;	// Delegator's audit token if type is kDelegationType_AuditToken.
25555 
25556 	}	ident;							// Delegator's identifier.
25557 
25558 }	Delegation;
25559 
25560 typedef struct
25561 {
25562 	dispatch_queue_t		queue;			// Serial queue for command's events.
25563 	dispatch_group_t		group;			// GCD group to know when command is done.
25564 	dnssd_getaddrinfo_t		gai;			// dnssd_getaddrinfo object.
25565 	dispatch_source_t		timer;			// Timer to impose time limit on dnssd_getaddrinfo activity.
25566 	dispatch_source_t		sigint;			// Dispatch source for SIGINT.
25567 	dispatch_source_t		sigterm;		// Dispatch source for SIGTERM.
25568 	const char *			hostname;		// dnssd_getaddrinfo's hostname argument.
25569 	const char *			serviceScheme;	// dnssd_getaddrinfo's service scheme argument.
25570 	const char *			accountID;		// dnssd_getaddrinfo's account ID argument.
25571 	char *					stopReason;		// Reason for stopping the command.
25572 	Delegation				delegation;		// Specifies the type of delegation to use for dnssd_getaddrinfo, if any.
25573 	int32_t					refCount;		// Reference count.
25574 	DNSServiceFlags			flags;			// dnssd_getaddrinfo's flags argument.
25575 	DNSServiceProtocol		protocols;		// dnssd_getaddrinfo's protocols argument.
25576 	uint32_t				ifIndex;		// dnssd_getaddrinfo's interface index argument.
25577 	unsigned int			timeLimitSecs;	// Time limit in seconds for dnssd_getaddrinfo activity.
25578 	OSStatus				error;			// Command's error.
25579 	Boolean					needAuthTags;	// True if dnssd_getaddrinfo
25580 	Boolean					stopped;		// True if the command has been stopped.
25581 	Boolean					oneshot;		// True if the command should stop after first set of results.
25582 	Boolean					printedHeader;	// True if the results header has been printed.
25583 
25584 }	GetAddrInfoNewCmd;
25585 
25586 static GetAddrInfoNewCmd *	_GetAddrInfoNewCmdCreateEx( qos_class_t inQoS, Boolean inUseQoS, OSStatus *outError );
25587 #define _GetAddrInfoNewCmdCreate( OUT_ERROR )						_GetAddrInfoNewCmdCreateEx( 0, false, OUT_ERROR )
25588 #define _GetAddrInfoNewCmdCreateWithQoS( IN_QOS, OUT_ERROR )		_GetAddrInfoNewCmdCreateEx( IN_QOS, true, OUT_ERROR )
25589 static OSStatus				_GetAddrInfoNewCmdRun( GetAddrInfoNewCmd *inCmd );
25590 static void					_GetAddrInfoNewCmdStopF( GetAddrInfoNewCmd *inCmd, const char *inFmt, ... );
25591 static GetAddrInfoNewCmd *	_GetAddrInfoNewCmdRetain( GetAddrInfoNewCmd *inCmd );
25592 static void					_GetAddrInfoNewCmdRelease( GetAddrInfoNewCmd *inCmd );
25593 
25594 #define _GetAddrInfoNewCmdForget( X )		ForgetCustom( X, _GetAddrInfoNewCmdRelease )
25595 
GetAddrInfoNewCommand(void)25596 static void	GetAddrInfoNewCommand( void )
25597 {
25598 	OSStatus				err;
25599 	GetAddrInfoNewCmd *		cmd = NULL;
25600 
25601 	if( gGAINew_QoS )
25602 	{
25603 		qos_class_t		qos;
25604 
25605 		qos = (qos_class_t) CLIArgToValue( kQoSArgShortName, gGAINew_QoS, &err,
25606 			kQoSTypeStr_Unspecified,		QOS_CLASS_UNSPECIFIED,
25607 			kQoSTypeStr_Background,			QOS_CLASS_BACKGROUND,
25608 			kQoSTypeStr_Utility,			QOS_CLASS_UTILITY,
25609 			kQoSTypeStr_Default,			QOS_CLASS_DEFAULT,
25610 			kQoSTypeStr_UserInitiated,		QOS_CLASS_USER_INITIATED,
25611 			kQoSTypeStr_UserInteractive,	QOS_CLASS_USER_INTERACTIVE,
25612 			NULL );
25613 		require_noerr_quiet( err, exit );
25614 
25615 		cmd = _GetAddrInfoNewCmdCreateWithQoS( qos, &err );
25616 		require_noerr( err, exit );
25617 	}
25618 	else
25619 	{
25620 		cmd = _GetAddrInfoNewCmdCreate( &err );
25621 		require_noerr( err, exit );
25622 	}
25623 	err = CheckIntegerArgument( gGAINew_TimeLimitSecs, "time limit", 0, INT_MAX );
25624 	require_noerr_quiet( err, exit );
25625 
25626 	cmd->timeLimitSecs = (unsigned int) gGAINew_TimeLimitSecs;
25627 
25628 	cmd->hostname		= gGAINew_Hostname;
25629 	cmd->flags			= GetDNSSDFlagsFromOpts();
25630 	cmd->serviceScheme	= gGAINew_ServiceScheme;
25631 	cmd->accountID		= gGAINew_AccountID;
25632 	cmd->needAuthTags	= gGAINew_WantAuthTags	? true : false;
25633 	cmd->oneshot		= gGAINew_OneShot		? true : false;
25634 
25635 	err = InterfaceIndexFromArgString( gInterface, &cmd->ifIndex );
25636 	require_noerr_quiet( err, exit );
25637 
25638 	cmd->protocols = 0;
25639 	if( gGAINew_ProtocolIPv4 ) cmd->protocols |= kDNSServiceProtocol_IPv4;
25640 	if( gGAINew_ProtocolIPv6 ) cmd->protocols |= kDNSServiceProtocol_IPv6;
25641 
25642 	// Get delegate ID.
25643 
25644 	if( gGAINew_DelegatorID )
25645 	{
25646 		pid_t		delegatorPID;
25647 
25648 		delegatorPID = _StringToPID( gGAINew_DelegatorID, &err );
25649 		if( !err )
25650 		{
25651 			if( delegatorPID >= 0 )
25652 			{
25653 				cmd->delegation.ident.pid	= delegatorPID;
25654 				cmd->delegation.type		= kDelegationType_PID;
25655 			}
25656 			else
25657 			{
25658 				delegatorPID = -delegatorPID;
25659 				if( audit_token_for_pid( delegatorPID, &cmd->delegation.ident.auditToken ) )
25660 				{
25661 					cmd->delegation.type = kDelegationType_AuditToken;
25662 				}
25663 				else
25664 				{
25665 					FPrintF( stderr, "Failed to get audit token for PID: %d\n", delegatorPID );
25666 					err = kParamErr;
25667 					goto exit;
25668 				}
25669 			}
25670 		}
25671 		else
25672 		{
25673 			err = uuid_parse( gGAINew_DelegatorID, cmd->delegation.ident.uuid );
25674 			if( !err )
25675 			{
25676 				cmd->delegation.type = kDelegationType_UUID;
25677 			}
25678 			else
25679 			{
25680 				FPrintF( stderr, "Invalid delegate ID (PID or UUID): %s\n", gGAINew_DelegatorID );
25681 				err = kParamErr;
25682 				goto exit;
25683 			}
25684 		}
25685 	}
25686 	err = _GetAddrInfoNewCmdRun( cmd );
25687 	require_noerr( err, exit );
25688 
25689 exit:
25690 	_GetAddrInfoNewCmdForget( &cmd );
25691 	gExitCode = err ? 1 : 0;
25692 }
25693 
25694 //===========================================================================================================================
25695 
_GetAddrInfoNewCmdCreateEx(qos_class_t inQoS,Boolean inUseQoS,OSStatus * outError)25696 static GetAddrInfoNewCmd *	_GetAddrInfoNewCmdCreateEx( qos_class_t inQoS, Boolean inUseQoS, OSStatus *outError )
25697 {
25698 	OSStatus				err;
25699 	GetAddrInfoNewCmd *		obj;
25700 	GetAddrInfoNewCmd *		cmd = NULL;
25701 
25702 	obj = (GetAddrInfoNewCmd *) calloc( 1, sizeof( *obj ) );
25703 	require_action( obj, exit, err = kNoMemoryErr );
25704 
25705 	obj->refCount = 1;
25706 	if( inUseQoS )
25707 	{
25708 		dispatch_queue_attr_t		attr;
25709 
25710 		attr = dispatch_queue_attr_make_with_qos_class( DISPATCH_QUEUE_SERIAL, inQoS, 0 );
25711 		obj->queue = dispatch_queue_create( "com.apple.dnssdutil.getaddrinfo-new-command", attr );
25712 		require_action( obj->queue, exit, err = kNoResourcesErr );
25713 	}
25714 	else
25715 	{
25716 		obj->queue = dispatch_queue_create( "com.apple.dnssdutil.getaddrinfo-new-command", DISPATCH_QUEUE_SERIAL );
25717 		require_action( obj->queue, exit, err = kNoResourcesErr );
25718 	}
25719 	obj->group = dispatch_group_create();
25720 	require_action( obj->group, exit, err = kNoResourcesErr );
25721 
25722 	cmd = obj;
25723 	obj = NULL;
25724 	err = kNoErr;
25725 
25726 exit:
25727 	if( outError ) *outError = err;
25728 	_GetAddrInfoNewCmdForget( &obj );
25729 	return( cmd );
25730 }
25731 
25732 //===========================================================================================================================
25733 
25734 static void	_GetAddrInfoNewCmdStart( void *inCtx );
25735 
_GetAddrInfoNewCmdRun(GetAddrInfoNewCmd * me)25736 static OSStatus	_GetAddrInfoNewCmdRun( GetAddrInfoNewCmd *me )
25737 {
25738 	dispatch_group_enter( me->group );
25739 	dispatch_async_f( me->queue, me, _GetAddrInfoNewCmdStart );
25740     dispatch_group_wait( me->group, DISPATCH_TIME_FOREVER );
25741 	FPrintF( stdout, "---\n" );
25742 	FPrintF( stdout, "End time:   %{du:time}\n", NULL );
25743 	if( me->stopReason ) FPrintF( stdout, "End reason: %s\n", me->stopReason );
25744 	return( me->error );
25745 }
25746 
_GetAddrInfoNewCmdStart(void * inCtx)25747 static void	_GetAddrInfoNewCmdStart( void *inCtx )
25748 {
25749 	OSStatus						err;
25750 	GetAddrInfoNewCmd * const		me = (GetAddrInfoNewCmd *) inCtx;
25751 	char							ifName[ kInterfaceNameBufLen ];
25752 
25753 	me->gai = dnssd_getaddrinfo_create();
25754 	require_action( me->gai, exit, err = kNoResourcesErr );
25755 
25756 	dnssd_getaddrinfo_set_hostname( me->gai, me->hostname );
25757 	dnssd_getaddrinfo_set_flags( me->gai, me->flags );
25758 	dnssd_getaddrinfo_set_interface_index( me->gai, me->ifIndex );
25759 	dnssd_getaddrinfo_set_protocols( me->gai, me->protocols );
25760 	dnssd_getaddrinfo_set_need_authenticated_results( me->gai, me->needAuthTags ? true : false );
25761 	switch( me->delegation.type )
25762 	{
25763 		case kDelegationType_PID:
25764 			dnssd_getaddrinfo_set_delegate_pid( me->gai, me->delegation.ident.pid );
25765 			break;
25766 
25767 		case kDelegationType_UUID:
25768 			dnssd_getaddrinfo_set_delegate_uuid( me->gai, me->delegation.ident.uuid );
25769 			break;
25770 
25771 		case kDelegationType_AuditToken:
25772 			if( __builtin_available( macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, * ) )
25773 			{
25774 				dnssd_getaddrinfo_set_delegate_audit_token( me->gai, me->delegation.ident.auditToken );
25775 			}
25776 			else
25777 			{
25778 				FPrintF( stderr, "error: dnssd_getaddrinfo_set_delegate_audit_token() is not available on this OS.\n" );
25779 				err = kUnsupportedErr;
25780 				goto exit;
25781 			}
25782 			break;
25783 
25784 		case kDelegationType_None:
25785 		default:
25786 			break;
25787 	}
25788 	if( me->serviceScheme )
25789 	{
25790 		if( __builtin_available( macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, * ) )
25791 		{
25792 			dnssd_getaddrinfo_set_service_scheme( me->gai, me->serviceScheme );
25793 		}
25794 		else
25795 		{
25796 			FPrintF( stderr, "error: dnssd_getaddrinfo_set_service_scheme() is not available on this OS.\n" );
25797 			err = kUnsupportedErr;
25798 			goto exit;
25799 		}
25800 	}
25801 	if( me->accountID )
25802 	{
25803 		if( __builtin_available( macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, * ) )
25804 		{
25805 			dnssd_getaddrinfo_set_account_id( me->gai, me->accountID );
25806 		}
25807 		else
25808 		{
25809 			FPrintF( stderr, "error: dnssd_getaddrinfo_set_account_id() is not available on this OS.\n" );
25810 			err = kUnsupportedErr;
25811 			goto exit;
25812 		}
25813 	}
25814 	dnssd_getaddrinfo_set_queue( me->gai, me->queue );
25815 	dnssd_getaddrinfo_set_result_handler( me->gai,
25816 	^( dnssd_getaddrinfo_result_t *inResultArray, size_t inResultCount )
25817 	{
25818 		if( !me->gai ) return;
25819 		if( inResultCount > 0 )
25820 		{
25821 			size_t		i;
25822 
25823 			for( i = 0; i < inResultCount; ++i )
25824 			{
25825 				const dnssd_getaddrinfo_result_t			result	= inResultArray[ i ];
25826 				const dnssd_getaddrinfo_result_type_t		type	= dnssd_getaddrinfo_result_get_type( result );
25827 				const char *								typeStr;
25828 				const char *								cacheStr;
25829 
25830 				if( !me->printedHeader )
25831 				{
25832 					FPrintF( stdout, "%-26s  Type C? IF %-30s Address\n", "Timestamp", "Hostname" );
25833 					me->printedHeader = true;
25834 				}
25835 				switch( type )
25836 				{
25837 					case dnssd_getaddrinfo_result_type_add:				typeStr = "Add"; break;
25838 					case dnssd_getaddrinfo_result_type_remove:			typeStr = "Rmv"; break;
25839 					case dnssd_getaddrinfo_result_type_no_address:		typeStr = "NoA"; break;
25840 					case dnssd_getaddrinfo_result_type_expired:			typeStr = "Exp"; break;
25841 					case dnssd_getaddrinfo_result_type_service_binding:	typeStr = "SvB"; break;
25842 					default:											typeStr = "???"; break;
25843 				}
25844 				cacheStr = "-";
25845 				if( __builtin_available( macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, * ) )
25846 				{
25847 					if( type != dnssd_getaddrinfo_result_type_remove )
25848 					{
25849 						cacheStr = dnssd_getaddrinfo_result_is_from_cache( result ) ? "Y" : "N";
25850 					}
25851 				}
25852 				FPrintF( stdout, "%{du:time}  %-4s %-2s %2d %-30s %##a\n",
25853 					NULL, typeStr, cacheStr, dnssd_getaddrinfo_result_get_interface_index( result ),
25854 					dnssd_getaddrinfo_result_get_hostname( result ), dnssd_getaddrinfo_result_get_address( result ) );
25855 				if( __builtin_available( macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, * ) )
25856 				{
25857 					if( me->flags & kDNSServiceFlagsReturnIntermediates )
25858 					{
25859 						const dnssd_cname_array_t		cnames = dnssd_getaddrinfo_result_get_cnames( result );
25860 						size_t							j, n;
25861 
25862 						FPrintF( stdout, "    Canonical Names: [" );
25863 						n = dnssd_cname_array_get_count( cnames );
25864 						for( j = 0; j < n; ++j )
25865 						{
25866 							FPrintF( stdout, "%s%s", ( j == 0 ) ? "" : ", ", dnssd_cname_array_get_cname( cnames, j ) );
25867 						}
25868 						FPrintF( stdout, "]\n" );
25869 					}
25870 				}
25871 				if( me->needAuthTags )
25872 				{
25873 					if( ( type == dnssd_getaddrinfo_result_type_add ) || ( type == dnssd_getaddrinfo_result_type_expired ) )
25874 					{
25875 						const void *		tagPtr;
25876 						size_t				tagLen;
25877 
25878 						tagPtr = dnssd_getaddrinfo_result_get_authentication_tag( result, &tagLen );
25879 						FPrintF( stdout, "    Auth Tag: " );
25880 						if( tagPtr )	FPrintF( stdout, "%.4H (%zu bytes)\n", tagPtr, (int) tagLen, (int) tagLen, tagLen );
25881 						else			FPrintF( stdout, "<NO AUTH TAG!>\n" );
25882 					}
25883 				}
25884 			}
25885 			if( me->oneshot ) _GetAddrInfoNewCmdStopF( me, "one-shot done" );
25886 		}
25887 	} );
25888 	_GetAddrInfoNewCmdRetain( me );
25889 	dispatch_group_enter( me->group );
25890 	dnssd_getaddrinfo_set_event_handler( me->gai,
25891 	^( dnssd_event_t inEvent, DNSServiceErrorType inError )
25892 	{
25893 		switch( inEvent )
25894 		{
25895 			case dnssd_event_invalidated:
25896 				dispatch_group_leave( me->group );
25897 				_GetAddrInfoNewCmdRelease( me );
25898 				break;
25899 
25900 			case dnssd_event_error:
25901 				if( !me->error ) me->error = inError;
25902 				_GetAddrInfoNewCmdStopF( me, "error %#m", inError );
25903 				break;
25904 
25905 			default:
25906 				break;
25907 		}
25908 	} );
25909 
25910 	// Set up signal handlers.
25911 
25912 	signal( SIGINT, SIG_IGN );
25913 	me->sigint = dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) SIGINT, 0, me->queue );
25914 	require_action( me->sigint, exit, err = kNoResourcesErr );
25915 
25916 	dispatch_source_set_event_handler( me->sigint, ^{ _GetAddrInfoNewCmdStopF( me, "interrupt signal" ); } );
25917 	dispatch_activate( me->sigint );
25918 
25919 	signal( SIGTERM, SIG_IGN );
25920 	me->sigterm = dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) SIGTERM, 0, me->queue );
25921 	require_action( me->sigterm, exit, err = kNoResourcesErr );
25922 
25923 	dispatch_source_set_event_handler( me->sigterm, ^{ _GetAddrInfoNewCmdStopF( me, "termination signal" ); } );
25924 	dispatch_activate( me->sigterm );
25925 
25926 	// Start getaddrinfo operation.
25927 
25928 	InterfaceIndexToName( me->ifIndex, ifName );
25929 	FPrintF( stdout, "Service Scheme: %s\n", me->serviceScheme );
25930 	FPrintF( stdout, "Flags:          %#{flags}\n",	me->flags, kDNSServiceFlagsDescriptors );
25931 	FPrintF( stdout, "Interface:      %d (%s)\n",	(int32_t) me->ifIndex, ifName );
25932 	FPrintF( stdout, "Protocols:      %#{flags}\n",	me->protocols, kDNSServiceProtocolDescriptors );
25933 	FPrintF( stdout, "Name:           %s\n",		me->hostname );
25934 	FPrintF( stdout, "Mode:           %s\n",		me->oneshot ? "one-shot" : "continuous" );
25935 	if( me->serviceScheme )	FPrintF( stdout, "Service Scheme: %s\n", me->serviceScheme );
25936 	if( me->accountID )		FPrintF( stdout, "Account ID:     %s\n", me->accountID );
25937 	FPrintF( stdout, "Time limit:     " );
25938 	if( me->timeLimitSecs > 0 )	FPrintF( stdout, "%d second%?c\n", me->timeLimitSecs, me->timeLimitSecs != 1, 's' );
25939 	else						FPrintF( stdout, "∞\n" );
25940 	FPrintF( stdout, "Start time: %{du:time}\n",	NULL );
25941 	FPrintF( stdout, "---\n" );
25942 
25943 	if( me->timeLimitSecs > 0 )
25944 	{
25945 		me->timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, me->queue );
25946 		require_action( me->timer, exit, err = kNoResourcesErr );
25947 
25948 		dispatch_source_set_timer( me->timer, dispatch_time_seconds( me->timeLimitSecs ), DISPATCH_TIME_FOREVER,
25949 			me->timeLimitSecs * ( UINT64_C_safe( kNanosecondsPerSecond ) / 20 ) );
25950 		dispatch_source_set_event_handler( me->timer, ^{ _GetAddrInfoNewCmdStopF( me, "time limit" ); } );
25951 		dispatch_activate( me->timer );
25952 	}
25953 	dnssd_getaddrinfo_activate( me->gai );
25954 	err = kNoErr;
25955 
25956 exit:
25957 	if( err ) _GetAddrInfoNewCmdStopF( me, "error %#m", err );
25958 }
25959 
25960 //===========================================================================================================================
25961 
_GetAddrInfoNewCmdStopF(GetAddrInfoNewCmd * me,const char * inFmt,...)25962 static void	_GetAddrInfoNewCmdStopF( GetAddrInfoNewCmd *me, const char *inFmt, ... )
25963 {
25964 	if( !me->stopped )
25965 	{
25966 		me->stopped = true;
25967 		dnssd_getaddrinfo_forget( &me->gai );
25968 		dispatch_source_forget( &me->timer );
25969 		dispatch_source_forget( &me->sigint );
25970 		dispatch_source_forget( &me->sigterm );
25971 		if( inFmt )
25972 		{
25973 			va_list		args;
25974 
25975 			check( !me->stopReason );
25976 			va_start( args, inFmt );
25977 			VASPrintF( &me->stopReason, inFmt, args );
25978 			va_end( args );
25979 			check( me->stopReason );
25980 		}
25981 		dispatch_group_leave( me->group );
25982 	}
25983 }
25984 
25985 //===========================================================================================================================
25986 
_GetAddrInfoNewCmdRetain(GetAddrInfoNewCmd * me)25987 static GetAddrInfoNewCmd *	_GetAddrInfoNewCmdRetain( GetAddrInfoNewCmd *me )
25988 {
25989 	atomic_add_32( &me->refCount, 1 );
25990 	return( me );
25991 }
25992 
25993 //===========================================================================================================================
25994 
_GetAddrInfoNewCmdRelease(GetAddrInfoNewCmd * me)25995 static void	_GetAddrInfoNewCmdRelease( GetAddrInfoNewCmd *me )
25996 {
25997 	if( atomic_add_and_fetch_32( &me->refCount, -1 ) == 0 )
25998 	{
25999 		check( !me->gai );
26000 		check( !me->timer );
26001 		check( !me->sigint );
26002 		check( !me->sigterm );
26003 		dispatch_forget( &me->queue );
26004 		dispatch_forget( &me->group );
26005 		ForgetMem( &me->stopReason );
26006 		free( me );
26007 	}
26008 }
26009 
26010 //===========================================================================================================================
26011 //    XCTestCmd
26012 //===========================================================================================================================
26013 
XCTestCmd(void)26014 static void    XCTestCmd( void )
26015 {
26016     int result = 0;
26017     setenv(DNSSDUTIL_XCTEST, DNSSDUTIL_XCTEST, 0);
26018     if(!run_xctest_named(gXCTest_Classname)) {
26019         result = 1;
26020     }
26021     unsetenv(DNSSDUTIL_XCTEST);
26022     exit( result );
26023 }
26024 
26025 //===========================================================================================================================
26026 //    MultiConnectTestCmd
26027 //===========================================================================================================================
26028 
MultiConnectTestCmd(void)26029 static void    MultiConnectTestCmd( void )
26030 {
26031     int result = 0;
26032 	dispatch_group_t group = dispatch_group_create();
26033 	int count = gMultiConnectTest_ConnectionCount;
26034 	DNSServiceRef *	refs = calloc( (uint32_t)count, sizeof(DNSServiceRef) );
26035 
26036 	// Create count connections on async queue
26037 	// rdar://problem/59861422
26038 	__block int goodcount = 0;
26039 	for ( int i = 0; i < count; i++ ) {
26040 		char label[256];
26041 		sprintf( label, "multi-connect queue %d", i+1 );
26042 		dispatch_group_async( group, dispatch_queue_create( label, DISPATCH_QUEUE_SERIAL ), ^{
26043 			DNSServiceErrorType err = DNSServiceCreateConnection( &refs[i] );
26044 			if ( err ) {
26045 				fprintf( stderr, "%s Failed to create connection # %d err(%d)\n", __FUNCTION__, i, err );
26046             } else {
26047                 goodcount++;
26048             }
26049 		});
26050 	}
26051 	dispatch_group_wait( group, DISPATCH_TIME_FOREVER );
26052 	dispatch_release( group );
26053 	fprintf( stderr, "%s Created %d connections of %d\n", __FUNCTION__, goodcount, count );
26054 
26055 	// Free them
26056 	goodcount = 0;
26057 	for ( int i = 0; i < count; i++ ) {
26058 		if ( refs[i] ) {
26059 			DNSServiceRefDeallocate( refs[i] );
26060 			goodcount++;
26061 		}
26062 	}
26063 	fprintf( stderr, "%s Stopped %d connections of %d\n", __FUNCTION__, goodcount, count );
26064 	result = (goodcount == count) ? 0 : 1;
26065     exit( result );
26066 }
26067 
26068 #endif	// MDNSRESPONDER_PROJECT
26069 
26070 //===========================================================================================================================
26071 //	DaemonVersionCmd
26072 //===========================================================================================================================
26073 
DaemonVersionCmd(void)26074 static void	DaemonVersionCmd( void )
26075 {
26076 	OSStatus		err;
26077 	uint32_t		size, version;
26078 	char			strBuf[ 16 ];
26079 
26080 	size = (uint32_t) sizeof( version );
26081 	err = DNSServiceGetProperty( kDNSServiceProperty_DaemonVersion, &version, &size );
26082 	require_noerr( err, exit );
26083 
26084 	FPrintF( stdout, "Daemon version: %s\n", SourceVersionToCString( version, strBuf ) );
26085 
26086 exit:
26087 	if( err ) exit( 1 );
26088 }
26089 
26090 //===========================================================================================================================
26091 //	Exit
26092 //===========================================================================================================================
26093 
Exit(void * inContext)26094 static void	Exit( void *inContext )
26095 {
26096 	const char * const		reason = (const char *) inContext;
26097 
26098 	FPrintF( stdout, "---\n" );
26099 	FPrintF( stdout, "End time:   %{du:time}\n", NULL );
26100 	if( reason ) FPrintF( stdout, "End reason: %s\n", reason );
26101 	exit( gExitCode );
26102 }
26103 
26104 //===========================================================================================================================
26105 //	_PrintFExtensionHandler_Timestamp
26106 //===========================================================================================================================
26107 
26108 static int
_PrintFExtensionHandler_Timestamp(PrintFContext * inContext,PrintFFormat * inFormat,PrintFVAList * inArgs,void * inUserContext)26109 	_PrintFExtensionHandler_Timestamp(
26110 		PrintFContext *	inContext,
26111 		PrintFFormat *	inFormat,
26112 		PrintFVAList *	inArgs,
26113 		void *			inUserContext )
26114 {
26115 	struct timeval				now;
26116 	const struct timeval *		tv;
26117 	struct tm *					localTime;
26118 	size_t						len;
26119 	int							n;
26120 	char						dateTimeStr[ 32 ];
26121 
26122 	Unused( inUserContext );
26123 
26124 	tv = va_arg( inArgs->args, const struct timeval * );
26125 	require_action_quiet( !inFormat->suppress, exit, n = 0 );
26126 
26127 	if( !tv )
26128 	{
26129 		gettimeofday( &now, NULL );
26130 		tv = &now;
26131 	}
26132 	localTime = localtime( &tv->tv_sec );
26133 	len = strftime( dateTimeStr, sizeof( dateTimeStr ), "%Y-%m-%d %H:%M:%S", localTime );
26134 	if( len == 0 ) dateTimeStr[ 0 ] = '\0';
26135 
26136 	n = PrintFCore( inContext, "%s.%06u", dateTimeStr, (unsigned int) tv->tv_usec );
26137 
26138 exit:
26139 	return( n );
26140 }
26141 
26142 //===========================================================================================================================
26143 //	_PrintFExtensionHandler_DNSMessage
26144 //===========================================================================================================================
26145 
26146 static int
26147 	_PrintFExtensionHandler_DNSMessageCommon(
26148 		PrintFContext *	inContext,
26149 		PrintFFormat *	inFormat,
26150 		PrintFVAList *	inArgs,
26151 		void *			inUserContext,
26152 		Boolean			inRawRData );
26153 
26154 static int
_PrintFExtensionHandler_DNSMessage(PrintFContext * inContext,PrintFFormat * inFormat,PrintFVAList * inArgs,void * inUserContext)26155 	_PrintFExtensionHandler_DNSMessage(
26156 		PrintFContext *	inContext,
26157 		PrintFFormat *	inFormat,
26158 		PrintFVAList *	inArgs,
26159 		void *			inUserContext )
26160 {
26161 	return( _PrintFExtensionHandler_DNSMessageCommon( inContext, inFormat, inArgs, inUserContext, false ) );
26162 }
26163 
26164 static int
_PrintFExtensionHandler_DNSMessageCommon(PrintFContext * inContext,PrintFFormat * inFormat,PrintFVAList * inArgs,void * inUserContext,Boolean inRawRData)26165 	_PrintFExtensionHandler_DNSMessageCommon(
26166 		PrintFContext *	inContext,
26167 		PrintFFormat *	inFormat,
26168 		PrintFVAList *	inArgs,
26169 		void *			inUserContext,
26170 		Boolean			inRawRData )
26171 {
26172 	OSStatus					err;
26173 	const void *				msgPtr;
26174 	size_t						msgLen;
26175 	char *						msgStr;
26176 	DNSMessageToStringFlags		flags;
26177 	int							n;
26178 
26179 	Unused( inUserContext );
26180 
26181 	msgPtr = va_arg( inArgs->args, const void * );
26182 	msgLen = va_arg( inArgs->args, size_t );
26183 	require_action_quiet( !inFormat->suppress, exit, n = 0 );
26184 
26185 	flags = kDNSMessageToStringFlags_None;
26186 	if( inRawRData )				flags |= kDNSMessageToStringFlag_RawRData;
26187 	if( inFormat->altForm   == 1 )	flags |= kDNSMessageToStringFlag_MDNS;
26188 	if( inFormat->precision == 1 )	flags |= kDNSMessageToStringFlag_OneLine;
26189 	err = DNSMessageToString( msgPtr, msgLen, flags, &msgStr );
26190 	if( !err )
26191 	{
26192 		n = PrintFCore( inContext, "%*{text}", inFormat->fieldWidth, msgStr, kSizeCString );
26193 		free( msgStr );
26194 	}
26195 	else
26196 	{
26197 		n = PrintFCore( inContext, "%*.1H", inFormat->fieldWidth, msgPtr, (int) msgLen, (int) msgLen );
26198 	}
26199 
26200 exit:
26201 	return( n );
26202 }
26203 
26204 //===========================================================================================================================
26205 //	_PrintFExtensionHandler_RawDNSMessage
26206 //===========================================================================================================================
26207 
26208 static int
_PrintFExtensionHandler_RawDNSMessage(PrintFContext * inContext,PrintFFormat * inFormat,PrintFVAList * inArgs,void * inUserContext)26209 	_PrintFExtensionHandler_RawDNSMessage(
26210 		PrintFContext *	inContext,
26211 		PrintFFormat *	inFormat,
26212 		PrintFVAList *	inArgs,
26213 		void *			inUserContext )
26214 {
26215 	return( _PrintFExtensionHandler_DNSMessageCommon( inContext, inFormat, inArgs, inUserContext, true ) );
26216 }
26217 
26218 //===========================================================================================================================
26219 //	_PrintFExtensionHandler_CallbackFlags
26220 //===========================================================================================================================
26221 
26222 static int
_PrintFExtensionHandler_CallbackFlags(PrintFContext * inContext,PrintFFormat * inFormat,PrintFVAList * inArgs,void * inUserContext)26223 	_PrintFExtensionHandler_CallbackFlags(
26224 		PrintFContext *	inContext,
26225 		PrintFFormat *	inFormat,
26226 		PrintFVAList *	inArgs,
26227 		void *			inUserContext )
26228 {
26229 	DNSServiceFlags		flags;
26230 	int					n;
26231 
26232 	Unused( inUserContext );
26233 
26234 	flags = va_arg( inArgs->args, DNSServiceFlags );
26235 	require_action_quiet( !inFormat->suppress, exit, n = 0 );
26236 
26237 	n = PrintFCore( inContext, "%08X %s%c %c%c",
26238 		flags, DNSServiceFlagsToAddRmvStr( flags ),
26239 		( flags & kDNSServiceFlagsMoreComing )       ? '+' : ' ',
26240 		( flags & kDNSServiceFlagAnsweredFromCache ) ? 'C' : ' ',
26241 		( flags & kDNSServiceFlagsExpiredAnswer )    ? '*' : ' ' );
26242 
26243 exit:
26244 	return( n );
26245 }
26246 
26247 //===========================================================================================================================
26248 //	_PrintFExtensionHandler_DNSRecordData
26249 //===========================================================================================================================
26250 
26251 static int
_PrintFExtensionHandler_DNSRecordData(PrintFContext * inContext,PrintFFormat * inFormat,PrintFVAList * inArgs,void * inUserContext)26252 	_PrintFExtensionHandler_DNSRecordData(
26253 		PrintFContext *	inContext,
26254 		PrintFFormat *	inFormat,
26255 		PrintFVAList *	inArgs,
26256 		void *			inUserContext )
26257 {
26258 	const void *		rdataPtr;
26259 	int					recordType, n, fieldWidth;
26260 	unsigned int		rdataLen;
26261 
26262 	Unused( inUserContext );
26263 
26264 	recordType	= va_arg( inArgs->args, int );
26265 	rdataPtr	= va_arg( inArgs->args, const void * );
26266 	rdataLen	= va_arg( inArgs->args, unsigned int );
26267 	require_action_quiet( !inFormat->suppress, exit, n = 0 );
26268 
26269 	check( inFormat->fieldWidth < INT_MAX );
26270 	fieldWidth = inFormat->leftJustify ? -( (int) inFormat->fieldWidth ) : ( (int) inFormat->fieldWidth );
26271 
26272 	if( rdataLen > 0 )
26273 	{
26274 		char *		rdataStr = NULL;
26275 
26276 		DNSRecordDataToString( rdataPtr, rdataLen, recordType, &rdataStr );
26277 		if( rdataStr )
26278 		{
26279 			n = PrintFCore( inContext, "%*s", fieldWidth, rdataStr );
26280 			free( rdataStr );
26281 		}
26282 		else
26283 		{
26284 			n = PrintFCore( inContext, "%*H", fieldWidth, rdataPtr, rdataLen, rdataLen );
26285 		}
26286 	}
26287 	else
26288 	{
26289 		n = PrintFCore( inContext, "%*s", fieldWidth, "<< ZERO-LENGTH RDATA >>" );
26290 	}
26291 
26292 exit:
26293 	return( n );
26294 }
26295 
26296 //===========================================================================================================================
26297 //	_PrintFExtensionHandler_DomainName
26298 //===========================================================================================================================
26299 
26300 static int
_PrintFExtensionHandler_DomainName(PrintFContext * inContext,PrintFFormat * inFormat,PrintFVAList * inArgs,void * inUserContext)26301 	_PrintFExtensionHandler_DomainName(
26302 		PrintFContext *	inContext,
26303 		PrintFFormat *	inFormat,
26304 		PrintFVAList *	inArgs,
26305 		void *			inUserContext )
26306 {
26307 	OSStatus			err;
26308 	const uint8_t *		namePtr;
26309 	int					n, fieldWidth;
26310 	char				nameStr[ kDNSServiceMaxDomainName ];
26311 
26312 	Unused( inUserContext );
26313 
26314 	namePtr = va_arg( inArgs->args, const uint8_t * );
26315 	require_action_quiet( !inFormat->suppress, exit, n = 0 );
26316 
26317 	check( inFormat->fieldWidth < INT_MAX );
26318 	fieldWidth = inFormat->leftJustify ? -( (int) inFormat->fieldWidth ) : ( (int) inFormat->fieldWidth );
26319 
26320 	err = DomainNameToString( namePtr, NULL, nameStr, NULL );
26321 	check_noerr( err );
26322 	if( !err )
26323 	{
26324 		n = PrintFCore( inContext, "%*s", fieldWidth, nameStr );
26325 	}
26326 	else
26327 	{
26328 		n = PrintFCore( inContext, "%*s", fieldWidth, "<< ERROR: domain name conversion failed >>" );
26329 	}
26330 
26331 exit:
26332 	return( n );
26333 }
26334 
26335 
26336 //===========================================================================================================================
26337 //	GetDNSSDFlagsFromOpts
26338 //===========================================================================================================================
26339 
GetDNSSDFlagsFromOpts(void)26340 static DNSServiceFlags	GetDNSSDFlagsFromOpts( void )
26341 {
26342 	DNSServiceFlags		flags;
26343 
26344 	flags = (DNSServiceFlags) gDNSSDFlags;
26345 	if( flags & kDNSServiceFlagsShareConnection )
26346 	{
26347 		FPrintF( stderr, "*** Warning: kDNSServiceFlagsShareConnection (0x%X) is explicitly set in flag parameters.\n",
26348 			kDNSServiceFlagsShareConnection );
26349 	}
26350 
26351 	if( gDNSSDFlag_AllowExpiredAnswers )	flags |= kDNSServiceFlagsAllowExpiredAnswers;
26352 	if( gDNSSDFlag_BrowseDomains )			flags |= kDNSServiceFlagsBrowseDomains;
26353 	if( gDNSSDFlag_DenyCellular )			flags |= kDNSServiceFlagsDenyCellular;
26354 	if( gDNSSDFlag_DenyConstrained )		flags |= kDNSServiceFlagsDenyConstrained;
26355 	if( gDNSSDFlag_DenyExpensive )			flags |= kDNSServiceFlagsDenyExpensive;
26356 	if( gDNSSDFlag_ForceMulticast )			flags |= kDNSServiceFlagsForceMulticast;
26357 	if( gDNSSDFlag_IncludeAWDL )			flags |= kDNSServiceFlagsIncludeAWDL;
26358 	if( gDNSSDFlag_KnownUnique )			flags |= kDNSServiceFlagsKnownUnique;
26359 	if( gDNSSDFlag_NoAutoRename )			flags |= kDNSServiceFlagsNoAutoRename;
26360 	if( gDNSSDFlag_PathEvaluationDone )		flags |= kDNSServiceFlagsPathEvaluationDone;
26361 	if( gDNSSDFlag_RegistrationDomains )	flags |= kDNSServiceFlagsRegistrationDomains;
26362 	if( gDNSSDFlag_ReturnIntermediates )	flags |= kDNSServiceFlagsReturnIntermediates;
26363 	if( gDNSSDFlag_Shared )					flags |= kDNSServiceFlagsShared;
26364 	if( gDNSSDFlag_SuppressUnusable )		flags |= kDNSServiceFlagsSuppressUnusable;
26365 	if( gDNSSDFlag_Timeout )				flags |= kDNSServiceFlagsTimeout;
26366 	if( gDNSSDFlag_UnicastResponse )		flags |= kDNSServiceFlagsUnicastResponse;
26367 	if( gDNSSDFlag_Unique )					flags |= kDNSServiceFlagsUnique;
26368 	if( gDNSSDFlag_WakeOnResolve )			flags |= kDNSServiceFlagsWakeOnResolve;
26369 	if( gDNSSDFlag_EnableDNSSEC )			flags |= kDNSServiceFlagsValidate;
26370 
26371 	return( flags );
26372 }
26373 
26374 //===========================================================================================================================
26375 //	CreateConnectionFromArgString
26376 //===========================================================================================================================
26377 
26378 static OSStatus
CreateConnectionFromArgString(const char * inString,dispatch_queue_t inQueue,DNSServiceRef * outSDRef,ConnectionDesc * outDesc)26379 	CreateConnectionFromArgString(
26380 		const char *			inString,
26381 		dispatch_queue_t		inQueue,
26382 		DNSServiceRef *			outSDRef,
26383 		ConnectionDesc *		outDesc )
26384 {
26385 	OSStatus			err;
26386 	DNSServiceRef		sdRef = NULL;
26387 	ConnectionType		type;
26388 	int32_t				pid = -1;	// Initializing because the analyzer claims pid may be used uninitialized.
26389 	uint8_t				uuid[ 16 ];
26390 
26391 	if( strcasecmp( inString, kConnectionArg_Normal ) == 0 )
26392 	{
26393 		err = DNSServiceCreateConnection( &sdRef );
26394 		require_noerr( err, exit );
26395 		type = kConnectionType_Normal;
26396 	}
26397 	else if( stricmp_prefix( inString, kConnectionArgPrefix_PID ) == 0 )
26398 	{
26399 		const char * const		pidStr = inString + sizeof_string( kConnectionArgPrefix_PID );
26400 
26401 		err = StringToInt32( pidStr, &pid );
26402 		if( err )
26403 		{
26404 			FPrintF( stderr, "Invalid delegate connection PID value: %s\n", pidStr );
26405 			err = kParamErr;
26406 			goto exit;
26407 		}
26408 
26409 		memset( uuid, 0, sizeof( uuid ) );
26410 		err = DNSServiceCreateDelegateConnection( &sdRef, pid, uuid );
26411 		if( err )
26412 		{
26413 			FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for PID %d\n", err, pid );
26414 			goto exit;
26415 		}
26416 		type = kConnectionType_DelegatePID;
26417 	}
26418 	else if( stricmp_prefix( inString, kConnectionArgPrefix_UUID ) == 0 )
26419 	{
26420 		const char * const		uuidStr = inString + sizeof_string( kConnectionArgPrefix_UUID );
26421 
26422 		check_compile_time_code( sizeof( uuid ) == sizeof( uuid_t ) );
26423 
26424 		err = StringToUUID( uuidStr, kSizeCString, false, uuid );
26425 		if( err )
26426 		{
26427 			FPrintF( stderr, "Invalid delegate connection UUID value: %s\n", uuidStr );
26428 			err = kParamErr;
26429 			goto exit;
26430 		}
26431 
26432 		err = DNSServiceCreateDelegateConnection( &sdRef, 0, uuid );
26433 		if( err )
26434 		{
26435 			FPrintF( stderr, "DNSServiceCreateDelegateConnection() returned %#m for UUID %#U\n", err, uuid );
26436 			goto exit;
26437 		}
26438 		type = kConnectionType_DelegateUUID;
26439 	}
26440 	else
26441 	{
26442 		FPrintF( stderr, "Unrecognized connection string \"%s\".\n", inString );
26443 		err = kParamErr;
26444 		goto exit;
26445 	}
26446 
26447 	err = DNSServiceSetDispatchQueue( sdRef, inQueue );
26448 	require_noerr( err, exit );
26449 
26450 	*outSDRef = sdRef;
26451 	if( outDesc )
26452 	{
26453 		outDesc->type = type;
26454 		if(      type == kConnectionType_DelegatePID )	outDesc->delegate.pid = pid;
26455 		else if( type == kConnectionType_DelegateUUID )	memcpy( outDesc->delegate.uuid, uuid, 16 );
26456 	}
26457 	sdRef = NULL;
26458 
26459 exit:
26460 	if( sdRef ) DNSServiceRefDeallocate( sdRef );
26461 	return( err );
26462 }
26463 
26464 //===========================================================================================================================
26465 //	InterfaceIndexFromArgString
26466 //===========================================================================================================================
26467 
InterfaceIndexFromArgString(const char * inString,uint32_t * outIndex)26468 static OSStatus	InterfaceIndexFromArgString( const char *inString, uint32_t *outIndex )
26469 {
26470 	OSStatus		err;
26471 	uint32_t		ifIndex;
26472 
26473 	if( inString )
26474 	{
26475 		ifIndex = if_nametoindex( inString );
26476 		if( ifIndex == 0 )
26477 		{
26478 			err = StringToUInt32( inString, &ifIndex );
26479 			if( err )
26480 			{
26481 				FPrintF( stderr, "error: Invalid interface value: %s\n", inString );
26482 				err = kParamErr;
26483 				goto exit;
26484 			}
26485 		}
26486 	}
26487 	else
26488 	{
26489 		ifIndex	= 0;
26490 	}
26491 
26492 	*outIndex = ifIndex;
26493 	err = kNoErr;
26494 
26495 exit:
26496 	return( err );
26497 }
26498 
26499 //===========================================================================================================================
26500 //	RecordDataFromArgString
26501 //===========================================================================================================================
26502 
RecordDataFromArgString(const char * inString,uint8_t ** outDataPtr,size_t * outDataLen)26503 static OSStatus	RecordDataFromArgString( const char *inString, uint8_t **outDataPtr, size_t *outDataLen )
26504 {
26505 	OSStatus		err;
26506 	uint8_t *		dataPtr = NULL;
26507 	size_t			dataLen;
26508 
26509 	if( 0 ) {}
26510 
26511 	// Domain name
26512 
26513 	else if( stricmp_prefix( inString, kRDataArgPrefix_Domain ) == 0 )
26514 	{
26515 		const char * const		str = inString + sizeof_string( kRDataArgPrefix_Domain );
26516 
26517 		err = StringToDomainName( str, &dataPtr, &dataLen );
26518 		require_noerr_quiet( err, exit );
26519 	}
26520 
26521 	// File path
26522 
26523 	else if( stricmp_prefix( inString, kRDataArgPrefix_File ) == 0 )
26524 	{
26525 		const char * const		path = inString + sizeof_string( kRDataArgPrefix_File );
26526 
26527 		err = CopyFileDataByPath( path, (char **) &dataPtr, &dataLen );
26528 		require_noerr( err, exit );
26529 		require_action( dataLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
26530 	}
26531 
26532 	// Hexadecimal string
26533 
26534 	else if( stricmp_prefix( inString, kRDataArgPrefix_HexString ) == 0 )
26535 	{
26536 		const char * const		str = inString + sizeof_string( kRDataArgPrefix_HexString );
26537 
26538 		err = HexToDataCopy( str, kSizeCString, kHexToData_DefaultFlags, &dataPtr, &dataLen, NULL );
26539 		require_noerr( err, exit );
26540 		require_action( dataLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
26541 	}
26542 
26543 	// IPv4 address string
26544 
26545 	else if( stricmp_prefix( inString, kRDataArgPrefix_IPv4 ) == 0 )
26546 	{
26547 		const char * const		str = inString + sizeof_string( kRDataArgPrefix_IPv4 );
26548 
26549 		err = StringToARecordData( str, &dataPtr, &dataLen );
26550 		require_noerr_quiet( err, exit );
26551 	}
26552 
26553 	// IPv6 address string
26554 
26555 	else if( stricmp_prefix( inString, kRDataArgPrefix_IPv6 ) == 0 )
26556 	{
26557 		const char * const		str = inString + sizeof_string( kRDataArgPrefix_IPv6 );
26558 
26559 		err = StringToAAAARecordData( str, &dataPtr, &dataLen );
26560 		require_noerr_quiet( err, exit );
26561 	}
26562 
26563 	// SRV record
26564 
26565 	else if( stricmp_prefix( inString, kRDataArgPrefix_SRV ) == 0 )
26566 	{
26567 		const char * const		str = inString + sizeof_string( kRDataArgPrefix_SRV );
26568 
26569 		err = CreateSRVRecordDataFromString( str, &dataPtr, &dataLen );
26570 		require_noerr( err, exit );
26571 	}
26572 
26573 	// String with escaped hex and octal bytes
26574 
26575 	else if( stricmp_prefix( inString, kRDataArgPrefix_String ) == 0 )
26576 	{
26577 		const char * const		str = inString + sizeof_string( kRDataArgPrefix_String );
26578 		const char * const		end = str + strlen( str );
26579 		size_t					copiedLen;
26580 		size_t					totalLen;
26581 		Boolean					success;
26582 
26583 		if( str < end )
26584 		{
26585 			success = _ParseQuotedEscapedString( str, end, "", NULL, 0, NULL, &totalLen, NULL );
26586 			require_action( success, exit, err = kParamErr );
26587 			require_action( totalLen <= kDNSRecordDataLengthMax, exit, err = kSizeErr );
26588 
26589 			dataLen = totalLen;
26590 			dataPtr = (uint8_t *) malloc( dataLen );
26591 			require_action( dataPtr, exit, err = kNoMemoryErr );
26592 
26593 			success = _ParseQuotedEscapedString( str, end, "", (char *) dataPtr, dataLen, &copiedLen, NULL, NULL );
26594 			require_action( success, exit, err = kParamErr );
26595 			check( copiedLen == dataLen );
26596 		}
26597 		else
26598 		{
26599 			dataPtr = NULL;
26600 			dataLen = 0;
26601 		}
26602 	}
26603 
26604 	// TXT record
26605 
26606 	else if( stricmp_prefix( inString, kRDataArgPrefix_TXT ) == 0 )
26607 	{
26608 		const char * const		str = inString + sizeof_string( kRDataArgPrefix_TXT );
26609 
26610 		err = CreateTXTRecordDataFromString( str, ',', &dataPtr, &dataLen );
26611 		require_noerr( err, exit );
26612 	}
26613 
26614 	// Unrecognized format
26615 
26616 	else
26617 	{
26618 		FPrintF( stderr, "Unrecognized record data string \"%s\".\n", inString );
26619 		err = kParamErr;
26620 		goto exit;
26621 	}
26622 
26623 	err = kNoErr;
26624 	*outDataLen = dataLen;
26625 	*outDataPtr = dataPtr;
26626 	dataPtr = NULL;
26627 
26628 exit:
26629 	FreeNullSafe( dataPtr );
26630 	return( err );
26631 }
26632 
26633 //===========================================================================================================================
26634 //	RecordTypeFromArgString
26635 //===========================================================================================================================
26636 
RecordTypeFromArgString(const char * inString,uint16_t * outValue)26637 static OSStatus	RecordTypeFromArgString( const char *inString, uint16_t *outValue )
26638 {
26639 	OSStatus		err;
26640 	uint16_t		value;
26641 
26642 	value = DNSRecordTypeStringToValue( inString );
26643 	if( value == 0 )
26644 	{
26645 		int32_t		i32;
26646 
26647 		err = StringToInt32( inString, &i32 );
26648 		require_noerr_quiet( err, exit );
26649 		require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
26650 		value = (uint16_t) i32;
26651 	}
26652 	*outValue = value;
26653 	err = kNoErr;
26654 
26655 exit:
26656 	return( err );
26657 }
26658 
26659 //===========================================================================================================================
26660 //	RecordClassFromArgString
26661 //===========================================================================================================================
26662 
RecordClassFromArgString(const char * inString,uint16_t * outValue)26663 static OSStatus	RecordClassFromArgString( const char *inString, uint16_t *outValue )
26664 {
26665 	OSStatus		err;
26666 	int32_t			i32;
26667 
26668 	if( strcasecmp( inString, "IN" ) == 0 )
26669 	{
26670 		*outValue = kDNSServiceClass_IN;
26671 		err = kNoErr;
26672 		goto exit;
26673 	}
26674 
26675 	err = StringToInt32( inString, &i32 );
26676 	require_noerr_quiet( err, exit );
26677 	require_action_quiet( ( i32 >= 0 ) && ( i32 <= UINT16_MAX ), exit, err = kParamErr );
26678 
26679 	*outValue = (uint16_t) i32;
26680 
26681 exit:
26682 	return( err );
26683 }
26684 
26685 //===========================================================================================================================
26686 //	InterfaceIndexToName
26687 //===========================================================================================================================
26688 
InterfaceIndexToName(uint32_t inIfIndex,char inNameBuf[kInterfaceNameBufLen])26689 static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] )
26690 {
26691 	switch( inIfIndex )
26692 	{
26693 		case kDNSServiceInterfaceIndexAny:
26694 			strlcpy( inNameBuf, "Any", kInterfaceNameBufLen );
26695 			break;
26696 
26697 		case kDNSServiceInterfaceIndexLocalOnly:
26698 			strlcpy( inNameBuf, "LocalOnly", kInterfaceNameBufLen );
26699 			break;
26700 
26701 		case kDNSServiceInterfaceIndexUnicast:
26702 			strlcpy( inNameBuf, "Unicast", kInterfaceNameBufLen );
26703 			break;
26704 
26705 		case kDNSServiceInterfaceIndexP2P:
26706 			strlcpy( inNameBuf, "P2P", kInterfaceNameBufLen );
26707 			break;
26708 
26709 	#if( defined( kDNSServiceInterfaceIndexBLE ) )
26710 		case kDNSServiceInterfaceIndexBLE:
26711 			strlcpy( inNameBuf, "BLE", kInterfaceNameBufLen );
26712 			break;
26713 	#endif
26714 
26715 		default:
26716 		{
26717 			const char *		name;
26718 
26719 			name = if_indextoname( inIfIndex, inNameBuf );
26720 			if( !name ) strlcpy( inNameBuf, "NO NAME", kInterfaceNameBufLen );
26721 			break;
26722 		}
26723 	}
26724 
26725 	return( inNameBuf );
26726 }
26727 
26728 //===========================================================================================================================
26729 //	RecordTypeToString
26730 //===========================================================================================================================
26731 
RecordTypeToString(int inValue)26732 static const char *	RecordTypeToString( int inValue )
26733 {
26734 	const char *		string;
26735 
26736 	string = DNSRecordTypeValueToString( inValue );
26737 	if( !string ) string = "???";
26738 	return( string );
26739 }
26740 
26741 #if( MDNSRESPONDER_PROJECT )
26742 //===========================================================================================================================
26743 //	RecordClassToString
26744 //===========================================================================================================================
26745 
RecordClassToString(int inValue)26746 static const char *	RecordClassToString( int inValue )
26747 {
26748 	return( ( inValue == kDNSServiceClass_IN ) ? "IN" : "???" );
26749 }
26750 #endif
26751 
26752 //===========================================================================================================================
26753 //	WriteDNSQueryMessage
26754 //===========================================================================================================================
26755 
26756 static OSStatus
WriteDNSQueryMessage(uint8_t inMsg[kDNSQueryMessageMaxLen],uint16_t inMsgID,uint16_t inFlags,const char * inQName,uint16_t inQType,uint16_t inQClass,size_t * outMsgLen)26757 	WriteDNSQueryMessage(
26758 		uint8_t			inMsg[ kDNSQueryMessageMaxLen ],
26759 		uint16_t		inMsgID,
26760 		uint16_t		inFlags,
26761 		const char *	inQName,
26762 		uint16_t		inQType,
26763 		uint16_t		inQClass,
26764 		size_t *		outMsgLen )
26765 {
26766 	OSStatus		err;
26767 	uint8_t			qname[ kDomainNameLengthMax ];
26768 
26769 	err = DomainNameFromString( qname, inQName, NULL );
26770 	require_noerr_quiet( err, exit );
26771 
26772 	err = DNSMessageWriteQuery( inMsgID, inFlags, qname, inQType, inQClass, inMsg, outMsgLen );
26773 	require_noerr_quiet( err, exit );
26774 
26775 exit:
26776 	return( err );
26777 }
26778 
26779 //===========================================================================================================================
26780 //	DispatchSignalSourceCreate
26781 //===========================================================================================================================
26782 
26783 static OSStatus
DispatchSignalSourceCreate(int inSignal,dispatch_queue_t inQueue,DispatchHandler inEventHandler,void * inContext,dispatch_source_t * outSource)26784 	DispatchSignalSourceCreate(
26785 		int					inSignal,
26786 		dispatch_queue_t	inQueue,
26787 		DispatchHandler		inEventHandler,
26788 		void *				inContext,
26789 		dispatch_source_t *	outSource )
26790 {
26791 	OSStatus				err;
26792 	dispatch_source_t		source;
26793 
26794 	source = dispatch_source_create( DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t) inSignal, 0, inQueue );
26795 	require_action( source, exit, err = kUnknownErr );
26796 
26797 	dispatch_set_context( source, inContext );
26798 	dispatch_source_set_event_handler_f( source, inEventHandler );
26799 
26800 	*outSource = source;
26801 	err = kNoErr;
26802 
26803 exit:
26804 	return( err );
26805 }
26806 
26807 //===========================================================================================================================
26808 //	DispatchSocketSourceCreate
26809 //===========================================================================================================================
26810 
26811 static OSStatus
DispatchSocketSourceCreate(SocketRef inSock,dispatch_source_type_t inType,dispatch_queue_t inQueue,DispatchHandler inEventHandler,DispatchHandler inCancelHandler,void * inContext,dispatch_source_t * outSource)26812 	DispatchSocketSourceCreate(
26813 		SocketRef				inSock,
26814 		dispatch_source_type_t	inType,
26815 		dispatch_queue_t		inQueue,
26816 		DispatchHandler			inEventHandler,
26817 		DispatchHandler			inCancelHandler,
26818 		void *					inContext,
26819 		dispatch_source_t *		outSource )
26820 {
26821 	OSStatus				err;
26822 	dispatch_source_t		source;
26823 
26824 	source = dispatch_source_create( inType, (uintptr_t) inSock, 0, inQueue ? inQueue : dispatch_get_main_queue() );
26825 	require_action( source, exit, err = kNoResourcesErr );
26826 
26827 	dispatch_set_context( source, inContext );
26828 	dispatch_source_set_event_handler_f( source, inEventHandler );
26829 	dispatch_source_set_cancel_handler_f( source, inCancelHandler );
26830 
26831 	*outSource = source;
26832 	err = kNoErr;
26833 
26834 exit:
26835 	return( err );
26836 }
26837 
26838 //===========================================================================================================================
26839 //	DispatchTimerCreate
26840 //===========================================================================================================================
26841 
26842 static OSStatus
DispatchTimerCreate(dispatch_time_t inStart,uint64_t inIntervalNs,uint64_t inLeewayNs,dispatch_queue_t inQueue,DispatchHandler inEventHandler,DispatchHandler inCancelHandler,void * inContext,dispatch_source_t * outTimer)26843 	DispatchTimerCreate(
26844 		dispatch_time_t		inStart,
26845 		uint64_t			inIntervalNs,
26846 		uint64_t			inLeewayNs,
26847 		dispatch_queue_t	inQueue,
26848 		DispatchHandler		inEventHandler,
26849 		DispatchHandler		inCancelHandler,
26850 		void *				inContext,
26851 		dispatch_source_t *	outTimer )
26852 {
26853 	OSStatus				err;
26854 	dispatch_source_t		timer;
26855 
26856 	timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, inQueue ? inQueue : dispatch_get_main_queue() );
26857 	require_action( timer, exit, err = kNoResourcesErr );
26858 
26859 	dispatch_source_set_timer( timer, inStart, inIntervalNs, inLeewayNs );
26860 	dispatch_set_context( timer, inContext );
26861 	dispatch_source_set_event_handler_f( timer, inEventHandler );
26862 	dispatch_source_set_cancel_handler_f( timer, inCancelHandler );
26863 
26864 	*outTimer = timer;
26865 	err = kNoErr;
26866 
26867 exit:
26868 	return( err );
26869 }
26870 
26871 #if( TARGET_OS_DARWIN )
26872 //===========================================================================================================================
26873 //	DispatchProcessMonitorCreate
26874 //===========================================================================================================================
26875 
26876 static OSStatus
DispatchProcessMonitorCreate(pid_t inPID,unsigned long inFlags,dispatch_queue_t inQueue,DispatchHandler inEventHandler,DispatchHandler inCancelHandler,void * inContext,dispatch_source_t * outMonitor)26877 	DispatchProcessMonitorCreate(
26878 		pid_t				inPID,
26879 		unsigned long		inFlags,
26880 		dispatch_queue_t	inQueue,
26881 		DispatchHandler		inEventHandler,
26882 		DispatchHandler		inCancelHandler,
26883 		void *				inContext,
26884 		dispatch_source_t *	outMonitor )
26885 {
26886 	OSStatus				err;
26887 	dispatch_source_t		monitor;
26888 
26889 	monitor = dispatch_source_create( DISPATCH_SOURCE_TYPE_PROC, (uintptr_t) inPID, inFlags,
26890 		inQueue ? inQueue : dispatch_get_main_queue() );
26891 	require_action( monitor, exit, err = kUnknownErr );
26892 
26893 	dispatch_set_context( monitor, inContext );
26894 	dispatch_source_set_event_handler_f( monitor, inEventHandler );
26895 	dispatch_source_set_cancel_handler_f( monitor, inCancelHandler );
26896 
26897 	*outMonitor = monitor;
26898 	err = kNoErr;
26899 
26900 exit:
26901 	return( err );
26902 }
26903 #endif
26904 
26905 //===========================================================================================================================
26906 //	ServiceTypeDescription
26907 //===========================================================================================================================
26908 
26909 typedef struct
26910 {
26911 	const char *		name;			// Name of the service type in two-label "_service._proto" format.
26912 	const char *		description;	// Description of the service type.
26913 
26914 }	ServiceType;
26915 
26916 // A Non-comprehensive table of DNS-SD service types
26917 
26918 static const ServiceType		kServiceTypes[] =
26919 {
26920 	{ "_acp-sync._tcp",			"AirPort Base Station Sync" },
26921 	{ "_adisk._tcp",			"Automatic Disk Discovery" },
26922 	{ "_afpovertcp._tcp",		"Apple File Sharing" },
26923 	{ "_airdrop._tcp",			"AirDrop" },
26924 	{ "_airplay._tcp",			"AirPlay" },
26925 	{ "_airport._tcp",			"AirPort Base Station" },
26926 	{ "_daap._tcp",				"Digital Audio Access Protocol (iTunes)" },
26927 	{ "_eppc._tcp",				"Remote AppleEvents" },
26928 	{ "_ftp._tcp",				"File Transfer Protocol" },
26929 	{ "_home-sharing._tcp",		"Home Sharing" },
26930 	{ "_homekit._tcp",			"HomeKit" },
26931 	{ "_http._tcp",				"World Wide Web HTML-over-HTTP" },
26932 	{ "_https._tcp",			"HTTP over SSL/TLS" },
26933 	{ "_ipp._tcp",				"Internet Printing Protocol" },
26934 	{ "_ldap._tcp",				"Lightweight Directory Access Protocol" },
26935 	{ "_mediaremotetv._tcp",	"Media Remote" },
26936 	{ "_net-assistant._tcp",	"Apple Remote Desktop" },
26937 	{ "_od-master._tcp",		"OpenDirectory Master" },
26938 	{ "_nfs._tcp",				"Network File System" },
26939 	{ "_presence._tcp",			"Peer-to-peer messaging / Link-Local Messaging" },
26940 	{ "_pdl-datastream._tcp",	"Printer Page Description Language Data Stream" },
26941 	{ "_raop._tcp",				"Remote Audio Output Protocol" },
26942 	{ "_rfb._tcp",				"Remote Frame Buffer" },
26943 	{ "_scanner._tcp",			"Bonjour Scanning" },
26944 	{ "_smb._tcp",				"Server Message Block over TCP/IP" },
26945 	{ "_sftp-ssh._tcp",			"Secure File Transfer Protocol over SSH" },
26946 	{ "_sleep-proxy._udp",		"Sleep Proxy Server" },
26947 	{ "_ssh._tcp",				"SSH Remote Login Protocol" },
26948 	{ "_teleport._tcp",			"teleport" },
26949 	{ "_tftp._tcp",				"Trivial File Transfer Protocol" },
26950 	{ "_workstation._tcp",		"Workgroup Manager" },
26951 	{ "_webdav._tcp",			"World Wide Web Distributed Authoring and Versioning (WebDAV)" },
26952 	{ "_webdavs._tcp",			"WebDAV over SSL/TLS" }
26953 };
26954 
ServiceTypeDescription(const char * inName)26955 static const char *	ServiceTypeDescription( const char *inName )
26956 {
26957 	const ServiceType *				serviceType;
26958 	const ServiceType * const		end = kServiceTypes + countof( kServiceTypes );
26959 
26960 	for( serviceType = kServiceTypes; serviceType < end; ++serviceType )
26961 	{
26962 		if( ( stricmp_prefix( inName, serviceType->name ) == 0 ) )
26963 		{
26964 			const char * const		ptr = &inName[ strlen( serviceType->name ) ];
26965 
26966 			if( ( ptr[ 0 ] == '\0' ) || ( ( ptr[ 0 ] == '.' ) && ( ptr[ 1 ] == '\0' ) ) )
26967 			{
26968 				return( serviceType->description );
26969 			}
26970 		}
26971 	}
26972 	return( NULL );
26973 }
26974 
26975 //===========================================================================================================================
26976 //	SocketContextCreate
26977 //===========================================================================================================================
26978 
SocketContextCreate(SocketRef inSock,void * inUserContext,OSStatus * outError)26979 static SocketContext *	SocketContextCreate( SocketRef inSock, void *inUserContext, OSStatus *outError )
26980 {
26981 	return( SocketContextCreateEx( inSock, inUserContext, NULL, outError ) );
26982 }
26983 
26984 //===========================================================================================================================
26985 //	SocketContextCreateEx
26986 //===========================================================================================================================
26987 
26988 static SocketContext *
SocketContextCreateEx(SocketRef inSock,void * inUserContext,SocketContextFinalizer_f inUserFinalizer,OSStatus * outError)26989 	SocketContextCreateEx(
26990 		SocketRef					inSock,
26991 		void *						inUserContext,
26992 		SocketContextFinalizer_f	inUserFinalizer,
26993 		OSStatus *					outError )
26994 {
26995 	OSStatus			err;
26996 	SocketContext *		context;
26997 
26998 	context = (SocketContext *) calloc( 1, sizeof( *context ) );
26999 	require_action( context, exit, err = kNoMemoryErr );
27000 
27001 	context->refCount		= 1;
27002 	context->sock			= inSock;
27003 	context->userContext	= inUserContext;
27004 	context->userFinalizer	= inUserFinalizer;
27005 	err = kNoErr;
27006 
27007 exit:
27008 	if( outError ) *outError = err;
27009 	return( context );
27010 }
27011 
27012 //===========================================================================================================================
27013 //	SocketContextRetain
27014 //===========================================================================================================================
27015 
SocketContextRetain(SocketContext * inContext)27016 static SocketContext *	SocketContextRetain( SocketContext *inContext )
27017 {
27018 	atomic_add_32( &inContext->refCount, 1 );
27019 	return( inContext );
27020 }
27021 
27022 //===========================================================================================================================
27023 //	SocketContextRelease
27024 //===========================================================================================================================
27025 
SocketContextRelease(SocketContext * me)27026 static void	SocketContextRelease( SocketContext *me )
27027 {
27028 	if( atomic_add_and_fetch_32( &me->refCount, -1 ) == 0 )
27029 	{
27030 		ForgetSocket( &me->sock );
27031 		if( me->userFinalizer )
27032 		{
27033 			me->userFinalizer( me->userContext );
27034 			me->userFinalizer = NULL;
27035 		}
27036 		me->userContext = NULL;
27037 		free( me );
27038 	}
27039 }
27040 
27041 //===========================================================================================================================
27042 //	SocketContextCancelHandler
27043 //===========================================================================================================================
27044 
SocketContextCancelHandler(void * inContext)27045 static void	SocketContextCancelHandler( void *inContext )
27046 {
27047 	SocketContextRelease( (SocketContext *) inContext );
27048 }
27049 
27050 //===========================================================================================================================
27051 //	SocketContextFinalizerCF
27052 //===========================================================================================================================
27053 
SocketContextFinalizerCF(void * inUserCtx)27054 static void	SocketContextFinalizerCF( void *inUserCtx )
27055 {
27056 	CFRelease( (CFTypeRef) inUserCtx );
27057 }
27058 
27059 //===========================================================================================================================
27060 //	StringToInt32
27061 //===========================================================================================================================
27062 
StringToInt32(const char * inString,int32_t * outValue)27063 static OSStatus	StringToInt32( const char *inString, int32_t *outValue )
27064 {
27065 	OSStatus		err;
27066 	long			value;
27067 	char *			endPtr;
27068 
27069 	value = strtol( inString, &endPtr, 0 );
27070 	require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
27071 	require_action_quiet( ( value >= INT32_MIN ) && ( value <= INT32_MAX ), exit, err = kRangeErr );
27072 
27073 	*outValue = (int32_t) value;
27074 	err = kNoErr;
27075 
27076 exit:
27077 	return( err );
27078 }
27079 
27080 //===========================================================================================================================
27081 //	StringToUInt32
27082 //===========================================================================================================================
27083 
StringToUInt32(const char * inString,uint32_t * outValue)27084 static OSStatus	StringToUInt32( const char *inString, uint32_t *outValue )
27085 {
27086 	OSStatus		err;
27087 	uint32_t		value;
27088 	char *			endPtr;
27089 
27090 	value = (uint32_t) strtol( inString, &endPtr, 0 );
27091 	require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
27092 
27093 	*outValue = value;
27094 	err = kNoErr;
27095 
27096 exit:
27097 	return( err );
27098 }
27099 
27100 #if( TARGET_OS_DARWIN )
27101 //===========================================================================================================================
27102 //	_StringToInt64
27103 //===========================================================================================================================
27104 
_StringToInt64(const char * inString,OSStatus * outError)27105 static int64_t	_StringToInt64( const char *inString, OSStatus *outError )
27106 {
27107 	OSStatus		err;
27108 	long long		ll;
27109 	char *			end;
27110 	int64_t			i64 = 0;
27111 	int				errnoVal;
27112 
27113 	set_errno_compat( 0 );
27114 	ll = strtoll( inString, &end, 0 );
27115 	errnoVal = errno_compat();
27116 	require_action_quiet( ( *end == '\0' ) && ( end != inString ), exit, err = kMalformedErr );
27117 	require_action_quiet( ( ( ll != LLONG_MIN ) && ( ll != LLONG_MAX ) ) || ( errnoVal != ERANGE ), exit, err = kRangeErr );
27118 	require_action_quiet( ( ll >= INT64_MIN ) && ( ll <= INT64_MAX ), exit, err = kRangeErr );
27119 	i64 = (int64_t) ll;
27120 	err = kNoErr;
27121 
27122 exit:
27123 	if( outError ) *outError = err;
27124 	return( i64 );
27125 }
27126 
27127 //===========================================================================================================================
27128 //	_StringToUInt64
27129 //===========================================================================================================================
27130 
_StringToUInt64(const char * inString,OSStatus * outError)27131 static uint64_t	_StringToUInt64( const char *inString, OSStatus *outError )
27132 {
27133 	OSStatus				err;
27134 	unsigned long long		val;
27135 	char *					end;
27136 	int						errnoVal;
27137 
27138 	set_errno_compat( 0 );
27139 	val = strtoull( inString, &end, 0 );
27140 	errnoVal = errno_compat();
27141 
27142 	require_action_quiet( ( *end == '\0' ) && ( end != inString ), exit, err = kMalformedErr );
27143 	require_action_quiet( ( val != ULLONG_MAX ) || ( errnoVal != ERANGE ), exit, err = kRangeErr );
27144 	require_action_quiet( val <= UINT64_MAX, exit, err = kRangeErr );
27145 	err = kNoErr;
27146 
27147 exit:
27148 	if( outError ) *outError = err;
27149 	return( (uint64_t)val );
27150 }
27151 
27152 //===========================================================================================================================
27153 //	_StringToPID
27154 //===========================================================================================================================
27155 
_StringToPID(const char * inString,OSStatus * outError)27156 static pid_t	_StringToPID( const char *inString, OSStatus *outError )
27157 {
27158 	OSStatus		err;
27159 	int64_t			i64;
27160 	pid_t			pid = 0;
27161 
27162 	i64 = _StringToInt64( inString, &err );
27163 	require_noerr_quiet( err, exit );
27164 	require_action_quiet( i64 == (pid_t) i64, exit, err = kRangeErr );
27165 	pid = (pid_t) i64;
27166 	err = kNoErr;
27167 
27168 exit:
27169 	if( outError ) *outError = err;
27170 	return( pid );
27171 }
27172 
27173 //===========================================================================================================================
27174 //	_ParseEscapedString
27175 //
27176 //	Note: Similar to ParseEscapedString() from CoreUtils except that _ParseEscapedString() takes an optional C string
27177 //	containing delimiter characters instead of being limited to one delimiter character. Also, when the function returns
27178 //	due to a delimiter, the output pointer is set to the delimiter character instead of the character after the delimiter.
27179 //===========================================================================================================================
27180 
27181 static OSStatus
_ParseEscapedString(const char * inSrc,const char * inEnd,const char * inDelimiters,char * inBufPtr,size_t inBufLen,size_t * outCopiedLen,size_t * outActualLen,const char ** outPtr)27182 	_ParseEscapedString(
27183 		const char *	inSrc,
27184 		const char *	inEnd,
27185 		const char *	inDelimiters,
27186 		char *			inBufPtr,
27187 		size_t			inBufLen,
27188 		size_t *		outCopiedLen,
27189 		size_t *		outActualLen,
27190 		const char **	outPtr )
27191 {
27192 	OSStatus				err;
27193 	const char *			ptr;
27194 	char *					dst = inBufPtr;
27195 	const char * const		lim = ( inBufLen > 0 ) ? &inBufPtr[ inBufLen - 1 ] : inBufPtr;
27196 	size_t					len;
27197 
27198 	len = 0;
27199 	ptr = inSrc;
27200 	if( !inDelimiters ) inDelimiters = "";
27201 	while( ptr < inEnd )
27202 	{
27203 		int					c;
27204 		const char *		del;
27205 
27206 		c = *ptr;
27207 		for( del = inDelimiters; ( *del != '\0' ) && ( c != *del ); ++del ) {}
27208 		if( *del != '\0' ) break;
27209 		++ptr;
27210 		if( c == '\\' )
27211 		{
27212 			require_action_quiet( ptr < inEnd, exit, err = kUnderrunErr );
27213 			c = *ptr++;
27214 		}
27215 		++len;
27216 		if( dst < lim ) *dst++ = (char) c;
27217 	}
27218 	if( inBufLen > 0 ) *dst = '\0';
27219 
27220 	if( outCopiedLen )	*outCopiedLen	= (size_t)( dst - inBufPtr );
27221 	if( outActualLen )	*outActualLen	= len;
27222 	if( outPtr )		*outPtr			= ptr;
27223 	err = kNoErr;
27224 
27225 exit:
27226 	return( err );
27227 }
27228 #endif
27229 
27230 //===========================================================================================================================
27231 //	StringToARecordData
27232 //===========================================================================================================================
27233 
StringToARecordData(const char * inString,uint8_t ** outPtr,size_t * outLen)27234 static OSStatus	StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
27235 {
27236 	OSStatus			err;
27237 	uint32_t *			addrPtr;
27238 	const size_t		addrLen = sizeof( *addrPtr );
27239 	const char *		end;
27240 
27241 	addrPtr = (uint32_t *) malloc( addrLen );
27242 	require_action( addrPtr, exit, err = kNoMemoryErr );
27243 
27244 	err = _StringToIPv4Address( inString, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix, addrPtr,
27245 		NULL, NULL, NULL, &end );
27246 	if( !err && ( *end != '\0' ) ) err = kMalformedErr;
27247 	require_noerr_quiet( err, exit );
27248 
27249 	*addrPtr = HostToBig32( *addrPtr );
27250 
27251 	*outPtr = (uint8_t *) addrPtr;
27252 	addrPtr = NULL;
27253 	*outLen = addrLen;
27254 
27255 exit:
27256 	FreeNullSafe( addrPtr );
27257 	return( err );
27258 }
27259 
27260 //===========================================================================================================================
27261 //	StringToAAAARecordData
27262 //===========================================================================================================================
27263 
StringToAAAARecordData(const char * inString,uint8_t ** outPtr,size_t * outLen)27264 static OSStatus	StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
27265 {
27266 	OSStatus			err;
27267 	uint8_t *			addrPtr;
27268 	const size_t		addrLen = 16;
27269 	const char *		end;
27270 
27271 	addrPtr = (uint8_t *) malloc( addrLen );
27272 	require_action( addrPtr, exit, err = kNoMemoryErr );
27273 
27274 	err = _StringToIPv6Address( inString,
27275 		kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
27276 		addrPtr, NULL, NULL, NULL, &end );
27277 	if( !err && ( *end != '\0' ) ) err = kMalformedErr;
27278 	require_noerr_quiet( err, exit );
27279 
27280 	*outPtr = addrPtr;
27281 	addrPtr = NULL;
27282 	*outLen = addrLen;
27283 
27284 exit:
27285 	FreeNullSafe( addrPtr );
27286 	return( err );
27287 }
27288 
27289 //===========================================================================================================================
27290 //	StringToDomainName
27291 //===========================================================================================================================
27292 
StringToDomainName(const char * inString,uint8_t ** outPtr,size_t * outLen)27293 static OSStatus	StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen )
27294 {
27295 	OSStatus		err;
27296 	uint8_t *		namePtr;
27297 	size_t			nameLen;
27298 	uint8_t *		end;
27299 	uint8_t			nameBuf[ kDomainNameLengthMax ];
27300 
27301 	err = DomainNameFromString( nameBuf, inString, &end );
27302 	require_noerr_quiet( err, exit );
27303 
27304 	nameLen = (size_t)( end - nameBuf );
27305 	namePtr = _memdup( nameBuf, nameLen );
27306 	require_action( namePtr, exit, err = kNoMemoryErr );
27307 
27308 	*outPtr = namePtr;
27309 	namePtr = NULL;
27310 	if( outLen ) *outLen = nameLen;
27311 
27312 exit:
27313 	return( err );
27314 }
27315 
27316 #if( TARGET_OS_DARWIN )
27317 //===========================================================================================================================
27318 //	GetDefaultDNSServer
27319 //===========================================================================================================================
27320 
GetDefaultDNSServer(sockaddr_ip * outAddr)27321 static OSStatus	GetDefaultDNSServer( sockaddr_ip *outAddr )
27322 {
27323 	OSStatus				err;
27324 	dns_config_t *			config;
27325 	struct sockaddr *		addr;
27326 	int32_t					i;
27327 
27328 	config = dns_configuration_copy();
27329 	require_action( config, exit, err = kUnknownErr );
27330 
27331 	addr = NULL;
27332 	for( i = 0; i < config->n_resolver; ++i )
27333 	{
27334 		const dns_resolver_t * const		resolver = config->resolver[ i ];
27335 
27336 		if( !resolver->domain && ( resolver->n_nameserver > 0 ) )
27337 		{
27338 			addr = resolver->nameserver[ 0 ];
27339 			break;
27340 		}
27341  	}
27342 	require_action_quiet( addr, exit, err = kNotFoundErr );
27343 
27344 	SockAddrCopy( addr, outAddr );
27345 	err = kNoErr;
27346 
27347 exit:
27348 	if( config ) dns_configuration_free( config );
27349 	return( err );
27350 }
27351 #endif
27352 
27353 //===========================================================================================================================
27354 //	GetMDNSMulticastAddrV4
27355 //===========================================================================================================================
27356 
27357 static void	_MDNSMulticastAddrV4Init( void *inContext );
27358 
GetMDNSMulticastAddrV4(void)27359 static const struct sockaddr *	GetMDNSMulticastAddrV4( void )
27360 {
27361 	static struct sockaddr_in		sMDNSMulticastAddrV4;
27362 	static dispatch_once_t			sMDNSMulticastAddrV4InitOnce = 0;
27363 
27364 	dispatch_once_f( &sMDNSMulticastAddrV4InitOnce, &sMDNSMulticastAddrV4, _MDNSMulticastAddrV4Init );
27365 	return( (const struct sockaddr *) &sMDNSMulticastAddrV4 );
27366 }
27367 
_MDNSMulticastAddrV4Init(void * inContext)27368 static void	_MDNSMulticastAddrV4Init( void *inContext )
27369 {
27370 	struct sockaddr_in * const		addr = (struct sockaddr_in *) inContext;
27371 
27372 	_SockAddrInitIPv4( addr, UINT32_C( 0xE00000FB ), kMDNSPort );	// The mDNS IPv4 multicast address is 224.0.0.251.
27373 }
27374 
27375 //===========================================================================================================================
27376 //	GetMDNSMulticastAddrV6
27377 //===========================================================================================================================
27378 
27379 static void	_MDNSMulticastAddrV6Init( void *inContext );
27380 
GetMDNSMulticastAddrV6(void)27381 static const struct sockaddr *	GetMDNSMulticastAddrV6( void )
27382 {
27383 	static struct sockaddr_in6		sMDNSMulticastAddrV6;
27384 	static dispatch_once_t			sMDNSMulticastAddrV6InitOnce = 0;
27385 
27386 	dispatch_once_f( &sMDNSMulticastAddrV6InitOnce, &sMDNSMulticastAddrV6, _MDNSMulticastAddrV6Init );
27387 	return( (const struct sockaddr *) &sMDNSMulticastAddrV6 );
27388 }
27389 
_MDNSMulticastAddrV6Init(void * inContext)27390 static void	_MDNSMulticastAddrV6Init( void *inContext )
27391 {
27392 	struct sockaddr_in6 * const		addr = (struct sockaddr_in6 *) inContext;
27393 
27394 	memset( addr, 0, sizeof( *addr ) );
27395 	SIN6_LEN_SET( addr );
27396 	addr->sin6_family	= AF_INET6;
27397 	addr->sin6_port		= htons( kMDNSPort );
27398 	addr->sin6_addr.s6_addr[  0 ] = 0xFF;	// The mDNS IPv6 multicast address is FF02::FB.
27399 	addr->sin6_addr.s6_addr[  1 ] = 0x02;
27400 	addr->sin6_addr.s6_addr[ 15 ] = 0xFB;
27401 }
27402 
27403 //===========================================================================================================================
27404 //	CreateMulticastSocket
27405 //===========================================================================================================================
27406 
27407 static OSStatus
CreateMulticastSocket(const struct sockaddr * inAddr,int inPort,const char * inIfName,uint32_t inIfIndex,Boolean inJoin,int * outPort,SocketRef * outSock)27408 	CreateMulticastSocket(
27409 		const struct sockaddr *	inAddr,
27410 		int						inPort,
27411 		const char *			inIfName,
27412 		uint32_t				inIfIndex,
27413 		Boolean					inJoin,
27414 		int *					outPort,
27415 		SocketRef *				outSock )
27416 {
27417 	OSStatus		err;
27418 	SocketRef		sock	= kInvalidSocketRef;
27419 	const int		family	= inAddr->sa_family;
27420 	int				port;
27421 
27422 	require_action_quiet( ( family == AF_INET ) ||( family == AF_INET6 ), exit, err = kUnsupportedErr );
27423 
27424 	err = ServerSocketOpen( family, SOCK_DGRAM, IPPROTO_UDP, inPort, &port, kSocketBufferSize_DontSet, &sock );
27425 	require_noerr_quiet( err, exit );
27426 
27427 	err = SocketSetMulticastInterface( sock, inIfName, inIfIndex );
27428 	require_noerr_quiet( err, exit );
27429 
27430 	if( family == AF_INET )
27431 	{
27432 		err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
27433 		err = map_socket_noerr_errno( sock, err );
27434 		require_noerr_quiet( err, exit );
27435 	}
27436 	else
27437 	{
27438 		err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
27439 		err = map_socket_noerr_errno( sock, err );
27440 		require_noerr_quiet( err, exit );
27441 	}
27442 
27443 	if( inJoin )
27444 	{
27445 		err = SocketJoinMulticast( sock, inAddr, inIfName, inIfIndex );
27446 		require_noerr_quiet( err, exit );
27447 	}
27448 
27449 	if( outPort ) *outPort = port;
27450 	*outSock = sock;
27451 	sock = kInvalidSocketRef;
27452 
27453 exit:
27454 	ForgetSocket( &sock );
27455 	return( err );
27456 }
27457 
27458 //===========================================================================================================================
27459 //	DecimalTextToUInt32
27460 //===========================================================================================================================
27461 
DecimalTextToUInt32(const char * inSrc,const char * inEnd,uint32_t * outValue,const char ** outPtr)27462 static OSStatus	DecimalTextToUInt32( const char *inSrc, const char *inEnd, uint32_t *outValue, const char **outPtr )
27463 {
27464 	OSStatus			err;
27465 	uint64_t			value;
27466 	const char *		ptr = inSrc;
27467 
27468 	require_action_quiet( ( ptr < inEnd ) && isdigit_safe( *ptr ), exit, err = kMalformedErr );
27469 
27470 	value = (uint64_t)( *ptr++ - '0' );
27471 	if( value == 0 )
27472 	{
27473 		if( ( ptr < inEnd ) && isdigit_safe( *ptr ) )
27474 		{
27475 			err = kMalformedErr;
27476 			goto exit;
27477 		}
27478 	}
27479 	else
27480 	{
27481 		while( ( ptr < inEnd ) && isdigit_safe( *ptr ) )
27482 		{
27483 			value = ( value * 10 ) + (uint64_t)( *ptr++ - '0' );
27484 			require_action_quiet( value <= UINT32_MAX, exit, err = kRangeErr );
27485 		}
27486 	}
27487 
27488 	*outValue = (uint32_t) value;
27489 	if( outPtr ) *outPtr = ptr;
27490 	err = kNoErr;
27491 
27492 exit:
27493 	return( err );
27494 }
27495 
27496 //===========================================================================================================================
27497 //	CheckIntegerArgument
27498 //===========================================================================================================================
27499 
CheckIntegerArgument(int inArgValue,const char * inArgName,int inMin,int inMax)27500 static OSStatus	CheckIntegerArgument( int inArgValue, const char *inArgName, int inMin, int inMax )
27501 {
27502 	if( ( inArgValue >= inMin ) && ( inArgValue <= inMax ) ) return( kNoErr );
27503 
27504 	FPrintF( stderr, "error: Invalid %s: %d. Valid range is [%d, %d].\n", inArgName, inArgValue, inMin, inMax );
27505 	return( kRangeErr );
27506 }
27507 
27508 //===========================================================================================================================
27509 //	CheckDoubleArgument
27510 //===========================================================================================================================
27511 
CheckDoubleArgument(double inArgValue,const char * inArgName,double inMin,double inMax)27512 static OSStatus	CheckDoubleArgument( double inArgValue, const char *inArgName, double inMin, double inMax )
27513 {
27514 	if( ( inArgValue >= inMin ) && ( inArgValue <= inMax ) ) return( kNoErr );
27515 
27516 	FPrintF( stderr, "error: Invalid %s: %.1f. Valid range is [%.1f, %.1f].\n", inArgName, inArgValue, inMin, inMax );
27517 	return( kRangeErr );
27518 }
27519 
27520 //===========================================================================================================================
27521 //	CheckRootUser
27522 //===========================================================================================================================
27523 
CheckRootUser(void)27524 static OSStatus	CheckRootUser( void )
27525 {
27526 	if( geteuid() == 0 ) return( kNoErr );
27527 
27528 	FPrintF( stderr, "error: This command must to be run as root.\n" );
27529 	return( kPermissionErr );
27530 }
27531 
27532 //===========================================================================================================================
27533 //	_SpawnCommand
27534 //===========================================================================================================================
27535 
27536 extern char **		environ;
27537 
27538 static OSStatus
_SpawnCommand(pid_t * outPID,const char * inStdOutRedirect,const char * inStdErrRedirect,const char * inFormat,...)27539 	_SpawnCommand(
27540 		pid_t *			outPID,
27541 		const char *	inStdOutRedirect,
27542 		const char *	inStdErrRedirect,
27543 		const char *	inFormat,
27544 		... )
27545 {
27546 	OSStatus							err;
27547 	va_list								args;
27548 	char *								cmdStr		= NULL;
27549 	size_t								cmdLen;
27550 	const char *						cmdEnd;
27551 	char *								bufPtr		= NULL;
27552 	size_t								bufLen;
27553 	const char *						bufLim;
27554 	pid_t								pid;
27555 	const char *						src;
27556 	char *								dst;
27557 	char **								argArray	= NULL;
27558 	size_t								argCapacity, argCount;
27559 	posix_spawn_file_actions_t			actions;
27560 	posix_spawn_file_actions_t *		actionsPtr	= NULL;
27561 
27562 	// Create command string from format string and its arguments.
27563 
27564 	va_start( args, inFormat );
27565 	VASPrintF( &cmdStr, inFormat, args );
27566 	va_end( args );
27567 	require_action( cmdStr, exit, err = kUnknownErr );
27568 
27569 	cmdLen = strlen( cmdStr );
27570 	cmdEnd = &cmdStr[ cmdLen ];
27571 
27572 	// Allocate buffer for argument strings.
27573 	// In the worst-case scenario in terms of memory requirements, the only non-escaped white space is one non-escaped
27574 	// white space character between each pair of adjacent arguments. The amount of required memory is equal to the size
27575 	// of cmdStr.
27576 
27577 	bufLen = cmdLen + 1; // +1 for NUL terminator.
27578 	bufPtr = (char *) malloc( bufLen );
27579 	require_action( bufPtr, exit, err = kNoMemoryErr );
27580 	bufLim = &bufPtr[ bufLen ];
27581 
27582 	// Allocate initial argument array.
27583 
27584 	argCount	= 0;
27585 	argCapacity	= 8;
27586 	argArray = (char **) malloc( ( argCapacity + 1 ) * sizeof( *argArray ) ); // +1 for NULL arg.
27587 	require_action( argArray, exit, err = kNoMemoryErr );
27588 
27589 	// Extract argument strings from command string.
27590 
27591 	src = cmdStr;
27592 	dst = bufPtr;
27593 	for( ;; )
27594 	{
27595 		size_t		maxLen, copiedLen, totalLen;
27596 		Boolean		more;
27597 
27598 		maxLen = (size_t)( bufLim - dst );
27599 		if( maxLen > 0 ) --maxLen; // -1 for NUL terminator.
27600 		more = _ParseQuotedEscapedString( src, cmdEnd, kWhiteSpaceCharSet, dst, maxLen, &copiedLen, &totalLen, &src );
27601 		if( !more ) break;
27602 		require_fatal( copiedLen == totalLen, "Incorrect assumption about maximum required buffer space." );
27603 
27604 		if( argCount >= argCapacity )
27605 		{
27606 			size_t		newCapactiy;
27607 			char **		newArray;
27608 
27609 			newCapactiy = 2 * argCapacity;
27610 			newArray = (char **) realloc( argArray, ( newCapactiy + 1 ) * sizeof( *newArray ) ); // +1 for NULL arg.
27611 			require_action( newArray, exit, err = kNoMemoryErr );
27612 
27613 			argArray	= newArray;
27614 			argCapacity	= newCapactiy;
27615 		}
27616 		argArray[ argCount++ ] = dst;
27617 		dst += copiedLen;
27618 		*dst++ = '\0';
27619 		check( dst <= bufLim );
27620 	}
27621 	require_action_quiet( argCount > 0, exit, err = kCommandErr );
27622 
27623 	argArray[ argCount ] = NULL;
27624 
27625 	// Set up stdout and stderr redirections, if any.
27626 
27627 	if( inStdOutRedirect || inStdErrRedirect )
27628 	{
27629 		err = posix_spawn_file_actions_init( &actions );
27630 		require_noerr( err, exit );
27631 
27632 		actionsPtr = &actions;
27633 		if( inStdOutRedirect )
27634 		{
27635 			err = posix_spawn_file_actions_addopen( actionsPtr, STDOUT_FILENO, inStdOutRedirect, O_WRONLY, 0 );
27636 			require_noerr( err, exit );
27637 		}
27638 		if( inStdErrRedirect )
27639 		{
27640 			err = posix_spawn_file_actions_addopen( actionsPtr, STDERR_FILENO, inStdErrRedirect, O_WRONLY, 0 );
27641 			require_noerr( err, exit );
27642 		}
27643 	}
27644 	// Spawn command.
27645 
27646 	err = posix_spawnp( &pid, argArray[ 0 ], actionsPtr, NULL, argArray, environ );
27647 	require_noerr_quiet( err, exit );
27648 
27649 	if( outPID ) *outPID = pid;
27650 
27651 exit:
27652 	FreeNullSafe( cmdStr );
27653 	FreeNullSafe( bufPtr );
27654 	FreeNullSafe( argArray );
27655 	if( actionsPtr ) posix_spawn_file_actions_destroy( actionsPtr );
27656 	return( err );
27657 }
27658 
27659 //===========================================================================================================================
27660 //	OutputFormatFromArgString
27661 //===========================================================================================================================
27662 
OutputFormatFromArgString(const char * inArgString,OutputFormatType * outFormat)27663 static OSStatus	OutputFormatFromArgString( const char *inArgString, OutputFormatType *outFormat )
27664 {
27665 	OSStatus				err;
27666 	OutputFormatType		format;
27667 
27668 	format = (OutputFormatType) CLIArgToValue( "format", inArgString, &err,
27669 		kOutputFormatStr_JSON,		kOutputFormatType_JSON,
27670 		kOutputFormatStr_XML,		kOutputFormatType_XML,
27671 		kOutputFormatStr_Binary,	kOutputFormatType_Binary,
27672 		NULL );
27673 	if( outFormat ) *outFormat = format;
27674 	return( err );
27675 }
27676 
27677 //===========================================================================================================================
27678 //	OutputPropertyList
27679 //===========================================================================================================================
27680 
OutputPropertyList(CFPropertyListRef inPList,OutputFormatType inType,const char * inOutputFilePath)27681 static OSStatus	OutputPropertyList( CFPropertyListRef inPList, OutputFormatType inType, const char *inOutputFilePath )
27682 {
27683 	OSStatus		err;
27684 	CFDataRef		results = NULL;
27685 	FILE *			file	= NULL;
27686 
27687 	// Convert plist to a specific format.
27688 
27689 	switch( inType )
27690 	{
27691 		case kOutputFormatType_JSON:
27692 			results = CFCreateJSONData( inPList, kJSONFlags_None, NULL );
27693 			require_action( results, exit, err = kUnknownErr );
27694 			break;
27695 
27696 		case kOutputFormatType_XML:
27697 			results = CFPropertyListCreateData( NULL, inPList, kCFPropertyListXMLFormat_v1_0, 0, NULL );
27698 			require_action( results, exit, err = kUnknownErr );
27699 			break;
27700 
27701 		case kOutputFormatType_Binary:
27702 			results = CFPropertyListCreateData( NULL, inPList, kCFPropertyListBinaryFormat_v1_0, 0, NULL );
27703 			require_action( results, exit, err = kUnknownErr );
27704 			break;
27705 
27706 		default:
27707 			err = kTypeErr;
27708 			goto exit;
27709 	}
27710 
27711 	// Write formatted results to file or stdout.
27712 
27713 	if( inOutputFilePath )
27714 	{
27715 		file = fopen( inOutputFilePath, "wb" );
27716 		err = map_global_value_errno( file, file );
27717 		require_noerr( err, exit );
27718 	}
27719 	else
27720 	{
27721 		file = stdout;
27722 	}
27723 
27724 	err = WriteANSIFile( file, CFDataGetBytePtr( results ), (size_t) CFDataGetLength( results ) );
27725 	require_noerr_quiet( err, exit );
27726 
27727 	// Write a trailing newline for JSON-formatted results.
27728 
27729 	if( inType == kOutputFormatType_JSON )
27730 	{
27731 		err = WriteANSIFile( file, "\n", 1 );
27732 		require_noerr_quiet( err, exit );
27733 	}
27734 
27735 exit:
27736 	if( file && ( file != stdout ) ) fclose( file );
27737 	CFReleaseNullSafe( results );
27738 	return( err );
27739 }
27740 
27741 //===========================================================================================================================
27742 //	CreateSRVRecordDataFromString
27743 //===========================================================================================================================
27744 
CreateSRVRecordDataFromString(const char * inString,uint8_t ** outPtr,size_t * outLen)27745 static OSStatus	CreateSRVRecordDataFromString( const char *inString, uint8_t **outPtr, size_t *outLen )
27746 {
27747 	OSStatus			err;
27748 	DataBuffer			dataBuf;
27749 	const char *		ptr;
27750 	int					i;
27751 	uint8_t *			end;
27752 	uint8_t				target[ kDomainNameLengthMax ];
27753 
27754 	DataBuffer_Init( &dataBuf, NULL, 0, ( 3 * 2 ) + kDomainNameLengthMax );
27755 
27756 	// Parse and set the priority, weight, and port values (all three are unsigned 16-bit values).
27757 
27758 	ptr = inString;
27759 	for( i = 0; i < 3; ++i )
27760 	{
27761 		char *		next;
27762 		long		value;
27763 		uint8_t		buf[ 2 ];
27764 
27765 		value = strtol( ptr, &next, 0 );
27766 		require_action_quiet( ( next != ptr ) && ( *next == ',' ), exit, err = kMalformedErr );
27767 		require_action_quiet( ( value >= 0 ) && ( value <= UINT16_MAX ), exit, err = kRangeErr );
27768 		ptr = next + 1;
27769 
27770 		WriteBig16( buf, value );
27771 
27772 		err = DataBuffer_Append( &dataBuf, buf, sizeof( buf ) );
27773 		require_noerr( err, exit );
27774 	}
27775 
27776 	// Set the target domain name.
27777 
27778 	err = DomainNameFromString( target, ptr, &end );
27779     require_noerr_quiet( err, exit );
27780 
27781 	err = DataBuffer_Append( &dataBuf, target, (size_t)( end - target ) );
27782 	require_noerr( err, exit );
27783 
27784 	err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
27785 	require_noerr( err, exit );
27786 
27787 exit:
27788 	DataBuffer_Free( &dataBuf );
27789 	return( err );
27790 }
27791 
27792 //===========================================================================================================================
27793 //	CreateTXTRecordDataFromString
27794 //===========================================================================================================================
27795 
CreateTXTRecordDataFromString(const char * inString,int inDelimiter,uint8_t ** outPtr,size_t * outLen)27796 static OSStatus	CreateTXTRecordDataFromString(const char *inString, int inDelimiter, uint8_t **outPtr, size_t *outLen )
27797 {
27798 	OSStatus			err;
27799 	DataBuffer			dataBuf;
27800 	const char *		src;
27801 	uint8_t				txtStr[ 256 ];	// Buffer for single TXT string: 1 length byte + up to 255 bytes of data.
27802 
27803 	DataBuffer_Init( &dataBuf, NULL, 0, kDNSRecordDataLengthMax );
27804 
27805 	src = inString;
27806 	for( ;; )
27807 	{
27808 		uint8_t *					dst = &txtStr[ 1 ];
27809 		const uint8_t * const		lim = &txtStr[ 256 ];
27810 		int							c;
27811 
27812 		while( *src && ( *src != inDelimiter ) )
27813 		{
27814 			if( ( c = *src++ ) == '\\' )
27815 			{
27816 				require_action_quiet( *src != '\0', exit, err = kUnderrunErr );
27817 				c = *src++;
27818 			}
27819 			require_action_quiet( dst < lim, exit, err = kOverrunErr );
27820 			*dst++ = (uint8_t) c;
27821 		}
27822 		txtStr[ 0 ] = (uint8_t)( dst - &txtStr[ 1 ] );
27823 		err = DataBuffer_Append( &dataBuf, txtStr, 1 + txtStr[ 0 ] );
27824 		require_noerr( err, exit );
27825 
27826 		if( *src == '\0' ) break;
27827 		++src;
27828 	}
27829 
27830 	err = DataBuffer_Detach( &dataBuf, outPtr, outLen );
27831 	require_noerr( err, exit );
27832 
27833 exit:
27834 	DataBuffer_Free( &dataBuf );
27835 	return( err );
27836 }
27837 
27838 //===========================================================================================================================
27839 //	CreateNSECRecordData
27840 //===========================================================================================================================
27841 
27842 DECLARE_QSORT_NUMERIC_COMPARATOR( _QSortCmpUnsigned );
DEFINE_QSORT_NUMERIC_COMPARATOR(unsigned int,_QSortCmpUnsigned)27843 DEFINE_QSORT_NUMERIC_COMPARATOR( unsigned int, _QSortCmpUnsigned )
27844 
27845 #define kNSECBitmapMaxLength		32	// 32 bytes (256 bits). See <https://tools.ietf.org/html/rfc4034#section-4.1.2>.
27846 
27847 static OSStatus
27848 	CreateNSECRecordData(
27849 		const uint8_t *	inNextDomainName,
27850 		uint8_t **		outPtr,
27851 		size_t *		outLen,
27852 		unsigned int	inTypeCount,
27853 		... )
27854 {
27855 	OSStatus			err;
27856 	va_list				args;
27857 	DataBuffer			rdataDB;
27858 	unsigned int *		array	= NULL;
27859 	unsigned int		i, type, maxBit, currBlock, bitmapLen;
27860 	uint8_t				fields[ 2 + kNSECBitmapMaxLength ];
27861 	uint8_t * const		bitmap	= &fields[ 2 ];
27862 
27863 	va_start( args, inTypeCount );
27864 	DataBuffer_Init( &rdataDB, NULL, 0, kDNSRecordDataLengthMax );
27865 
27866 	// Append Next Domain Name.
27867 
27868 	err = DataBuffer_Append( &rdataDB, inNextDomainName, DomainNameLength( inNextDomainName ) );
27869 	require_noerr( err, exit );
27870 
27871 	// Append Type Bit Maps.
27872 
27873 	maxBit = 0;
27874 	memset( bitmap, 0, kNSECBitmapMaxLength );
27875 	if( inTypeCount > 0 )
27876 	{
27877 		array = (unsigned int *) malloc( inTypeCount * sizeof_element( array ) );
27878 		require_action( array, exit, err = kNoMemoryErr );
27879 
27880 		for( i = 0; i < inTypeCount; ++i )
27881 		{
27882 			type = va_arg( args, unsigned int );
27883 			require_action_quiet( type <= UINT16_MAX, exit, err = kRangeErr );
27884 			array[ i ] = type;
27885 		}
27886 		qsort( array, inTypeCount, sizeof_element( array ), _QSortCmpUnsigned );
27887 
27888 		currBlock = array[ 0 ] / 256;
27889 		for( i = 0; i < inTypeCount; ++i )
27890 		{
27891 			const unsigned int		block	= array[ i ] / 256;
27892 			const unsigned int		bit		= array[ i ] % 256;
27893 
27894 			if( block != currBlock )
27895 			{
27896 				bitmapLen	= BitArray_MaxBytes( maxBit + 1 );
27897 				fields[ 0 ] = (uint8_t) currBlock;
27898 				fields[ 1 ] = (uint8_t) bitmapLen;
27899 
27900 				err = DataBuffer_Append( &rdataDB, fields, 2 + bitmapLen );
27901 				require_noerr( err, exit );
27902 
27903 				maxBit		= 0;
27904 				currBlock	= block;
27905 				memset( bitmap, 0, bitmapLen );
27906 			}
27907 			BitArray_SetBit( bitmap, bit );
27908 			if( bit > maxBit ) maxBit = bit;
27909 		}
27910 	}
27911 	else
27912 	{
27913 		currBlock = 0;
27914 	}
27915 
27916 	bitmapLen	= BitArray_MaxBytes( maxBit + 1 );
27917 	fields[ 0 ] = (uint8_t) currBlock;
27918 	fields[ 1 ] = (uint8_t) bitmapLen;
27919 
27920 	err = DataBuffer_Append( &rdataDB, fields, 2 + bitmapLen );
27921 	require_noerr( err, exit );
27922 
27923 	err = DataBuffer_Detach( &rdataDB, outPtr, outLen );
27924 	require_noerr( err, exit );
27925 
27926 exit:
27927 	va_end( args );
27928 	DataBuffer_Free( &rdataDB );
27929 	FreeNullSafe( array );
27930 	return( err );
27931 }
27932 
27933 //===========================================================================================================================
27934 //	AppendSOARecord
27935 //===========================================================================================================================
27936 
27937 static OSStatus
27938 	_AppendSOARecordData(
27939 		DataBuffer *	inDB,
27940 		const uint8_t *	inMName,
27941 		const uint8_t *	inRName,
27942 		uint32_t		inSerial,
27943 		uint32_t		inRefresh,
27944 		uint32_t		inRetry,
27945 		uint32_t		inExpire,
27946 		uint32_t		inMinimumTTL,
27947 		size_t *		outLen );
27948 
27949 static OSStatus
AppendSOARecord(DataBuffer * inDB,const uint8_t * inNamePtr,size_t inNameLen,uint16_t inType,uint16_t inClass,uint32_t inTTL,const uint8_t * inMName,const uint8_t * inRName,uint32_t inSerial,uint32_t inRefresh,uint32_t inRetry,uint32_t inExpire,uint32_t inMinimumTTL,size_t * outLen)27950 	AppendSOARecord(
27951 		DataBuffer *	inDB,
27952 		const uint8_t *	inNamePtr,
27953 		size_t			inNameLen,
27954 		uint16_t		inType,
27955 		uint16_t		inClass,
27956 		uint32_t		inTTL,
27957 		const uint8_t *	inMName,
27958 		const uint8_t *	inRName,
27959 		uint32_t		inSerial,
27960 		uint32_t		inRefresh,
27961 		uint32_t		inRetry,
27962 		uint32_t		inExpire,
27963 		uint32_t		inMinimumTTL,
27964 		size_t *		outLen )
27965 {
27966 	OSStatus		err;
27967 	size_t			rdataLen;
27968 	size_t			rdlengthOffset = 0;
27969 	uint8_t *		rdlengthPtr;
27970 
27971 	if( inDB )
27972 	{
27973 		err = _DataBuffer_AppendDNSRecord( inDB, inNamePtr, inNameLen, inType, inClass, inTTL, NULL, 0 );
27974 		require_noerr( err, exit );
27975 
27976 		rdlengthOffset = DataBuffer_GetLen( inDB ) - 2;
27977 	}
27978 
27979 	err = _AppendSOARecordData( inDB, inMName, inRName, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL, &rdataLen );
27980 	require_noerr( err, exit );
27981 
27982 	if( inDB )
27983 	{
27984 		rdlengthPtr = DataBuffer_GetPtr( inDB ) + rdlengthOffset;
27985 		WriteBig16( rdlengthPtr, rdataLen );
27986 	}
27987 
27988 	if( outLen ) *outLen = inNameLen + sizeof( dns_fixed_fields_record ) + rdataLen;
27989 	err = kNoErr;
27990 
27991 exit:
27992 	return( err );
27993 }
27994 
27995 static OSStatus
_AppendSOARecordData(DataBuffer * inDB,const uint8_t * inMName,const uint8_t * inRName,uint32_t inSerial,uint32_t inRefresh,uint32_t inRetry,uint32_t inExpire,uint32_t inMinimumTTL,size_t * outLen)27996 	_AppendSOARecordData(
27997 		DataBuffer *	inDB,
27998 		const uint8_t *	inMName,
27999 		const uint8_t *	inRName,
28000 		uint32_t		inSerial,
28001 		uint32_t		inRefresh,
28002 		uint32_t		inRetry,
28003 		uint32_t		inExpire,
28004 		uint32_t		inMinimumTTL,
28005 		size_t *		outLen )
28006 {
28007 	OSStatus					err;
28008 	dns_fixed_fields_soa		fields;
28009 	const size_t				mnameLen = DomainNameLength( inMName );
28010 	const size_t				rnameLen = DomainNameLength( inRName );
28011 
28012 	if( inDB )
28013 	{
28014 		err = DataBuffer_Append( inDB, inMName, mnameLen );
28015 		require_noerr( err, exit );
28016 
28017 		err = DataBuffer_Append( inDB, inRName, rnameLen );
28018 		require_noerr( err, exit );
28019 
28020 		dns_fixed_fields_soa_init( &fields, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL );
28021 		err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
28022 		require_noerr( err, exit );
28023 	}
28024 	if( outLen ) *outLen = mnameLen + rnameLen + sizeof( fields );
28025 	err = kNoErr;
28026 
28027 exit:
28028 	return( err );
28029 }
28030 
28031 //===========================================================================================================================
28032 //	CreateSOARecordData
28033 //===========================================================================================================================
28034 
28035 static OSStatus
CreateSOARecordData(const uint8_t * inMName,const uint8_t * inRName,uint32_t inSerial,uint32_t inRefresh,uint32_t inRetry,uint32_t inExpire,uint32_t inMinimumTTL,uint8_t ** outPtr,size_t * outLen)28036 	CreateSOARecordData(
28037 		const uint8_t *	inMName,
28038 		const uint8_t *	inRName,
28039 		uint32_t		inSerial,
28040 		uint32_t		inRefresh,
28041 		uint32_t		inRetry,
28042 		uint32_t		inExpire,
28043 		uint32_t		inMinimumTTL,
28044 		uint8_t **		outPtr,
28045 		size_t *		outLen )
28046 {
28047 	OSStatus		err;
28048 	DataBuffer		rdataDB;
28049 
28050 	DataBuffer_Init( &rdataDB, NULL, 0, kDNSRecordDataLengthMax );
28051 
28052 	err = _AppendSOARecordData( &rdataDB, inMName, inRName, inSerial, inRefresh, inRetry, inExpire, inMinimumTTL, NULL );
28053 	require_noerr( err, exit );
28054 
28055 	err = DataBuffer_Detach( &rdataDB, outPtr, outLen );
28056 	require_noerr( err, exit );
28057 
28058 exit:
28059 	DataBuffer_Free( &rdataDB );
28060 	return( err );
28061 }
28062 
28063 //===========================================================================================================================
28064 //	_DataBuffer_AppendDNSQuestion
28065 //===========================================================================================================================
28066 
28067 static OSStatus
_DataBuffer_AppendDNSQuestion(DataBuffer * inDB,const uint8_t * inNamePtr,size_t inNameLen,uint16_t inType,uint16_t inClass)28068 	_DataBuffer_AppendDNSQuestion(
28069 		DataBuffer *	inDB,
28070 		const uint8_t *	inNamePtr,
28071 		size_t			inNameLen,
28072 		uint16_t		inType,
28073 		uint16_t		inClass )
28074 {
28075 	OSStatus						err;
28076 	dns_fixed_fields_question		fields;
28077 
28078 	err = DataBuffer_Append( inDB, inNamePtr, inNameLen );
28079 	require_noerr( err, exit );
28080 
28081 	dns_fixed_fields_question_init( &fields, inType, inClass );
28082 	err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
28083 	require_noerr( err, exit );
28084 
28085 exit:
28086 	return( err );
28087 }
28088 
28089 //===========================================================================================================================
28090 //	_DataBuffer_AppendDNSRecord
28091 //===========================================================================================================================
28092 
28093 static OSStatus
_DataBuffer_AppendDNSRecord(DataBuffer * inDB,const uint8_t * inNamePtr,size_t inNameLen,uint16_t inType,uint16_t inClass,uint32_t inTTL,const uint8_t * inRDataPtr,size_t inRDataLen)28094 	_DataBuffer_AppendDNSRecord(
28095 		DataBuffer *	inDB,
28096 		const uint8_t *	inNamePtr,
28097 		size_t			inNameLen,
28098 		uint16_t		inType,
28099 		uint16_t		inClass,
28100 		uint32_t		inTTL,
28101 		const uint8_t *	inRDataPtr,
28102 		size_t			inRDataLen )
28103 {
28104 	OSStatus					err;
28105 	dns_fixed_fields_record		fields;
28106 
28107 	require_action_quiet( inRDataLen < kDNSRecordDataLengthMax, exit, err = kSizeErr );
28108 
28109 	err = DataBuffer_Append( inDB, inNamePtr, inNameLen );
28110 	require_noerr( err, exit );
28111 
28112 	dns_fixed_fields_record_init( &fields, inType, inClass, inTTL, (uint16_t) inRDataLen );
28113 	err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
28114 	require_noerr( err, exit );
28115 
28116 	if( inRDataPtr )
28117 	{
28118 		err = DataBuffer_Append( inDB, inRDataPtr, inRDataLen );
28119 		require_noerr( err, exit );
28120 	}
28121 
28122 exit:
28123 	return( err );
28124 }
28125 
28126 //===========================================================================================================================
28127 //	_NanoTime64ToTimestamp
28128 //===========================================================================================================================
28129 
_NanoTime64ToTimestamp(NanoTime64 inTime,char * inBuf,size_t inMaxLen)28130 static char *	_NanoTime64ToTimestamp( NanoTime64 inTime, char *inBuf, size_t inMaxLen )
28131 {
28132 	struct  timeval		tv;
28133 
28134 	NanoTimeToTimeVal( inTime, &tv );
28135 	return( MakeFractionalDateString( &tv, inBuf, inMaxLen ) );
28136 }
28137 
28138 //===========================================================================================================================
28139 //	_MDNSInterfaceListCreate
28140 //===========================================================================================================================
28141 
28142 static Boolean	_MDNSInterfaceIsBlacklisted( SocketRef inInfoSock, const char *inIfName );
28143 
_MDNSInterfaceListCreate(MDNSInterfaceSubset inSubset,size_t inItemSize,MDNSInterfaceItem ** outList)28144 static OSStatus	_MDNSInterfaceListCreate( MDNSInterfaceSubset inSubset, size_t inItemSize, MDNSInterfaceItem **outList )
28145 {
28146 	OSStatus					err;
28147 	struct ifaddrs *			ifaList;
28148 	const struct ifaddrs *		ifa;
28149 	MDNSInterfaceItem *			interfaceList;
28150 	MDNSInterfaceItem **		ptr;
28151 	SocketRef					infoSock;
28152 
28153 	ifaList			= NULL;
28154 	interfaceList	= NULL;
28155 	infoSock		= kInvalidSocketRef;
28156 	if( inItemSize == 0 ) inItemSize = sizeof( MDNSInterfaceItem );
28157 	require_action_quiet( inItemSize >= sizeof( MDNSInterfaceItem ), exit, err = kSizeErr );
28158 
28159 	infoSock = socket( AF_INET, SOCK_DGRAM, 0 );
28160 	err = map_socket_creation_errno( infoSock );
28161 	require_noerr( err, exit );
28162 
28163 	err = getifaddrs( &ifaList );
28164 	err = map_global_noerr_errno( err );
28165 	require_noerr( err, exit );
28166 
28167 	ptr = &interfaceList;
28168 	for( ifa = ifaList; ifa; ifa = ifa->ifa_next )
28169 	{
28170 		MDNSInterfaceItem *		item;
28171 		int						family;
28172 		const unsigned int		flagsMask	= IFF_UP | IFF_MULTICAST | IFF_POINTOPOINT;
28173 		const unsigned int		flagsNeeded	= IFF_UP | IFF_MULTICAST;
28174 
28175 		if( ( ifa->ifa_flags & flagsMask ) != flagsNeeded )		continue;
28176 		if( !ifa->ifa_addr || !ifa->ifa_name )					continue;
28177 		family = ifa->ifa_addr->sa_family;
28178 		if( ( family != AF_INET ) && ( family != AF_INET6 ) )	continue;
28179 
28180 		for( item = interfaceList; item && ( strcmp( item->ifName, ifa->ifa_name ) != 0 ); item = item->next ) {}
28181 		if( !item )
28182 		{
28183 			NetTransportType		type;
28184 			uint32_t				ifIndex;
28185 			const char * const		ifName = ifa->ifa_name;
28186 
28187 			if( _MDNSInterfaceIsBlacklisted( infoSock, ifName ) ) continue;
28188 			err = SocketGetInterfaceInfo( infoSock, ifName, NULL, &ifIndex, NULL, NULL, NULL, NULL, NULL, &type );
28189 			require_noerr( err, exit );
28190 
28191 			if( ifIndex == 0 ) continue;
28192 			if( type == kNetTransportType_AWDL )
28193 			{
28194 				if( inSubset == kMDNSInterfaceSubset_NonAWDL ) continue;
28195 			}
28196 			else
28197 			{
28198 				if( inSubset == kMDNSInterfaceSubset_AWDL ) continue;
28199 			}
28200 			item = (MDNSInterfaceItem *) calloc( 1, inItemSize );
28201 			require_action( item, exit, err = kNoMemoryErr );
28202 
28203 			*ptr =  item;
28204 			 ptr = &item->next;
28205 
28206 			item->ifName = strdup( ifName );
28207 			require_action( item->ifName, exit, err = kNoMemoryErr );
28208 
28209 			item->ifIndex = ifIndex;
28210 			if(      type == kNetTransportType_AWDL ) item->isAWDL = true;
28211 			else if( type == kNetTransportType_WiFi ) item->isWiFi = true;
28212 		}
28213 		if( family == AF_INET )	item->hasIPv4 = true;
28214 		else					item->hasIPv6 = true;
28215 	}
28216 	require_action_quiet( interfaceList, exit, err = kNotFoundErr );
28217 
28218 	if( outList )
28219 	{
28220 		*outList = interfaceList;
28221 		interfaceList = NULL;
28222 	}
28223 
28224 exit:
28225 	if( ifaList ) freeifaddrs( ifaList );
28226 	_MDNSInterfaceListFree( interfaceList );
28227 	ForgetSocket( &infoSock );
28228 	return( err );
28229 }
28230 
_MDNSInterfaceIsBlacklisted(SocketRef inInfoSock,const char * inIfName)28231 static Boolean	_MDNSInterfaceIsBlacklisted( SocketRef inInfoSock, const char *inIfName )
28232 {
28233 	OSStatus						err;
28234 	int								i;
28235 	static const char * const		kMDNSInterfacePrefixBlacklist[] = { "llw", "nan" };
28236 	struct ifreq					ifr;
28237 
28238 	// Check if the interface name's prefix matches the prefix blacklist.
28239 
28240 	for( i = 0; i < (int) countof( kMDNSInterfacePrefixBlacklist ); ++i )
28241 	{
28242 		const char * const		prefix	= kMDNSInterfacePrefixBlacklist[ i ];
28243 
28244         if( strcmp_prefix( inIfName, prefix ) == 0 )
28245 		{
28246 			const char *		ptr = &inIfName[ strlen( prefix ) ];
28247 
28248 			while( isdigit_safe( *ptr ) ) ++ptr;
28249 			if( *ptr == '\0' ) return( true );
28250 		}
28251 	}
28252 
28253 	// Check if the interface is used for inter-(co)processor networking.
28254 
28255 	memset( &ifr, 0, sizeof( ifr ) );
28256 	strlcpy( ifr.ifr_name, inIfName, sizeof( ifr.ifr_name ) );
28257 	err = ioctl( inInfoSock, SIOCGIFFUNCTIONALTYPE, &ifr );
28258 	err = map_global_value_errno( err != -1, err );
28259 	if( !err && ( ifr.ifr_functional_type == IFRTYPE_FUNCTIONAL_INTCOPROC ) ) return( true );
28260 
28261 	return( false );
28262 }
28263 
28264 //===========================================================================================================================
28265 //	_MDNSInterfaceListFree
28266 //===========================================================================================================================
28267 
_MDNSInterfaceListFree(MDNSInterfaceItem * inList)28268 static void	_MDNSInterfaceListFree( MDNSInterfaceItem *inList )
28269 {
28270 	MDNSInterfaceItem *		item;
28271 
28272 	while( ( item = inList ) != NULL )
28273 	{
28274 		inList = item->next;
28275 		FreeNullSafe( item->ifName );
28276 		free( item );
28277 	}
28278 }
28279 
28280 //===========================================================================================================================
28281 //	_MDNSInterfaceGetAny
28282 //===========================================================================================================================
28283 
_MDNSInterfaceGetAny(MDNSInterfaceSubset inSubset,char inNameBuf[IF_NAMESIZE+1],uint32_t * outIndex)28284 static OSStatus	_MDNSInterfaceGetAny( MDNSInterfaceSubset inSubset, char inNameBuf[ IF_NAMESIZE + 1 ], uint32_t *outIndex )
28285 {
28286 	OSStatus						err;
28287 	MDNSInterfaceItem *				list;
28288 	const MDNSInterfaceItem *		item;
28289 
28290 	list = NULL;
28291 	err = _MDNSInterfaceListCreate( inSubset, 0, &list );
28292 	require_noerr_quiet( err, exit );
28293 	require_action_quiet( list, exit, err = kNotFoundErr );
28294 
28295 	for( item = list; item; item = item->next )
28296 	{
28297 		if( item->hasIPv4 && item->hasIPv6 ) break;
28298 	}
28299 	if( !item ) item = list;
28300 	if( inNameBuf )	strlcpy( inNameBuf, item->ifName, IF_NAMESIZE + 1 );
28301 	if( outIndex ) *outIndex = item->ifIndex;
28302 
28303 exit:
28304 	_MDNSInterfaceListFree( list );
28305 	return( err );
28306 }
28307 
28308 //===========================================================================================================================
28309 //	_SetComputerName
28310 //===========================================================================================================================
28311 
_SetComputerName(CFStringRef inComputerName,CFStringEncoding inEncoding)28312 static OSStatus	_SetComputerName( CFStringRef inComputerName, CFStringEncoding inEncoding )
28313 {
28314 	OSStatus				err;
28315 	SCPreferencesRef		prefs;
28316 	Boolean					ok;
28317 
28318 	prefs = SCPreferencesCreateWithAuthorization( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
28319 	err = map_scerror( prefs );
28320 	require_noerr_quiet( err, exit );
28321 
28322 	ok = SCPreferencesSetComputerName( prefs, inComputerName, inEncoding );
28323 	err = map_scerror( ok );
28324 	require_noerr_quiet( err, exit );
28325 
28326 	ok = SCPreferencesCommitChanges( prefs );
28327 	err = map_scerror( ok );
28328 	require_noerr_quiet( err, exit );
28329 
28330 	ok = SCPreferencesApplyChanges( prefs );
28331 	err = map_scerror( ok );
28332 	require_noerr_quiet( err, exit );
28333 
28334 exit:
28335 	CFReleaseNullSafe( prefs );
28336 	return( err );
28337 }
28338 
28339 //===========================================================================================================================
28340 //	_SetComputerNameWithUTF8CString
28341 //===========================================================================================================================
28342 
_SetComputerNameWithUTF8CString(const char * inComputerName)28343 static OSStatus	_SetComputerNameWithUTF8CString( const char *inComputerName )
28344 {
28345 	OSStatus		err;
28346 	CFStringRef		computerName;
28347 
28348 	computerName = CFStringCreateWithCString( NULL, inComputerName, kCFStringEncodingUTF8 );
28349 	require_action( computerName, exit, err = kNoMemoryErr );
28350 
28351 	err = _SetComputerName( computerName, kCFStringEncodingUTF8 );
28352 	require_noerr_quiet( err, exit );
28353 
28354 exit:
28355 	CFReleaseNullSafe( computerName );
28356 	return( err );
28357 }
28358 
28359 //===========================================================================================================================
28360 //	_SetLocalHostName
28361 //===========================================================================================================================
28362 
_SetLocalHostName(CFStringRef inLocalHostName)28363 static OSStatus	_SetLocalHostName( CFStringRef inLocalHostName )
28364 {
28365 	OSStatus				err;
28366 	SCPreferencesRef		prefs;
28367 	Boolean					ok;
28368 
28369 	prefs = SCPreferencesCreateWithAuthorization( NULL, CFSTR( kDNSSDUtilIdentifier ), NULL, NULL );
28370 	err = map_scerror( prefs );
28371 	require_noerr_quiet( err, exit );
28372 
28373 	ok = SCPreferencesSetLocalHostName( prefs, inLocalHostName );
28374 	err = map_scerror( ok );
28375 	require_noerr_quiet( err, exit );
28376 
28377 	ok = SCPreferencesCommitChanges( prefs );
28378 	err = map_scerror( ok );
28379 	require_noerr_quiet( err, exit );
28380 
28381 	ok = SCPreferencesApplyChanges( prefs );
28382 	err = map_scerror( ok );
28383 	require_noerr_quiet( err, exit );
28384 
28385 exit:
28386 	CFReleaseNullSafe( prefs );
28387 	return( err );
28388 }
28389 
28390 //===========================================================================================================================
28391 //	_SetLocalHostNameWithUTF8CString
28392 //===========================================================================================================================
28393 
_SetLocalHostNameWithUTF8CString(const char * inLocalHostName)28394 static OSStatus	_SetLocalHostNameWithUTF8CString( const char *inLocalHostName )
28395 {
28396 	OSStatus		err;
28397 	CFStringRef		localHostName;
28398 
28399 	localHostName = CFStringCreateWithCString( NULL, inLocalHostName, kCFStringEncodingUTF8 );
28400 	require_action( localHostName, exit, err = kNoMemoryErr );
28401 
28402 	err = _SetLocalHostName( localHostName );
28403 	require_noerr_quiet( err, exit );
28404 
28405 exit:
28406 	CFReleaseNullSafe( localHostName );
28407 	return( err );
28408 }
28409 
28410 #if( TARGET_OS_DARWIN )
28411 //===========================================================================================================================
28412 //	_InterfaceIPv6AddressAdd
28413 //===========================================================================================================================
28414 
_InterfaceIPv6AddressAdd(const char * inIfName,uint8_t inAddr[STATIC_PARAM16],int inMaskBitLen)28415 static OSStatus	_InterfaceIPv6AddressAdd( const char *inIfName, uint8_t inAddr[ STATIC_PARAM 16 ], int inMaskBitLen )
28416 {
28417 	OSStatus					err;
28418 	SocketRef					infoSock = kInvalidSocketRef;
28419 	struct in6_aliasreq			ifra;
28420 	size_t						len;
28421 	struct sockaddr_in6 *		sin6;
28422 	int							wholeBytes, remainingBits;
28423 
28424 	require_action_quiet( ( inMaskBitLen >= 0 ) &&  ( inMaskBitLen <= 128 ), exit, err = kSizeErr );
28425 
28426 	infoSock = socket( AF_INET6, SOCK_DGRAM, 0 );
28427 	err = map_socket_creation_errno( infoSock );
28428 	require_noerr( err, exit );
28429 
28430 	// Set interface name.
28431 
28432 	memset( &ifra, 0, sizeof( ifra ) );
28433 	len = strlcpy( ifra.ifra_name, inIfName, sizeof( ifra.ifra_name ) );
28434 	require_action_quiet( len < sizeof( ifra.ifra_name ), exit, err = kSizeErr );
28435 
28436 	// Set IPv6 address.
28437 
28438 	sin6 = &ifra.ifra_addr;
28439 	SIN6_LEN_SET( sin6 );
28440 	sin6->sin6_family = AF_INET6;
28441 	memcpy( sin6->sin6_addr.s6_addr, inAddr, 16 );
28442 
28443 	// Set prefix mask.
28444 
28445 	sin6 = &ifra.ifra_prefixmask;
28446 	SIN6_LEN_SET( sin6 );
28447 	sin6->sin6_family = AF_INET6;
28448 	wholeBytes = inMaskBitLen / 8;
28449 	if( wholeBytes > 0 ) memset( sin6->sin6_addr.s6_addr, 0xFF, (size_t) wholeBytes );
28450 	remainingBits = inMaskBitLen % 8;
28451 	if( remainingBits > 0 ) sin6->sin6_addr.s6_addr[ wholeBytes ] = ( 0xFFU << ( 8 - remainingBits ) ) & 0xFFU;
28452 
28453 	ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
28454 	ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
28455 
28456 	err = ioctl( infoSock, SIOCAIFADDR_IN6, &ifra );
28457 	err = map_global_value_errno( err != -1, err );
28458 	require_noerr_quiet( err, exit );
28459 
28460 exit:
28461 	ForgetSocket( &infoSock );
28462 	return( err );
28463 }
28464 
28465 //===========================================================================================================================
28466 //	_InterfaceIPv6AddressRemove
28467 //===========================================================================================================================
28468 
_InterfaceIPv6AddressRemove(const char * inIfName,const uint8_t inAddr[STATIC_PARAM16])28469 static OSStatus	_InterfaceIPv6AddressRemove( const char *inIfName, const uint8_t inAddr[ STATIC_PARAM 16 ] )
28470 {
28471 	OSStatus					err;
28472 	SocketRef					infoSock = kInvalidSocketRef;
28473 	struct in6_ifreq			ifr;
28474 	size_t						len;
28475 	struct sockaddr_in6 *		sin6;
28476 
28477 	infoSock = socket( AF_INET6, SOCK_DGRAM, 0 );
28478 	err = map_socket_creation_errno( infoSock );
28479 	require_noerr( err, exit );
28480 
28481 	memset( &ifr, 0, sizeof( ifr ) );
28482 	len = strlcpy( ifr.ifr_name, inIfName, sizeof( ifr.ifr_name ) );
28483 	require_action_quiet( len < sizeof( ifr.ifr_name ), exit, err = kSizeErr );
28484 
28485 	sin6 = &ifr.ifr_ifru.ifru_addr;
28486 	SIN6_LEN_SET( sin6 );
28487 	sin6->sin6_family = AF_INET6;
28488 	memcpy( sin6->sin6_addr.s6_addr, inAddr, 16 );
28489 
28490 	err = ioctl( infoSock, SIOCDIFADDR_IN6, &ifr );
28491 	err = map_global_value_errno( err != -1, err );
28492 	require_noerr_quiet( err, exit );
28493 
28494 exit:
28495 	ForgetSocket( &infoSock );
28496 	return( err );
28497 }
28498 #endif	// TARGET_OS_DARWIN
28499 
28500 //===========================================================================================================================
28501 //	_TicksDiff
28502 //===========================================================================================================================
28503 
_TicksDiff(uint64_t inT1,uint64_t inT2)28504 static int64_t	_TicksDiff( uint64_t inT1, uint64_t inT2 )
28505 {
28506 	return( (int64_t)( inT1 - inT2 ) );
28507 }
28508 
28509 //===========================================================================================================================
28510 //	_SockAddrInitIPv4
28511 //===========================================================================================================================
28512 
_SockAddrInitIPv4(struct sockaddr_in * inSA,uint32_t inIPv4,uint16_t inPort)28513 static void	_SockAddrInitIPv4( struct sockaddr_in *inSA, uint32_t inIPv4, uint16_t inPort )
28514 {
28515 	memset( inSA, 0, sizeof( *inSA ) );
28516 	SIN_LEN_SET( inSA );
28517 	inSA->sin_family		= AF_INET;
28518 	inSA->sin_port			= htons( inPort );
28519 	inSA->sin_addr.s_addr	= htonl( inIPv4 );
28520 }
28521 
28522 //===========================================================================================================================
28523 //	_SockAddrInitIPv6
28524 //===========================================================================================================================
28525 
28526 static void
_SockAddrInitIPv6(struct sockaddr_in6 * inSA,const uint8_t inIPv6[STATIC_PARAM16],uint32_t inScope,uint16_t inPort)28527 	_SockAddrInitIPv6(
28528 		struct sockaddr_in6 *	inSA,
28529 		const uint8_t			inIPv6[ STATIC_PARAM 16 ],
28530 		uint32_t				inScope,
28531 		uint16_t				inPort )
28532 {
28533 	check_compile_time_code( sizeof( inSA->sin6_addr.s6_addr ) == 16 );
28534 
28535 	memset( inSA, 0, sizeof( *inSA ) );
28536 	SIN6_LEN_SET( inSA );
28537 	inSA->sin6_family	= AF_INET6;
28538 	inSA->sin6_port		= htons( inPort );
28539 	memcpy( inSA->sin6_addr.s6_addr, inIPv6, 16 );
28540 	inSA->sin6_scope_id	= inScope;
28541 }
28542 
28543 //===========================================================================================================================
28544 //	_WriteReverseIPv6DomainNameString
28545 //===========================================================================================================================
28546 
28547 static void
_WriteReverseIPv6DomainNameString(const uint8_t inIPv6Addr[STATIC_PARAM16],char outBuffer[STATIC_PARAM kReverseIPv6DomainNameBufLen])28548 	_WriteReverseIPv6DomainNameString(
28549 		const uint8_t	inIPv6Addr[ STATIC_PARAM 16 ],
28550 		char			outBuffer[ STATIC_PARAM kReverseIPv6DomainNameBufLen ] )
28551 {
28552 	char *		dst;
28553 	int			i;
28554 
28555 	dst = outBuffer;
28556 	for( i = 0; i < 16; ++i )
28557 	{
28558 		const unsigned int		octet = inIPv6Addr[ 15 - i ];
28559 
28560 		*dst++ = kHexDigitsLowercase[ octet & 0x0F ];
28561 		*dst++ = '.';
28562 		*dst++ = kHexDigitsLowercase[ octet >> 4 ];
28563 		*dst++ = '.';
28564 	}
28565 	memcpy( dst, kIP6ArpaDomainStr, sizeof( kIP6ArpaDomainStr ) );
28566 	dst += sizeof( kIP6ArpaDomainStr );
28567 	check( ( dst - outBuffer ) == kReverseIPv6DomainNameBufLen );
28568 }
28569 
28570 //===========================================================================================================================
28571 //	_WriteReverseIPv4DomainNameString
28572 //===========================================================================================================================
28573 
28574 static void
_WriteReverseIPv4DomainNameString(uint32_t inIPv4Addr,char outBuffer[STATIC_PARAM kReverseIPv4DomainNameBufLen])28575 	_WriteReverseIPv4DomainNameString(
28576 		uint32_t	inIPv4Addr,
28577 		char		outBuffer[ STATIC_PARAM kReverseIPv4DomainNameBufLen ] )
28578 {
28579 	SNPrintF( outBuffer, kReverseIPv4DomainNameBufLen, "%u.%u.%u.%u.%s",
28580 		  inIPv4Addr         & 0xFF,
28581 		( inIPv4Addr >>  8 ) & 0xFF,
28582 		( inIPv4Addr >> 16 ) & 0xFF,
28583 		( inIPv4Addr >> 24 ) & 0xFF, kInAddrArpaDomainStr );
28584 }
28585 
28586 #if( MDNSRESPONDER_PROJECT )
28587 //===========================================================================================================================
28588 //	_SetDefaultFallbackDNSService
28589 //===========================================================================================================================
28590 
_SetDefaultFallbackDNSService(const char * inFallbackDNSServiceStr)28591 static OSStatus	_SetDefaultFallbackDNSService( const char *inFallbackDNSServiceStr )
28592 {
28593 	OSStatus		err;
28594 
28595 	if( __builtin_available( macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, * ) )
28596 	{
28597 		nw_resolver_config_t		resolverConfig;
28598 		CFDataRef					plistData;
28599 
28600 		if( stricmp_prefix( inFallbackDNSServiceStr, kFallbackDNSServiceArgPrefix_DoH ) == 0 )
28601 		{
28602 			nw_endpoint_t			endpoint;
28603 			const char * const		url = inFallbackDNSServiceStr + sizeof_string( kFallbackDNSServiceArgPrefix_DoH );
28604 
28605 			endpoint = nw_endpoint_create_url( url );
28606 			require_action( endpoint, exit, err = kUnknownErr );
28607 
28608 			resolverConfig = nw_resolver_config_create_https( endpoint );
28609 			nw_forget( &endpoint );
28610 			require_action( resolverConfig, exit, err = kUnknownErr );
28611 		}
28612 		else if( stricmp_prefix( inFallbackDNSServiceStr, kFallbackDNSServiceArgPrefix_DoT ) == 0 )
28613 		{
28614 			nw_endpoint_t			endpoint;
28615 			const char * const		hostname = inFallbackDNSServiceStr + sizeof_string( kFallbackDNSServiceArgPrefix_DoT );
28616 
28617 			endpoint = nw_endpoint_create_host( hostname, "0" );
28618 			require_action( endpoint, exit, err = kUnknownErr );
28619 
28620 			resolverConfig = nw_resolver_config_create_tls( endpoint );
28621 			nw_forget( &endpoint );
28622 			require_action( resolverConfig, exit, err = kUnknownErr );
28623 		}
28624 		else
28625 		{
28626 			FPrintF( stderr, "error: Unrecognized fallback DNS service string: \"%s\"\n", inFallbackDNSServiceStr );
28627 			err = kParamErr;
28628 			goto exit;
28629 		}
28630 		plistData = nw_resolver_config_copy_plist_data_ref( resolverConfig );
28631 		require_action( plistData, exit, err = kUnknownErr );
28632 
28633 		err = DNSServiceSetResolverDefaults( CFDataGetBytePtr( plistData ), (size_t) CFDataGetLength( plistData ), true );
28634 		ForgetCF( &plistData );
28635 		require_noerr( err, exit );
28636 	}
28637 	else
28638 	{
28639 		FPrintF( stderr, "error: Setting a default fallback DNS service is unsupported on this OS.\n" );
28640 		err = kUnsupportedErr;
28641 	}
28642 
28643 exit:
28644 	return( err );
28645 }
28646 #endif
28647 
28648 //===========================================================================================================================
28649 //	MDNSColliderCreate
28650 //===========================================================================================================================
28651 
28652 typedef enum
28653 {
28654 	kMDNSColliderOpCode_Invalid			= 0,
28655 	kMDNSColliderOpCode_Send			= 1,
28656 	kMDNSColliderOpCode_Wait			= 2,
28657 	kMDNSColliderOpCode_SetProbeActions	= 3,
28658 	kMDNSColliderOpCode_LoopPush		= 4,
28659 	kMDNSColliderOpCode_LoopPop			= 5,
28660 	kMDNSColliderOpCode_Exit			= 6
28661 
28662 }	MDNSColliderOpCode;
28663 
28664 typedef struct
28665 {
28666 	MDNSColliderOpCode		opcode;
28667 	uint32_t				operand;
28668 
28669 }	MDNSCInstruction;
28670 
28671 #define kMaxLoopDepth		16
28672 
28673 struct MDNSColliderPrivate
28674 {
28675 	CFRuntimeBase					base;							// CF object base.
28676 	dispatch_queue_t				queue;							// Queue for collider's events.
28677 	dispatch_source_t				readSourceV4;					// Read dispatch source for IPv4 socket.
28678 	dispatch_source_t				readSourceV6;					// Read dispatch source for IPv6 socket.
28679 	SocketRef						sockV4;							// IPv4 UDP socket for mDNS.
28680 	SocketRef						sockV6;							// IPv6 UDP socket for mDNS.
28681 	uint8_t *						target;							// Record name being targeted. (malloced)
28682 	uint8_t *						responsePtr;					// Response message pointer. (malloced)
28683 	size_t							responseLen;					// Response message length.
28684 	uint8_t *						probePtr;						// Probe query message pointer. (malloced)
28685 	size_t							probeLen;						// Probe query message length.
28686 	unsigned int					probeCount;						// Count of probe queries received for collider's record.
28687 	uint32_t						probeActionMap;					// Bitmap of actions to take for
28688 	MDNSCInstruction *				program;						// Program to execute.
28689 	uint32_t						pc;								// Program's program counter.
28690 	uint32_t						loopCounts[ kMaxLoopDepth ];	// Stack of loop counters.
28691 	uint32_t						loopDepth;						// Current loop depth.
28692 	dispatch_source_t				waitTimer;						// Timer for program's wait commands.
28693 	uint32_t						interfaceIndex;					// Interface over which to send and receive mDNS msgs.
28694 	MDNSColliderStopHandler_f		stopHandler;					// User's stop handler.
28695 	void *							stopContext;					// User's stop handler context.
28696 	MDNSColliderProtocols			protocols;						// Protocols to use, i.e., IPv4, IPv6.
28697 	Boolean							stopped;						// True if the collider has been stopped.
28698 	uint8_t							msgBuf[ kMDNSMessageSizeMax ];	// mDNS message buffer.
28699 };
28700 
28701 static void		_MDNSColliderStop( MDNSColliderRef inCollider, OSStatus inError );
28702 static void		_MDNSColliderReadHandler( void *inContext );
28703 static void		_MDNSColliderExecuteProgram( void *inContext );
28704 static OSStatus	_MDNSColliderSendResponse( MDNSColliderRef inCollider, SocketRef inSock, const struct sockaddr *inDest );
28705 static OSStatus	_MDNSColliderSendProbe( MDNSColliderRef inCollider, SocketRef inSock, const struct sockaddr *inDest );
28706 
28707 CF_CLASS_DEFINE( MDNSCollider );
28708 
28709 ulog_define_ex( kDNSSDUtilIdentifier, MDNSCollider, kLogLevelInfo, kLogFlags_None, "MDNSCollider", NULL );
28710 #define mc_ulog( LEVEL, ... )		ulog( &log_category_from_name( MDNSCollider ), (LEVEL), __VA_ARGS__ )
28711 
MDNSColliderCreate(dispatch_queue_t inQueue,MDNSColliderRef * outCollider)28712 static OSStatus	MDNSColliderCreate( dispatch_queue_t inQueue, MDNSColliderRef *outCollider )
28713 {
28714 	OSStatus			err;
28715 	MDNSColliderRef		obj = NULL;
28716 
28717 	CF_OBJECT_CREATE( MDNSCollider, obj, err, exit );
28718 
28719 	ReplaceDispatchQueue( &obj->queue, inQueue );
28720 	obj->sockV4 = kInvalidSocketRef;
28721 	obj->sockV6 = kInvalidSocketRef;
28722 
28723 	*outCollider = obj;
28724 	err = kNoErr;
28725 
28726 exit:
28727 	return( err );
28728 }
28729 
28730 //===========================================================================================================================
28731 //	_MDNSColliderFinalize
28732 //===========================================================================================================================
28733 
_MDNSColliderFinalize(CFTypeRef inObj)28734 static void	_MDNSColliderFinalize( CFTypeRef inObj )
28735 {
28736 	MDNSColliderRef const		me = (MDNSColliderRef) inObj;
28737 
28738 	check( !me->waitTimer );
28739 	check( !me->readSourceV4 );
28740 	check( !me->readSourceV6 );
28741 	check( !IsValidSocket( me->sockV4 ) );
28742 	check( !IsValidSocket( me->sockV6 ) );
28743 	ForgetMem( &me->target );
28744 	ForgetMem( &me->responsePtr );
28745 	ForgetMem( &me->probePtr );
28746 	ForgetMem( &me->program );
28747 	dispatch_forget( &me->queue );
28748 }
28749 
28750 //===========================================================================================================================
28751 //	MDNSColliderStart
28752 //===========================================================================================================================
28753 
28754 static void	_MDNSColliderStart( void *inContext );
28755 
MDNSColliderStart(MDNSColliderRef me)28756 static OSStatus	MDNSColliderStart( MDNSColliderRef me )
28757 {
28758 	OSStatus		err;
28759 
28760 	require_action_quiet( me->target,         exit, err = kNotPreparedErr );
28761 	require_action_quiet( me->responsePtr,    exit, err = kNotPreparedErr );
28762 	require_action_quiet( me->probePtr,       exit, err = kNotPreparedErr );
28763 	require_action_quiet( me->program,        exit, err = kNotPreparedErr );
28764 	require_action_quiet( me->interfaceIndex, exit, err = kNotPreparedErr );
28765 	require_action_quiet( me->protocols,      exit, err = kNotPreparedErr );
28766 
28767 	CFRetain( me );
28768 	dispatch_async_f( me->queue, me, _MDNSColliderStart );
28769 	err = kNoErr;
28770 
28771 exit:
28772 	return( err );
28773 }
28774 
_MDNSColliderStart(void * inContext)28775 static void	_MDNSColliderStart( void *inContext )
28776 {
28777 	OSStatus					err;
28778 	MDNSColliderRef const		me		= (MDNSColliderRef) inContext;
28779 	SocketRef					sock	= kInvalidSocketRef;
28780 	SocketContext *				sockCtx	= NULL;
28781 
28782 	if( me->protocols & kMDNSColliderProtocol_IPv4 )
28783 	{
28784 		err = CreateMulticastSocket( GetMDNSMulticastAddrV4(), kMDNSPort, NULL, me->interfaceIndex, true, NULL, &sock );
28785 		require_noerr( err, exit );
28786 
28787 		sockCtx = SocketContextCreate( sock, me, &err );
28788 		require_noerr( err, exit );
28789 		sock = kInvalidSocketRef;
28790 
28791 		err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _MDNSColliderReadHandler, SocketContextCancelHandler,
28792 			sockCtx, &me->readSourceV4 );
28793 		require_noerr( err, exit );
28794 		me->sockV4 = sockCtx->sock;
28795 		sockCtx = NULL;
28796 
28797 		dispatch_resume( me->readSourceV4 );
28798 	}
28799 
28800 	if( me->protocols & kMDNSColliderProtocol_IPv6 )
28801 	{
28802 		err = CreateMulticastSocket( GetMDNSMulticastAddrV6(), kMDNSPort, NULL, me->interfaceIndex, true, NULL, &sock );
28803 		require_noerr( err, exit );
28804 
28805 		sockCtx = SocketContextCreate( sock, me, &err );
28806 		require_noerr( err, exit );
28807 		sock = kInvalidSocketRef;
28808 
28809 		err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _MDNSColliderReadHandler, SocketContextCancelHandler,
28810 			sockCtx, &me->readSourceV6 );
28811 		require_noerr( err, exit );
28812 		me->sockV6 = sockCtx->sock;
28813 		sockCtx = NULL;
28814 
28815 		dispatch_resume( me->readSourceV6 );
28816 	}
28817 
28818 	_MDNSColliderExecuteProgram( me );
28819 	err = kNoErr;
28820 
28821 exit:
28822 	ForgetSocket( &sock );
28823 	ForgetSocketContext( &sockCtx );
28824 	if( err ) _MDNSColliderStop( me, err );
28825 }
28826 
28827 //===========================================================================================================================
28828 //	MDNSColliderStop
28829 //===========================================================================================================================
28830 
28831 static void	_MDNSColliderUserStop( void *inContext );
28832 
MDNSColliderStop(MDNSColliderRef me)28833 static void	MDNSColliderStop( MDNSColliderRef me )
28834 {
28835 	CFRetain( me );
28836 	dispatch_async_f( me->queue, me, _MDNSColliderUserStop );
28837 }
28838 
_MDNSColliderUserStop(void * inContext)28839 static void	_MDNSColliderUserStop( void *inContext )
28840 {
28841 	MDNSColliderRef const		me = (MDNSColliderRef) inContext;
28842 
28843 	_MDNSColliderStop( me, kCanceledErr );
28844 	CFRelease( me );
28845 }
28846 
28847 //===========================================================================================================================
28848 //	MDNSColliderSetProtocols
28849 //===========================================================================================================================
28850 
MDNSColliderSetProtocols(MDNSColliderRef me,MDNSColliderProtocols inProtocols)28851 static void	MDNSColliderSetProtocols( MDNSColliderRef me, MDNSColliderProtocols inProtocols )
28852 {
28853 	me->protocols = inProtocols;
28854 }
28855 
28856 //===========================================================================================================================
28857 //	MDNSColliderSetInterfaceIndex
28858 //===========================================================================================================================
28859 
MDNSColliderSetInterfaceIndex(MDNSColliderRef me,uint32_t inInterfaceIndex)28860 static void	MDNSColliderSetInterfaceIndex( MDNSColliderRef me, uint32_t inInterfaceIndex )
28861 {
28862 	me->interfaceIndex = inInterfaceIndex;
28863 }
28864 
28865 //===========================================================================================================================
28866 //	MDNSColliderSetProgram
28867 //===========================================================================================================================
28868 
28869 #define kMDNSColliderProgCmd_Done		"done"
28870 #define kMDNSColliderProgCmd_Loop		"loop"
28871 #define kMDNSColliderProgCmd_Send		"send"
28872 #define kMDNSColliderProgCmd_Probes		"probes"
28873 #define kMDNSColliderProgCmd_Wait		"wait"
28874 
28875 typedef uint32_t		MDNSColliderProbeAction;
28876 
28877 #define kMDNSColliderProbeAction_None					0
28878 #define kMDNSColliderProbeAction_Respond				1
28879 #define kMDNSColliderProbeAction_RespondUnicast			2
28880 #define kMDNSColliderProbeAction_RespondMulticast		3
28881 #define kMDNSColliderProbeAction_Probe					4
28882 #define kMDNSColliderProbeAction_MaxValue				kMDNSColliderProbeAction_Probe
28883 
28884 #define kMDNSColliderProbeActionBits_Count			3
28885 #define kMDNSColliderProbeActionBits_Mask			( ( 1U << kMDNSColliderProbeActionBits_Count ) - 1 )
28886 #define kMDNSColliderProbeActionMaxProbeCount		( 32 / kMDNSColliderProbeActionBits_Count )
28887 
28888 check_compile_time( kMDNSColliderProbeAction_MaxValue <= kMDNSColliderProbeActionBits_Mask );
28889 
28890 static OSStatus	_MDNSColliderParseProbeActionString( const char *inString, size_t inLen, uint32_t *outBitmap );
28891 
MDNSColliderSetProgram(MDNSColliderRef me,const char * inProgramStr)28892 static OSStatus	MDNSColliderSetProgram( MDNSColliderRef me, const char *inProgramStr )
28893 {
28894 	OSStatus				err;
28895 	uint32_t				insCount;
28896 	unsigned int			loopDepth;
28897 	const char *			cmd;
28898 	const char *			end;
28899 	const char *			next;
28900 	MDNSCInstruction *		program = NULL;
28901 	uint32_t				loopStart[ kMaxLoopDepth ];
28902 
28903 	insCount = 0;
28904 	for( cmd = inProgramStr; *cmd; cmd = next )
28905 	{
28906 		for( end = cmd; *end && ( *end != ';' ); ++end ) {}
28907 		require_action_quiet( end != cmd, exit, err = kMalformedErr );
28908 		next = ( *end == ';' ) ? ( end + 1 ) : end;
28909 		++insCount;
28910 	}
28911 
28912 	program = (MDNSCInstruction *) calloc( insCount + 1, sizeof( *program ) );
28913 	require_action( program, exit, err = kNoMemoryErr );
28914 
28915 	insCount	= 0;
28916 	loopDepth	= 0;
28917 	for( cmd = inProgramStr; *cmd; cmd = next )
28918 	{
28919 		size_t							cmdLen;
28920 		const char *					ptr;
28921 		const char *					arg;
28922 		size_t							argLen;
28923 		uint32_t						value;
28924 		MDNSCInstruction * const		ins = &program[ insCount ];
28925 
28926 		while( isspace_safe( *cmd ) ) ++cmd;
28927 		for( end = cmd; *end && ( *end != ';' ); ++end ) {}
28928 		next = ( *end == ';' ) ? ( end + 1 ) : end;
28929 
28930 		for( ptr = cmd; ( ptr < end ) && !isspace_safe( *ptr ); ++ptr ) {}
28931 		cmdLen = (size_t)( ptr - cmd );
28932 
28933 		// Done statement
28934 
28935 		if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Done ) == 0 )
28936 		{
28937 			while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
28938 			require_action_quiet( ptr == end, exit, err = kMalformedErr );
28939 
28940 			require_action_quiet( loopDepth > 0, exit, err = kMalformedErr );
28941 
28942 			ins->opcode		= kMDNSColliderOpCode_LoopPop;
28943 			ins->operand	= loopStart[ --loopDepth ];
28944 		}
28945 
28946 		// Loop command
28947 
28948 		else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Loop ) == 0 )
28949 		{
28950 			for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
28951 			err = DecimalTextToUInt32( arg, end, &value, &ptr );
28952 			require_noerr_quiet( err, exit );
28953 			require_action_quiet( value > 0, exit, err = kValueErr );
28954 
28955 			while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
28956 			require_action_quiet( ptr == end, exit, err = kMalformedErr );
28957 
28958 			ins->opcode 	= kMDNSColliderOpCode_LoopPush;
28959 			ins->operand	= value;
28960 
28961 			require_action_quiet( loopDepth < kMaxLoopDepth, exit, err = kNoSpaceErr );
28962 			loopStart[ loopDepth++ ] = insCount + 1;
28963 		}
28964 
28965 		// Probes command
28966 
28967 		else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Probes ) == 0 )
28968 		{
28969 			for( arg = ptr; ( arg < end ) &&  isspace_safe( *arg ); ++arg ) {}
28970 			for( ptr = arg; ( ptr < end ) && !isspace_safe( *ptr ); ++ptr ) {}
28971 			argLen = (size_t)( ptr - arg );
28972 			if( argLen > 0 )
28973 			{
28974 				err = _MDNSColliderParseProbeActionString( arg, argLen, &value );
28975 				require_noerr_quiet( err, exit );
28976 			}
28977 			else
28978 			{
28979 				value = 0;
28980 			}
28981 
28982 			while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
28983 			require_action_quiet( ptr == end, exit, err = kMalformedErr );
28984 
28985 			ins->opcode 	= kMDNSColliderOpCode_SetProbeActions;
28986 			ins->operand	= value;
28987 		}
28988 
28989 		// Send command
28990 
28991 		else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Send ) == 0 )
28992 		{
28993 			while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
28994 			require_action_quiet( ptr == end, exit, err = kMalformedErr );
28995 
28996 			ins->opcode = kMDNSColliderOpCode_Send;
28997 		}
28998 
28999 		// Wait command
29000 
29001 		else if( strnicmpx( cmd, cmdLen, kMDNSColliderProgCmd_Wait ) == 0 )
29002 		{
29003 			for( arg = ptr; ( arg < end ) && isspace_safe( *arg ); ++arg ) {}
29004 			err = DecimalTextToUInt32( arg, end, &value, &ptr );
29005 			require_noerr_quiet( err, exit );
29006 
29007 			while( ( ptr < end ) && isspace_safe( *ptr ) ) ++ptr;
29008 			require_action_quiet( ptr == end, exit, err = kMalformedErr );
29009 
29010 			ins->opcode		= kMDNSColliderOpCode_Wait;
29011 			ins->operand	= value;
29012 		}
29013 
29014 		// Unrecognized command
29015 
29016 		else
29017 		{
29018 			err = kCommandErr;
29019 			goto exit;
29020 		}
29021 		++insCount;
29022 	}
29023 	require_action_quiet( loopDepth == 0, exit, err = kMalformedErr );
29024 
29025 	program[ insCount ].opcode = kMDNSColliderOpCode_Exit;
29026 
29027 	FreeNullSafe( me->program );
29028 	me->program = program;
29029 	program = NULL;
29030 	err = kNoErr;
29031 
29032 exit:
29033 	FreeNullSafe( program );
29034 	return( err );
29035 }
29036 
_MDNSColliderParseProbeActionString(const char * inString,size_t inLen,uint32_t * outBitmap)29037 static OSStatus	_MDNSColliderParseProbeActionString( const char *inString, size_t inLen, uint32_t *outBitmap )
29038 {
29039 	OSStatus				err;
29040 	const char *			ptr;
29041 	const char * const		end = &inString[ inLen ];
29042 	uint32_t				bitmap;
29043 	int						index;
29044 
29045 	bitmap	= 0;
29046 	index	= 0;
29047 	ptr		= inString;
29048 	while( ptr < end )
29049 	{
29050 		int							c, count;
29051 		MDNSColliderProbeAction		action;
29052 
29053 		c = *ptr++;
29054 		if( isdigit_safe( c ) )
29055 		{
29056 			count = 0;
29057 			do
29058 			{
29059 				count = ( count * 10 ) + ( c - '0' );
29060 				require_action_quiet( count <= ( kMDNSColliderProbeActionMaxProbeCount - index ), exit, err = kCountErr );
29061 				require_action_quiet( ptr < end, exit, err = kUnderrunErr );
29062 				c = *ptr++;
29063 
29064 			}	while( isdigit_safe( c ) );
29065 			require_action_quiet( count > 0, exit, err = kCountErr );
29066 		}
29067 		else
29068 		{
29069 			require_action_quiet( index < kMDNSColliderProbeActionMaxProbeCount, exit, err = kMalformedErr );
29070 			count = 1;
29071 		}
29072 
29073 		switch( c )
29074 		{
29075 			case 'n':	action = kMDNSColliderProbeAction_None;				break;
29076 			case 'r':	action = kMDNSColliderProbeAction_Respond;			break;
29077 			case 'u':	action = kMDNSColliderProbeAction_RespondUnicast;	break;
29078 			case 'm':	action = kMDNSColliderProbeAction_RespondMulticast;	break;
29079 			case 'p':	action = kMDNSColliderProbeAction_Probe;			break;
29080 			default:	err = kMalformedErr;								goto exit;
29081 		}
29082 		if( ptr < end )
29083 		{
29084 			c = *ptr++;
29085 			require_action_quiet( ( c == '-' ) && ( ptr < end ), exit, err = kMalformedErr );
29086 		}
29087 		while( count-- > 0 )
29088 		{
29089 			bitmap |= ( action << ( index * kMDNSColliderProbeActionBits_Count ) );
29090 			++index;
29091 		}
29092 	}
29093 
29094 	*outBitmap = bitmap;
29095 	err = kNoErr;
29096 
29097 exit:
29098 	return( err );
29099 }
29100 
29101 //===========================================================================================================================
29102 //	MDNSColliderSetStopHandler
29103 //===========================================================================================================================
29104 
MDNSColliderSetStopHandler(MDNSColliderRef me,MDNSColliderStopHandler_f inStopHandler,void * inStopContext)29105 static void	MDNSColliderSetStopHandler( MDNSColliderRef me, MDNSColliderStopHandler_f inStopHandler, void *inStopContext )
29106 {
29107 	me->stopHandler = inStopHandler;
29108 	me->stopContext = inStopContext;
29109 }
29110 
29111 //===========================================================================================================================
29112 //	MDNSColliderSetRecord
29113 //===========================================================================================================================
29114 
29115 #define kMDNSColliderDummyStr			"\x16" "mdnscollider-sent-this" "\x05" "local"
29116 #define kMDNSColliderDummyName			( (const uint8_t *) kMDNSColliderDummyStr )
29117 #define kMDNSColliderDummyNameLen		sizeof( kMDNSColliderDummyStr )
29118 
29119 static OSStatus
MDNSColliderSetRecord(MDNSColliderRef me,const uint8_t * inName,uint16_t inType,const void * inRDataPtr,size_t inRDataLen)29120 	MDNSColliderSetRecord(
29121 		MDNSColliderRef	me,
29122 		const uint8_t *	inName,
29123 		uint16_t		inType,
29124 		const void *	inRDataPtr,
29125 		size_t			inRDataLen )
29126 {
29127 	OSStatus		err;
29128 	DataBuffer		msgDB;
29129 	DNSHeader		header;
29130 	uint8_t *		targetPtr	= NULL;
29131 	size_t			targetLen;
29132 	uint8_t *		responsePtr	= NULL;
29133 	size_t			responseLen;
29134 	uint8_t *		probePtr	= NULL;
29135 	size_t			probeLen;
29136 
29137 	DataBuffer_Init( &msgDB, NULL, 0, kMDNSMessageSizeMax );
29138 
29139 	err = DomainNameDup( inName, &targetPtr, &targetLen );
29140 	require_noerr_quiet( err, exit );
29141 
29142 	// Create response message.
29143 
29144 	memset( &header, 0, sizeof( header ) );
29145 	DNSHeaderSetFlags( &header, kDNSHeaderFlag_Response | kDNSHeaderFlag_AuthAnswer );
29146 	DNSHeaderSetAnswerCount( &header, 1 );
29147 
29148 	err = DataBuffer_Append( &msgDB, &header, sizeof( header ) );
29149 	require_noerr( err, exit );
29150 
29151 	err = _DataBuffer_AppendDNSRecord( &msgDB, targetPtr, targetLen, inType, kDNSServiceClass_IN | kMDNSClassCacheFlushBit,
29152 		1976, inRDataPtr, inRDataLen );
29153 	require_noerr( err, exit );
29154 
29155 	err = DataBuffer_Detach( &msgDB, &responsePtr, &responseLen );
29156 	require_noerr( err, exit );
29157 
29158 	// Create probe message.
29159 
29160 	memset( &header, 0, sizeof( header ) );
29161 	DNSHeaderSetQuestionCount( &header, 2 );
29162 	DNSHeaderSetAuthorityCount( &header, 1 );
29163 
29164 	err = DataBuffer_Append( &msgDB, &header, sizeof( header ) );
29165 	require_noerr( err, exit );
29166 
29167 	err = _DataBuffer_AppendDNSQuestion( &msgDB, targetPtr, targetLen, kDNSServiceType_ANY, kDNSServiceClass_IN );
29168 	require_noerr( err, exit );
29169 
29170 	err = _DataBuffer_AppendDNSQuestion( &msgDB, kMDNSColliderDummyName, kMDNSColliderDummyNameLen,
29171 		kDNSServiceType_NULL, kDNSServiceClass_IN );
29172 	require_noerr( err, exit );
29173 
29174 	err = _DataBuffer_AppendDNSRecord( &msgDB, targetPtr, targetLen, inType, kDNSServiceClass_IN,
29175 		1976, inRDataPtr, inRDataLen );
29176 	require_noerr( err, exit );
29177 
29178 	err = DataBuffer_Detach( &msgDB, &probePtr, &probeLen );
29179 	require_noerr( err, exit );
29180 
29181 	FreeNullSafe( me->target );
29182 	me->target = targetPtr;
29183 	targetPtr = NULL;
29184 
29185 	FreeNullSafe( me->responsePtr );
29186 	me->responsePtr = responsePtr;
29187 	me->responseLen = responseLen;
29188 	responsePtr = NULL;
29189 
29190 	FreeNullSafe( me->probePtr );
29191 	me->probePtr = probePtr;
29192 	me->probeLen = probeLen;
29193 	probePtr = NULL;
29194 
29195 exit:
29196 	DataBuffer_Free( &msgDB );
29197 	FreeNullSafe( targetPtr );
29198 	FreeNullSafe( responsePtr );
29199 	FreeNullSafe( probePtr );
29200 	return( err );
29201 }
29202 
29203 //===========================================================================================================================
29204 //	_MDNSColliderStop
29205 //===========================================================================================================================
29206 
_MDNSColliderStop(MDNSColliderRef me,OSStatus inError)29207 static void	_MDNSColliderStop( MDNSColliderRef me, OSStatus inError )
29208 {
29209 	dispatch_source_forget( &me->waitTimer );
29210 	dispatch_source_forget( &me->readSourceV4 );
29211 	dispatch_source_forget( &me->readSourceV6 );
29212 	me->sockV4 = kInvalidSocketRef;
29213 	me->sockV6 = kInvalidSocketRef;
29214 
29215 	if( !me->stopped )
29216 	{
29217 		me->stopped = true;
29218 		if( me->stopHandler ) me->stopHandler( me->stopContext, inError );
29219 		CFRelease( me );
29220 	}
29221 }
29222 
29223 //===========================================================================================================================
29224 //	_MDNSColliderReadHandler
29225 //===========================================================================================================================
29226 
29227 static MDNSColliderProbeAction	_MDNSColliderGetProbeAction( uint32_t inBitmap, unsigned int inProbeNumber );
29228 static const char *				_MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction );
29229 
_MDNSColliderReadHandler(void * inContext)29230 static void	_MDNSColliderReadHandler( void *inContext )
29231 {
29232 	OSStatus					err;
29233 	struct timeval				now;
29234 	SocketContext * const		sockCtx	= (SocketContext *) inContext;
29235 	MDNSColliderRef const		me		= (MDNSColliderRef) sockCtx->userContext;
29236 	size_t						msgLen;
29237 	sockaddr_ip					sender;
29238 	const DNSHeader *			hdr;
29239 	const uint8_t *				ptr;
29240 	const struct sockaddr *		dest;
29241 	int							probeFound, probeIsQU;
29242 	unsigned int				qCount, i;
29243 	MDNSColliderProbeAction		action;
29244 
29245 	gettimeofday( &now, NULL );
29246 
29247 	err = SocketRecvFrom( sockCtx->sock, me->msgBuf, sizeof( me->msgBuf ), &msgLen, &sender, sizeof( sender ),
29248 		NULL, NULL, NULL, NULL );
29249 	require_noerr( err, exit );
29250 
29251 	require_quiet( msgLen >= kDNSHeaderLength, exit );
29252 	hdr = (const DNSHeader *) me->msgBuf;
29253 
29254 	probeFound	= false;
29255 	probeIsQU	= false;
29256 	qCount = DNSHeaderGetQuestionCount( hdr );
29257 	ptr = (const uint8_t *) &hdr[ 1 ];
29258 	for( i = 0; i < qCount; ++i )
29259 	{
29260 		uint16_t		qtype, qclass;
29261 		uint8_t			qname[ kDomainNameLengthMax ];
29262 
29263 		err = DNSMessageExtractQuestion( me->msgBuf, msgLen, ptr, qname, &qtype, &qclass, &ptr );
29264 		require_noerr_quiet( err, exit );
29265 
29266 		if( ( qtype == kDNSServiceType_NULL ) && ( qclass == kDNSServiceClass_IN ) &&
29267 			DomainNameEqual( qname, kMDNSColliderDummyName ) )
29268 		{
29269 			probeFound = false;
29270 			break;
29271 		}
29272 
29273 		if( qtype != kDNSServiceType_ANY ) continue;
29274 		if( ( qclass & ~kMDNSClassUnicastResponseBit ) != kDNSServiceClass_IN ) continue;
29275 		if( !DomainNameEqual( qname, me->target ) ) continue;
29276 
29277 		if( !probeFound )
29278 		{
29279 			probeFound	= true;
29280 			probeIsQU	= ( qclass & kMDNSClassUnicastResponseBit ) ? true : false;
29281 		}
29282 	}
29283 	require_quiet( probeFound, exit );
29284 
29285 	++me->probeCount;
29286 	action = _MDNSColliderGetProbeAction( me->probeActionMap, me->probeCount );
29287 
29288 	mc_ulog( kLogLevelInfo, "Received probe from %##a at %{du:time} (action: %s) -- %#.1{du:dnsmsg}\n",
29289 		&sender, &now, _MDNSColliderProbeActionToString( action ), me->msgBuf, msgLen );
29290 
29291 	if( ( action == kMDNSColliderProbeAction_Respond )			||
29292 		( action == kMDNSColliderProbeAction_RespondUnicast )	||
29293 		( action == kMDNSColliderProbeAction_RespondMulticast ) )
29294 	{
29295 		if( ( ( action == kMDNSColliderProbeAction_Respond ) && probeIsQU ) ||
29296 			(   action == kMDNSColliderProbeAction_RespondUnicast ) )
29297 		{
29298 			dest = &sender.sa;
29299 		}
29300 		else if( ( ( action == kMDNSColliderProbeAction_Respond ) && !probeIsQU ) ||
29301 				 (   action == kMDNSColliderProbeAction_RespondMulticast ) )
29302 		{
29303 			dest = ( sender.sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
29304 		}
29305 
29306 		err = _MDNSColliderSendResponse( me, sockCtx->sock, dest );
29307 		require_noerr( err, exit );
29308 	}
29309 	else if( action == kMDNSColliderProbeAction_Probe )
29310 	{
29311 		dest = ( sender.sa.sa_family == AF_INET ) ? GetMDNSMulticastAddrV4() : GetMDNSMulticastAddrV6();
29312 
29313 		err = _MDNSColliderSendProbe( me, sockCtx->sock, dest );
29314 		require_noerr( err, exit );
29315 	}
29316 
29317 exit:
29318 	return;
29319 }
29320 
_MDNSColliderGetProbeAction(uint32_t inBitmap,unsigned int inProbeNumber)29321 static MDNSColliderProbeAction	_MDNSColliderGetProbeAction( uint32_t inBitmap, unsigned int inProbeNumber )
29322 {
29323 	MDNSColliderProbeAction		action;
29324 
29325 	if( ( inProbeNumber >= 1 ) && ( inProbeNumber <= kMDNSColliderProbeActionMaxProbeCount ) )
29326 	{
29327 		action = ( inBitmap >> ( ( inProbeNumber - 1 ) * kMDNSColliderProbeActionBits_Count ) ) &
29328 			kMDNSColliderProbeActionBits_Mask;
29329 	}
29330 	else
29331 	{
29332 		action = kMDNSColliderProbeAction_None;
29333 	}
29334 	return( action );
29335 }
29336 
_MDNSColliderProbeActionToString(MDNSColliderProbeAction inAction)29337 static const char *	_MDNSColliderProbeActionToString( MDNSColliderProbeAction inAction )
29338 {
29339 	switch( inAction )
29340 	{
29341 		case kMDNSColliderProbeAction_None:				return( "None" );
29342 		case kMDNSColliderProbeAction_Respond:			return( "Respond" );
29343 		case kMDNSColliderProbeAction_RespondUnicast:	return( "Respond (unicast)" );
29344 		case kMDNSColliderProbeAction_RespondMulticast:	return( "Respond (multicast)" );
29345 		case kMDNSColliderProbeAction_Probe:			return( "Probe" );
29346 		default:										return( "???" );
29347 	}
29348 }
29349 
29350 //===========================================================================================================================
29351 //	_MDNSColliderExecuteProgram
29352 //===========================================================================================================================
29353 
_MDNSColliderExecuteProgram(void * inContext)29354 static void	_MDNSColliderExecuteProgram( void *inContext )
29355 {
29356 	OSStatus					err;
29357 	MDNSColliderRef const		me = (MDNSColliderRef) inContext;
29358 	int							stop;
29359 
29360 	dispatch_forget( &me->waitTimer );
29361 
29362 	stop = false;
29363 	for( ;; )
29364 	{
29365 		const MDNSCInstruction * const		ins = &me->program[ me->pc++ ];
29366 		uint32_t							waitMs;
29367 
29368 		switch( ins->opcode )
29369 		{
29370 			case kMDNSColliderOpCode_Send:
29371 				if( IsValidSocket( me->sockV4 ) )
29372 				{
29373 					err = _MDNSColliderSendResponse( me, me->sockV4, GetMDNSMulticastAddrV4() );
29374 					require_noerr( err, exit );
29375 				}
29376 				if( IsValidSocket( me->sockV6 ) )
29377 				{
29378 					err = _MDNSColliderSendResponse( me, me->sockV6, GetMDNSMulticastAddrV6() );
29379 					require_noerr( err, exit );
29380 				}
29381 				break;
29382 
29383 			case kMDNSColliderOpCode_Wait:
29384 				waitMs = ins->operand;
29385 				if( waitMs > 0 )
29386 				{
29387 					err = DispatchTimerOneShotCreate( dispatch_time_milliseconds( waitMs ), 1, me->queue,
29388 						_MDNSColliderExecuteProgram, me, &me->waitTimer );
29389 					require_noerr( err, exit );
29390 					dispatch_resume( me->waitTimer );
29391 					goto exit;
29392 				}
29393 				break;
29394 
29395 			case kMDNSColliderOpCode_SetProbeActions:
29396 				me->probeCount		= 0;
29397 				me->probeActionMap	= ins->operand;
29398 				break;
29399 
29400 			case kMDNSColliderOpCode_LoopPush:
29401 				check( me->loopDepth < kMaxLoopDepth );
29402 				me->loopCounts[ me->loopDepth++ ] = ins->operand;
29403 				break;
29404 
29405 			case kMDNSColliderOpCode_LoopPop:
29406 				check( me->loopDepth > 0 );
29407 				if( --me->loopCounts[ me->loopDepth - 1 ] > 0 )
29408 				{
29409 					me->pc = ins->operand;
29410 				}
29411 				else
29412 				{
29413 					--me->loopDepth;
29414 				}
29415 				break;
29416 
29417 			case kMDNSColliderOpCode_Exit:
29418 				stop = true;
29419 				err	= kNoErr;
29420 				goto exit;
29421 
29422 			default:
29423 				dlogassert( "Unhandled opcode %u\n", ins->opcode );
29424 				err = kCommandErr;
29425 				goto exit;
29426 		}
29427 	}
29428 
29429 exit:
29430 	if( err || stop ) _MDNSColliderStop( me, err );
29431 }
29432 
29433 //===========================================================================================================================
29434 //	_MDNSColliderSendResponse
29435 //===========================================================================================================================
29436 
_MDNSColliderSendResponse(MDNSColliderRef me,SocketRef inSock,const struct sockaddr * inDest)29437 static OSStatus	_MDNSColliderSendResponse( MDNSColliderRef me, SocketRef inSock, const struct sockaddr *inDest )
29438 {
29439 	OSStatus		err;
29440 	ssize_t			n;
29441 
29442 	n = sendto( inSock, (char *) me->responsePtr, me->responseLen, 0, inDest, SockAddrGetSize( inDest ) );
29443 	err = map_socket_value_errno( inSock, n == (ssize_t) me->responseLen, n );
29444 	return( err );
29445 }
29446 
29447 //===========================================================================================================================
29448 //	_MDNSColliderSendProbe
29449 //===========================================================================================================================
29450 
_MDNSColliderSendProbe(MDNSColliderRef me,SocketRef inSock,const struct sockaddr * inDest)29451 static OSStatus	_MDNSColliderSendProbe( MDNSColliderRef me, SocketRef inSock, const struct sockaddr *inDest )
29452 {
29453 	OSStatus		err;
29454 	ssize_t			n;
29455 
29456 	n = sendto( inSock, (char *) me->probePtr, me->probeLen, 0, inDest, SockAddrGetSize( inDest ) );
29457 	err = map_socket_value_errno( inSock, n == (ssize_t) me->probeLen, n );
29458 	return( err );
29459 }
29460 
29461 //===========================================================================================================================
29462 //	ServiceBrowserCreate
29463 //===========================================================================================================================
29464 
29465 typedef struct SBDomain					SBDomain;
29466 typedef struct SBServiceType			SBServiceType;
29467 typedef struct SBServiceBrowse			SBServiceBrowse;
29468 typedef struct SBServiceInstance		SBServiceInstance;
29469 typedef struct SBIPAddress				SBIPAddress;
29470 
29471 struct ServiceBrowserPrivate
29472 {
29473 	CFRuntimeBase					base;				// CF object base.
29474 	dispatch_queue_t				queue;				// Queue for service browser's events.
29475 	DNSServiceRef					connection;			// Shared connection for DNS-SD ops.
29476 	DNSServiceRef					domainsQuery;		// Query for recommended browsing domains.
29477 	char *							domain;				// If non-null, then browsing is limited to this domain.
29478 	StringListItem *				serviceTypeList;	// If non-null, then browsing is limited to these service types.
29479 	ServiceBrowserCallback_f		userCallback;		// User's callback. Called when browsing stops.
29480 	void *							userContext;		// User's callback context.
29481 	SBDomain *						domainList;			// List of domains and their browse results.
29482 	dispatch_source_t				stopTimer;			// Timer to stop browsing after browseTimeSecs.
29483 	uint32_t						ifIndex;			// If non-zero, then browsing is limited to this interface.
29484 	unsigned int					browseTimeSecs;		// Amount of time to spend browsing in seconds.
29485 #if( MDNSRESPONDER_PROJECT )
29486 	Boolean							useNewGAI;			// Use dnssd_getaddrinfo_* instead of DNSServiceGetAddrInfo().
29487 #endif
29488 	Boolean							includeAWDL;		// True if the IncludeAWDL flag should be used for DNS-SD ops that
29489 														// use the "any" interface.
29490 };
29491 
29492 struct SBDomain
29493 {
29494 	SBDomain *				next;			// Next domain object in list.
29495 	ServiceBrowserRef		browser;		// Pointer to parent service browser.
29496 	char *					name;			// Name of the domain.
29497 	DNSServiceRef			servicesQuery;	// Query for services (_services._dns-sd._udp.<domain> PTR record) in domain.
29498 	SBServiceType *			typeList;		// List of service types to browse for in this domain.
29499 };
29500 
29501 struct SBServiceType
29502 {
29503 	SBServiceType *			next;		// Next service type object in list.
29504 	char *					name;		// Name of the service type.
29505 	SBServiceBrowse *		browseList;	// List of browses for this service type.
29506 };
29507 
29508 struct SBServiceBrowse
29509 {
29510 	SBServiceBrowse *		next;			// Next browse object in list.
29511 	ServiceBrowserRef		browser;		// Pointer to parent service browser.
29512 	DNSServiceRef			browse;			// Reference to DNSServiceBrowse op.
29513 	SBServiceInstance *		instanceList;	// List of service instances that were discovered by this browse.
29514 	uint64_t				startTicks;		// Value of UpTicks() when the browse op began.
29515 	uint32_t				ifIndex;		// If non-zero, then the browse is limited to this interface.
29516 };
29517 
29518 struct SBServiceInstance
29519 {
29520 	SBServiceInstance *		next;				// Next service instance object in list.
29521 	ServiceBrowserRef		browser;			// Pointer to parent service browser.
29522 	char *					name;				// Name of the service instance.
29523 	char *					fqdn;				// Fully qualified domain name of service instance (for logging/debugging).
29524 	uint32_t				ifIndex;			// Index of interface over which this service instance was discovered.
29525 	uint64_t				discoverTimeUs;		// Time it took to discover this service instance in microseconds.
29526 	DNSServiceRef			resolve;			// Reference to DNSServiceResolve op for this service instance.
29527 	uint64_t				resolveStartTicks;	// Value of UpTicks() when the DNSServiceResolve op began.
29528 	uint64_t				resolveTimeUs;		// Time it took to resolve this service instance.
29529 	char *					hostname;			// Service instance's hostname. Result of DNSServiceResolve.
29530 	uint16_t				port;				// Service instance's port number. Result of DNSServiceResolve.
29531 	uint8_t *				txtPtr;				// Service instance's TXT record data. Result of DNSServiceResolve.
29532 	size_t					txtLen;				// Length of service instance's TXT record data.
29533 	DNSServiceRef			gai;				// Reference to DNSServiceGetAddrInfo op for service instance's hostname.
29534 #if( MDNSRESPONDER_PROJECT )
29535 	dnssd_getaddrinfo_t		newGAI;				// Reference to dnssd_getaddrinfo object for service instance's hostname.
29536 #endif
29537 	uint64_t				gaiStartTicks;		// Value of UpTicks() when the DNSServiceGetAddrInfo op began.
29538 	SBIPAddress *			ipaddrList;			// List of IP addresses that the hostname resolved to.
29539 	int32_t					refCount;			// This object's reference count.
29540 };
29541 
29542 struct SBIPAddress
29543 {
29544 	SBIPAddress *		next;			// Next IP address object in list.
29545 	sockaddr_ip			sip;			// IPv4 or IPv6 address.
29546 	uint64_t			resolveTimeUs;	// Time it took to resolve this IP address in microseconds.
29547 };
29548 
29549 typedef struct
29550 {
29551 	SBRDomain *		domainList;	// List of domains in which services were found.
29552 	int32_t			refCount;	// This object's reference count.
29553 
29554 }	ServiceBrowserResultsPrivate;
29555 
29556 static void		_ServiceBrowserStop( ServiceBrowserRef me, OSStatus inError );
29557 static OSStatus	_ServiceBrowserAddDomain( ServiceBrowserRef inBrowser, const char *inDomain );
29558 static OSStatus	_ServiceBrowserRemoveDomain( ServiceBrowserRef inBrowser, const char *inName );
29559 static void		_ServiceBrowserTimerHandler( void *inContext );
29560 static void DNSSD_API
29561 	_ServiceBrowserDomainsQueryCallback(
29562 		DNSServiceRef			inSDRef,
29563 		DNSServiceFlags			inFlags,
29564 		uint32_t				inInterfaceIndex,
29565 		DNSServiceErrorType		inError,
29566 		const char *			inFullName,
29567 		uint16_t				inType,
29568 		uint16_t				inClass,
29569 		uint16_t				inRDataLen,
29570 		const void *			inRDataPtr,
29571 		uint32_t				inTTL,
29572 		void *					inContext );
29573 static void DNSSD_API
29574 	_ServiceBrowserServicesQueryCallback(
29575 		DNSServiceRef			inSDRef,
29576 		DNSServiceFlags			inFlags,
29577 		uint32_t				inInterfaceIndex,
29578 		DNSServiceErrorType		inError,
29579 		const char *			inFullName,
29580 		uint16_t				inType,
29581 		uint16_t				inClass,
29582 		uint16_t				inRDataLen,
29583 		const void *			inRDataPtr,
29584 		uint32_t				inTTL,
29585 		void *					inContext );
29586 static void DNSSD_API
29587 	_ServiceBrowserBrowseCallback(
29588 		DNSServiceRef		inSDRef,
29589 		DNSServiceFlags		inFlags,
29590 		uint32_t			inInterfaceIndex,
29591 		DNSServiceErrorType	inError,
29592 		const char *		inName,
29593 		const char *		inRegType,
29594 		const char *		inDomain,
29595 		void *				inContext );
29596 static void DNSSD_API
29597 	_ServiceBrowserResolveCallback(
29598 		DNSServiceRef			inSDRef,
29599 		DNSServiceFlags			inFlags,
29600 		uint32_t				inInterfaceIndex,
29601 		DNSServiceErrorType		inError,
29602 		const char *			inFullName,
29603 		const char *			inHostname,
29604 		uint16_t				inPort,
29605 		uint16_t				inTXTLen,
29606 		const unsigned char *	inTXTPtr,
29607 		void *					inContext );
29608 #if( MDNSRESPONDER_PROJECT )
29609 static void
29610 	_ServiceBrowserGAIResultHandler(
29611 		ServiceBrowserRef				inBrowser,
29612 		SBServiceInstance *				inInstance,
29613 		dnssd_getaddrinfo_result_t *	inResultArray,
29614 		size_t							inResultCount );
29615 #endif
29616 static void DNSSD_API
29617 	_ServiceBrowserGAICallback(
29618 		DNSServiceRef			inSDRef,
29619 		DNSServiceFlags			inFlags,
29620 		uint32_t				inInterfaceIndex,
29621 		DNSServiceErrorType		inError,
29622 		const char *			inHostname,
29623 		const struct sockaddr *	inSockAddr,
29624 		uint32_t				inTTL,
29625 		void *					inContext );
29626 static OSStatus
29627 	_ServiceBrowserAddServiceType(
29628 		ServiceBrowserRef	inBrowser,
29629 		SBDomain *			inDomain,
29630 		const char *		inName,
29631 		uint32_t			inIfIndex );
29632 static OSStatus
29633 	_ServiceBrowserRemoveServiceType(
29634 		ServiceBrowserRef	inBrowser,
29635 		SBDomain *			inDomain,
29636 		const char *		inName,
29637 		uint32_t			inIfIndex );
29638 static OSStatus
29639 	_ServiceBrowserAddServiceInstance(
29640 		ServiceBrowserRef	inBrowser,
29641 		SBServiceBrowse *	inBrowse,
29642 		uint32_t			inIfIndex,
29643 		const char *		inName,
29644 		const char *		inRegType,
29645 		const char *		inDomain,
29646 		uint64_t			inDiscoverTimeUs );
29647 static OSStatus
29648 	_ServiceBrowserRemoveServiceInstance(
29649 		ServiceBrowserRef	inBrowser,
29650 		SBServiceBrowse *	inBrowse,
29651 		const char *		inName,
29652 		uint32_t			inIfIndex );
29653 static OSStatus
29654 	_ServiceBrowserAddIPAddress(
29655 		ServiceBrowserRef		inBrowser,
29656 		SBServiceInstance *		inInstance,
29657 		const struct sockaddr *	inSockAddr,
29658 		uint64_t				inResolveTimeUs );
29659 static OSStatus
29660 	_ServiceBrowserRemoveIPAddress(
29661 		ServiceBrowserRef		inBrowser,
29662 		SBServiceInstance *		inInstance,
29663 		const struct sockaddr *	inSockAddr );
29664 static OSStatus	_ServiceBrowserCreateResults( ServiceBrowserRef me, ServiceBrowserResults **outResults );
29665 static OSStatus	_SBDomainCreate( const char *inName, ServiceBrowserRef inBrowser, SBDomain **outDomain );
29666 static void		_SBDomainFree( SBDomain *inDomain );
29667 static OSStatus	_SBServiceTypeCreate( const char *inName, SBServiceType **outType );
29668 static void		_SBServiceTypeFree( SBServiceType *inType );
29669 static OSStatus	_SBServiceBrowseCreate( uint32_t inIfIndex, ServiceBrowserRef inBrowser, SBServiceBrowse **outBrowse );
29670 static void		_SBServiceBrowseFree( SBServiceBrowse *inBrowse );
29671 static OSStatus
29672 	_SBServiceInstanceCreate(
29673 		const char *			inName,
29674 		const char *			inType,
29675 		const char *			inDomain,
29676 		uint32_t				inIfIndex,
29677 		uint64_t				inDiscoverTimeUs,
29678 		ServiceBrowserRef		inBrowser,
29679 		SBServiceInstance **	outInstance );
29680 #if( MDNSRESPONDER_PROJECT )
29681 static void		_SBServiceInstanceRetain( SBServiceInstance *inInstance );
29682 #endif
29683 static void		_SBServiceInstanceStop( SBServiceInstance *inInstance );
29684 static void		_SBServiceInstanceRelease( SBServiceInstance *inInstance );
29685 #define _SBServiceInstanceForget( X )		ForgetCustomEx( X, _SBServiceInstanceStop, _SBServiceInstanceRelease )
29686 static OSStatus
29687 	_SBIPAddressCreate(
29688 		const struct sockaddr *	inSockAddr,
29689 		uint64_t				inResolveTimeUs,
29690 		SBIPAddress **			outIPAddress );
29691 static void		_SBIPAddressFree( SBIPAddress *inIPAddress );
29692 static void		_SBIPAddressFreeList( SBIPAddress *inList );
29693 static OSStatus	_SBRDomainCreate( const char *inName, SBRDomain **outDomain );
29694 static void		_SBRDomainFree( SBRDomain *inDomain );
29695 static OSStatus	_SBRServiceTypeCreate( const char *inName, SBRServiceType **outType );
29696 static void		_SBRServiceTypeFree( SBRServiceType *inType );
29697 static OSStatus
29698 	_SBRServiceInstanceCreate(
29699 		const char *			inName,
29700 		uint32_t				inInterfaceIndex,
29701 		const char *			inHostname,
29702 		uint16_t				inPort,
29703 		const uint8_t *			inTXTPtr,
29704 		size_t					inTXTLen,
29705 		uint64_t				inDiscoverTimeUs,
29706 		uint64_t				inResolveTimeUs,
29707 		SBRServiceInstance **	outInstance );
29708 static void		_SBRServiceInstanceFree( SBRServiceInstance *inInstance );
29709 static OSStatus
29710 	_SBRIPAddressCreate(
29711 		const struct sockaddr *	inSockAddr,
29712 		uint64_t				inResolveTimeUs,
29713 		SBRIPAddress **			outIPAddress );
29714 static void		_SBRIPAddressFree( SBRIPAddress *inIPAddress );
29715 
29716 #define ForgetSBIPAddressList( X )		ForgetCustom( X, _SBIPAddressFreeList )
29717 
29718 CF_CLASS_DEFINE( ServiceBrowser );
29719 
29720 ulog_define_ex( kDNSSDUtilIdentifier, ServiceBrowser, kLogLevelTrace, kLogFlags_None, "ServiceBrowser", NULL );
29721 #define sb_ulog( LEVEL, ... )		ulog( &log_category_from_name( ServiceBrowser ), (LEVEL), __VA_ARGS__ )
29722 
29723 static OSStatus
ServiceBrowserCreate(dispatch_queue_t inQueue,uint32_t inInterfaceIndex,const char * inDomain,unsigned int inBrowseTimeSecs,Boolean inIncludeAWDL,ServiceBrowserRef * outBrowser)29724 	ServiceBrowserCreate(
29725 		dispatch_queue_t	inQueue,
29726 		uint32_t			inInterfaceIndex,
29727 		const char *		inDomain,
29728 		unsigned int		inBrowseTimeSecs,
29729 		Boolean				inIncludeAWDL,
29730 		ServiceBrowserRef *	outBrowser )
29731 {
29732 	OSStatus				err;
29733 	ServiceBrowserRef		obj;
29734 
29735 	CF_OBJECT_CREATE( ServiceBrowser, obj, err, exit );
29736 
29737 	ReplaceDispatchQueue( &obj->queue, inQueue );
29738 	obj->ifIndex		= inInterfaceIndex;
29739 	if( inDomain )
29740 	{
29741 		obj->domain = strdup( inDomain );
29742 		require_action( obj->domain, exit, err = kNoMemoryErr );
29743 	}
29744 	obj->browseTimeSecs	= inBrowseTimeSecs;
29745 	obj->includeAWDL	= inIncludeAWDL;
29746 
29747 	*outBrowser = obj;
29748 	obj = NULL;
29749 	err = kNoErr;
29750 
29751 exit:
29752 	CFReleaseNullSafe( obj );
29753 	return( err );
29754 }
29755 
29756 //===========================================================================================================================
29757 //	_ServiceBrowserFinalize
29758 //===========================================================================================================================
29759 
_ServiceBrowserFinalize(CFTypeRef inObj)29760 static void	_ServiceBrowserFinalize( CFTypeRef inObj )
29761 {
29762 	ServiceBrowserRef const		me = (ServiceBrowserRef) inObj;
29763 	StringListItem *			serviceType;
29764 
29765 	dispatch_forget( &me->queue );
29766 	check( !me->connection );
29767 	check( !me->domainsQuery );
29768 	ForgetMem( &me->domain );
29769 	while( ( serviceType = me->serviceTypeList ) != NULL )
29770 	{
29771 		me->serviceTypeList = serviceType->next;
29772 		ForgetMem( &serviceType->str );
29773 		free( serviceType );
29774 	}
29775 	check( !me->domainList );
29776 	check( !me->stopTimer );
29777 }
29778 
29779 #if( MDNSRESPONDER_PROJECT )
29780 //===========================================================================================================================
29781 //	ServiceBrowserSetUseNewGAI
29782 //===========================================================================================================================
29783 
ServiceBrowserSetUseNewGAI(ServiceBrowserRef me,Boolean inUseNewGAI)29784 static void	ServiceBrowserSetUseNewGAI( ServiceBrowserRef me, Boolean inUseNewGAI )
29785 {
29786 	me->useNewGAI = inUseNewGAI;
29787 }
29788 #endif
29789 
29790 //===========================================================================================================================
29791 //	ServiceBrowserStart
29792 //===========================================================================================================================
29793 
29794 static void	_ServiceBrowserStart( void *inContext );
29795 
ServiceBrowserStart(ServiceBrowserRef me)29796 static void	ServiceBrowserStart( ServiceBrowserRef me )
29797 {
29798 	CFRetain( me );
29799 	dispatch_async_f( me->queue, me, _ServiceBrowserStart );
29800 }
29801 
_ServiceBrowserStart(void * inContext)29802 static void	_ServiceBrowserStart( void *inContext )
29803 {
29804 	OSStatus					err;
29805 	ServiceBrowserRef const		me = (ServiceBrowserRef) inContext;
29806 
29807 	err = DNSServiceCreateConnection( &me->connection );
29808 	require_noerr( err, exit );
29809 
29810 	err = DNSServiceSetDispatchQueue( me->connection, me->queue );
29811 	require_noerr( err, exit );
29812 
29813 	if( me->domain )
29814 	{
29815 		err = _ServiceBrowserAddDomain( me, me->domain );
29816 		require_noerr( err, exit );
29817 	}
29818 	else
29819 	{
29820 		DNSServiceRef			sdRef;
29821 		const char * const		recordName	= "b._dns-sd._udp.local.";
29822 		const uint32_t			ifIndex		= kDNSServiceInterfaceIndexLocalOnly;
29823 
29824 		// Perform PTR meta-query for "b._dns-sd._udp.local." to enumerate recommended browsing domains.
29825 		// See <https://tools.ietf.org/html/rfc6763#section-11>.
29826 
29827 		sb_ulog( kLogLevelTrace, "Starting PTR QueryRecord on interface %d for %s", (int32_t) ifIndex, recordName );
29828 
29829 		sdRef = me->connection;
29830 		err = DNSServiceQueryRecord( &sdRef, kDNSServiceFlagsShareConnection, ifIndex, recordName,
29831 			kDNSServiceType_PTR, kDNSServiceClass_IN, _ServiceBrowserDomainsQueryCallback, me );
29832 		require_noerr( err, exit );
29833 
29834 		me->domainsQuery = sdRef;
29835 	}
29836 
29837 	err = DispatchTimerCreate( dispatch_time_seconds( me->browseTimeSecs ), DISPATCH_TIME_FOREVER,
29838 		100 * kNanosecondsPerMillisecond, me->queue, _ServiceBrowserTimerHandler, NULL, me, &me->stopTimer );
29839 	require_noerr( err, exit );
29840 	dispatch_resume( me->stopTimer );
29841 
29842 exit:
29843 	if( err ) _ServiceBrowserStop( me, err );
29844 }
29845 
29846 //===========================================================================================================================
29847 //	ServiceBrowserAddServiceType
29848 //===========================================================================================================================
29849 
ServiceBrowserAddServiceType(ServiceBrowserRef me,const char * inServiceType)29850 static OSStatus	ServiceBrowserAddServiceType( ServiceBrowserRef me, const char *inServiceType )
29851 {
29852 	OSStatus				err;
29853 	StringListItem *		item;
29854 	StringListItem **		itemPtr;
29855 	StringListItem *		newItem = NULL;
29856 
29857 	for( itemPtr = &me->serviceTypeList; ( item = *itemPtr ) != NULL; itemPtr = &item->next )
29858 	{
29859 		if( strcmp( item->str, inServiceType ) == 0 ) break;
29860 	}
29861 	if( !item )
29862 	{
29863 		newItem = (StringListItem *) calloc( 1, sizeof( *newItem ) );
29864 		require_action( newItem, exit, err = kNoMemoryErr );
29865 
29866 		newItem->str = strdup( inServiceType );
29867 		require_action( newItem->str, exit, err = kNoMemoryErr );
29868 
29869 		*itemPtr = newItem;
29870 		newItem = NULL;
29871 	}
29872 	err = kNoErr;
29873 
29874 exit:
29875 	FreeNullSafe( newItem );
29876 	return( err );
29877 }
29878 
29879 //===========================================================================================================================
29880 //	ServiceBrowserSetCallback
29881 //===========================================================================================================================
29882 
ServiceBrowserSetCallback(ServiceBrowserRef me,ServiceBrowserCallback_f inCallback,void * inContext)29883 static void	ServiceBrowserSetCallback( ServiceBrowserRef me, ServiceBrowserCallback_f inCallback, void *inContext )
29884 {
29885 	me->userCallback	= inCallback;
29886 	me->userContext		= inContext;
29887 }
29888 
29889 //===========================================================================================================================
29890 //	ServiceBrowserResultsRetain
29891 //===========================================================================================================================
29892 
ServiceBrowserResultsRetain(ServiceBrowserResults * inResults)29893 static void	ServiceBrowserResultsRetain( ServiceBrowserResults *inResults )
29894 {
29895 	ServiceBrowserResultsPrivate * const		results = (ServiceBrowserResultsPrivate *) inResults;
29896 
29897 	atomic_add_32( &results->refCount, 1 );
29898 }
29899 
29900 //===========================================================================================================================
29901 //	ServiceBrowserResultsRelease
29902 //===========================================================================================================================
29903 
ServiceBrowserResultsRelease(ServiceBrowserResults * inResults)29904 static void	ServiceBrowserResultsRelease( ServiceBrowserResults *inResults )
29905 {
29906 	ServiceBrowserResultsPrivate * const		results = (ServiceBrowserResultsPrivate *) inResults;
29907 	SBRDomain *									domain;
29908 
29909 	if( atomic_add_and_fetch_32( &results->refCount, -1 ) == 0 )
29910 	{
29911 		while( ( domain = inResults->domainList ) != NULL )
29912 		{
29913 			inResults->domainList = domain->next;
29914 			_SBRDomainFree( domain );
29915 		}
29916 		free( inResults );
29917 	}
29918 }
29919 
29920 //===========================================================================================================================
29921 //	_ServiceBrowserStop
29922 //===========================================================================================================================
29923 
_ServiceBrowserStop(ServiceBrowserRef me,OSStatus inError)29924 static void	_ServiceBrowserStop( ServiceBrowserRef me, OSStatus inError )
29925 {
29926 	OSStatus		err;
29927 	SBDomain *		d;
29928 
29929 	dispatch_source_forget( &me->stopTimer );
29930 	DNSServiceForget( &me->domainsQuery );
29931 	for( d = me->domainList; d; d = d->next )
29932 	{
29933 		SBServiceType *		t;
29934 
29935 		DNSServiceForget( &d->servicesQuery );
29936 		for( t = d->typeList; t; t = t->next )
29937 		{
29938 			SBServiceBrowse *		b;
29939 
29940 			for( b = t->browseList; b; b = b->next )
29941 			{
29942 				SBServiceInstance *		i;
29943 
29944 				DNSServiceForget( &b->browse );
29945 				for( i = b->instanceList; i; i = i->next )
29946 				{
29947 					_SBServiceInstanceStop( i );
29948 				}
29949 			}
29950 		}
29951 	}
29952 	DNSServiceForget( &me->connection );
29953 
29954 	if( me->userCallback )
29955 	{
29956 		ServiceBrowserResults *		results = NULL;
29957 
29958 		err = _ServiceBrowserCreateResults( me, &results );
29959 		if( !err ) err = inError;
29960 
29961 		me->userCallback( results, err, me->userContext );
29962 		me->userCallback	= NULL;
29963 		me->userContext		= NULL;
29964 		if( results ) ServiceBrowserResultsRelease( results );
29965 	}
29966 
29967 	while( ( d = me->domainList ) != NULL )
29968 	{
29969 		me->domainList = d->next;
29970 		_SBDomainFree( d );
29971 	}
29972 	CFRelease( me );
29973 }
29974 
29975 //===========================================================================================================================
29976 //	_ServiceBrowserAddDomain
29977 //===========================================================================================================================
29978 
_ServiceBrowserAddDomain(ServiceBrowserRef me,const char * inDomain)29979 static OSStatus	_ServiceBrowserAddDomain( ServiceBrowserRef me, const char *inDomain )
29980 {
29981 	OSStatus		err;
29982 	SBDomain *		domain;
29983 	SBDomain **		domainPtr;
29984 	SBDomain *		newDomain = NULL;
29985 
29986 	for( domainPtr = &me->domainList; ( domain = *domainPtr ) != NULL; domainPtr = &domain->next )
29987 	{
29988 		if( strcasecmp( domain->name, inDomain ) == 0 ) break;
29989 	}
29990 	require_action_quiet( !domain, exit, err = kDuplicateErr );
29991 
29992 	err = _SBDomainCreate( inDomain, me, &newDomain );
29993 	require_noerr_quiet( err, exit );
29994 
29995 	if( me->serviceTypeList )
29996 	{
29997 		const StringListItem *		item;
29998 
29999 		for( item = me->serviceTypeList; item; item = item->next )
30000 		{
30001 			err = _ServiceBrowserAddServiceType( me, newDomain, item->str, me->ifIndex );
30002 			if( err == kDuplicateErr ) err = kNoErr;
30003 			require_noerr( err, exit );
30004 		}
30005 	}
30006 	else
30007 	{
30008 		char *				recordName;
30009 		DNSServiceRef		sdRef;
30010 		DNSServiceFlags		flags;
30011 
30012 		// Perform PTR meta-query for _services._dns-sd._udp.<domain> to enumerate service types in domain.
30013 		// See <https://tools.ietf.org/html/rfc6763#section-9>.
30014 
30015 		ASPrintF( &recordName, "_services._dns-sd._udp.%s", newDomain->name );
30016 		require_action( recordName, exit, err = kNoMemoryErr );
30017 
30018 		flags = kDNSServiceFlagsShareConnection;
30019 		if( ( me->ifIndex == kDNSServiceInterfaceIndexAny ) && me->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
30020 
30021 		sb_ulog( kLogLevelTrace, "Starting PTR QueryRecord on interface %d for %s", (int32_t) me->ifIndex, recordName );
30022 
30023 		sdRef = newDomain->browser->connection;
30024 		err = DNSServiceQueryRecord( &sdRef, flags, me->ifIndex, recordName, kDNSServiceType_PTR, kDNSServiceClass_IN,
30025 			_ServiceBrowserServicesQueryCallback, newDomain );
30026 		free( recordName );
30027 		require_noerr( err, exit );
30028 
30029 		newDomain->servicesQuery = sdRef;
30030 	}
30031 
30032 	*domainPtr	= newDomain;
30033 	newDomain	= NULL;
30034 	err = kNoErr;
30035 
30036 exit:
30037 	if( newDomain ) _SBDomainFree( newDomain );
30038 	return( err );
30039 }
30040 
30041 //===========================================================================================================================
30042 //	_ServiceBrowserRemoveDomain
30043 //===========================================================================================================================
30044 
_ServiceBrowserRemoveDomain(ServiceBrowserRef me,const char * inName)30045 static OSStatus	_ServiceBrowserRemoveDomain( ServiceBrowserRef me, const char *inName )
30046 {
30047 	OSStatus		err;
30048 	SBDomain *		domain;
30049 	SBDomain **		domainPtr;
30050 
30051 	for( domainPtr = &me->domainList; ( domain = *domainPtr ) != NULL; domainPtr = &domain->next )
30052 	{
30053 		if( strcasecmp( domain->name, inName ) == 0 ) break;
30054 	}
30055 
30056 	if( domain )
30057 	{
30058 		*domainPtr = domain->next;
30059 		_SBDomainFree( domain );
30060 		err = kNoErr;
30061 	}
30062 	else
30063 	{
30064 		err = kNotFoundErr;
30065 	}
30066 
30067 	return( err );
30068 }
30069 
30070 //===========================================================================================================================
30071 //	_ServiceBrowserTimerHandler
30072 //===========================================================================================================================
30073 
_ServiceBrowserTimerHandler(void * inContext)30074 static void	_ServiceBrowserTimerHandler( void *inContext )
30075 {
30076 	ServiceBrowserRef const		me = (ServiceBrowserRef) inContext;
30077 
30078 	_ServiceBrowserStop( me, kNoErr );
30079 }
30080 
30081 //===========================================================================================================================
30082 //	_ServiceBrowserDomainsQueryCallback
30083 //===========================================================================================================================
30084 
30085 static void DNSSD_API
_ServiceBrowserDomainsQueryCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inFullName,uint16_t inType,uint16_t inClass,uint16_t inRDataLen,const void * inRDataPtr,uint32_t inTTL,void * inContext)30086 	_ServiceBrowserDomainsQueryCallback(
30087 		DNSServiceRef			inSDRef,
30088 		DNSServiceFlags			inFlags,
30089 		uint32_t				inInterfaceIndex,
30090 		DNSServiceErrorType		inError,
30091 		const char *			inFullName,
30092 		uint16_t				inType,
30093 		uint16_t				inClass,
30094 		uint16_t				inRDataLen,
30095 		const void *			inRDataPtr,
30096 		uint32_t				inTTL,
30097 		void *					inContext )
30098 {
30099 	ServiceBrowserRef const		me = (ServiceBrowserRef) inContext;
30100 	OSStatus					err;
30101 	char						domainStr[ kDNSServiceMaxDomainName ];
30102 
30103 	Unused( inSDRef );
30104 	Unused( inClass );
30105 	Unused( inTTL );
30106 
30107 	sb_ulog( kLogLevelTrace, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
30108 		DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inFullName, inType, inRDataPtr, inRDataLen,
30109 		!inError, inError );
30110 
30111 	require_noerr( inError, exit );
30112 
30113 	err = DomainNameToString( inRDataPtr, ( (const uint8_t *) inRDataPtr ) + inRDataLen, domainStr, NULL );
30114 	require_noerr( err, exit );
30115 
30116 	if( inFlags & kDNSServiceFlagsAdd )
30117 	{
30118 		err = _ServiceBrowserAddDomain( me, domainStr );
30119 		if( err == kDuplicateErr ) err = kNoErr;
30120 		require_noerr( err, exit );
30121 	}
30122 	else
30123 	{
30124 		err = _ServiceBrowserRemoveDomain( me, domainStr );
30125 		if( err == kNotFoundErr ) err = kNoErr;
30126 		require_noerr( err, exit );
30127 	}
30128 
30129 exit:
30130 	return;
30131 }
30132 
30133 //===========================================================================================================================
30134 //	_ServiceBrowserServicesQueryCallback
30135 //===========================================================================================================================
30136 
30137 static void DNSSD_API
_ServiceBrowserServicesQueryCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inFullName,uint16_t inType,uint16_t inClass,uint16_t inRDataLen,const void * inRDataPtr,uint32_t inTTL,void * inContext)30138 	_ServiceBrowserServicesQueryCallback(
30139 		DNSServiceRef			inSDRef,
30140 		DNSServiceFlags			inFlags,
30141 		uint32_t				inInterfaceIndex,
30142 		DNSServiceErrorType		inError,
30143 		const char *			inFullName,
30144 		uint16_t				inType,
30145 		uint16_t				inClass,
30146 		uint16_t				inRDataLen,
30147 		const void *			inRDataPtr,
30148 		uint32_t				inTTL,
30149 		void *					inContext )
30150 {
30151 	OSStatus					err;
30152 	SBDomain * const			domain	= (SBDomain *) inContext;
30153 	ServiceBrowserRef const		me		= domain->browser;
30154 	const uint8_t *				src;
30155 	const uint8_t *				end;
30156 	uint8_t *					dst;
30157 	int							i;
30158 	uint8_t						serviceType[ 2 * ( 1 + kDomainLabelLengthMax ) + 1 ];
30159 	char						serviceTypeStr[ kDNSServiceMaxDomainName ];
30160 
30161 	Unused( inSDRef );
30162 	Unused( inTTL );
30163 	Unused( inClass );
30164 
30165 	sb_ulog( kLogLevelTrace, "QueryRecord result: %s on interface %d for %s -> %{du:rdata}%?{end} (error: %#m)",
30166 		DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inFullName, inType, inRDataPtr, inRDataLen,
30167 		!inError, inError );
30168 
30169 	require_noerr( inError, exit );
30170 
30171 	check( inType  == kDNSServiceType_PTR );
30172 	check( inClass == kDNSServiceClass_IN );
30173 
30174 	// The first two labels of the domain name in the RDATA describe a service type.
30175 	// See <https://tools.ietf.org/html/rfc6763#section-9>.
30176 
30177 	src = (const uint8_t *) inRDataPtr;
30178 	end = src + inRDataLen;
30179 	dst = serviceType;
30180 	for( i = 0; i < 2; ++i )
30181 	{
30182 		size_t		labelLen;
30183 
30184 		require_action_quiet( ( end - src ) > 0, exit, err = kUnderrunErr );
30185 
30186 		labelLen = *src;
30187 		require_action_quiet( ( labelLen > 0 ) && ( labelLen <= kDomainLabelLengthMax ), exit, err = kMalformedErr );
30188 		require_action_quiet( ( (size_t)( end - src ) ) >= ( 1 + labelLen ), exit, err = kUnderrunErr );
30189 
30190 		memcpy( dst, src, 1 + labelLen );
30191 		src += 1 + labelLen;
30192 		dst += 1 + labelLen;
30193 	}
30194 	*dst = 0;
30195 
30196 	err = DomainNameToString( serviceType, NULL, serviceTypeStr, NULL );
30197 	require_noerr( err, exit );
30198 
30199 	if( inFlags & kDNSServiceFlagsAdd )
30200 	{
30201 		err = _ServiceBrowserAddServiceType( me, domain, serviceTypeStr, inInterfaceIndex );
30202 		if( err == kDuplicateErr ) err = kNoErr;
30203 		require_noerr( err, exit );
30204 	}
30205 	else
30206 	{
30207 		err = _ServiceBrowserRemoveServiceType( me, domain, serviceTypeStr, inInterfaceIndex );
30208 		if( err == kNotFoundErr ) err = kNoErr;
30209 		require_noerr( err, exit );
30210 	}
30211 
30212 exit:
30213 	return;
30214 }
30215 
30216 //===========================================================================================================================
30217 //	_ServiceBrowserBrowseCallback
30218 //===========================================================================================================================
30219 
30220 static void DNSSD_API
_ServiceBrowserBrowseCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inName,const char * inRegType,const char * inDomain,void * inContext)30221 	_ServiceBrowserBrowseCallback(
30222 		DNSServiceRef		inSDRef,
30223 		DNSServiceFlags		inFlags,
30224 		uint32_t			inInterfaceIndex,
30225 		DNSServiceErrorType	inError,
30226 		const char *		inName,
30227 		const char *		inRegType,
30228 		const char *		inDomain,
30229 		void *				inContext )
30230 {
30231 	OSStatus					err;
30232 	const uint64_t				nowTicks	= UpTicks();
30233 	SBServiceBrowse * const		browse		= (SBServiceBrowse *) inContext;
30234 	ServiceBrowserRef const		me			= (ServiceBrowserRef) browse->browser;
30235 
30236 	Unused( inSDRef );
30237 
30238 	sb_ulog( kLogLevelTrace, "Browse result: %s on interface %d for %s.%s%s%?{end} (error: %#m)",
30239 		DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, inName, inRegType, inDomain, !inError, inError );
30240 
30241 	require_noerr( inError, exit );
30242 
30243 	if( inFlags & kDNSServiceFlagsAdd )
30244 	{
30245 		err = _ServiceBrowserAddServiceInstance( me, browse, inInterfaceIndex, inName, inRegType, inDomain,
30246 			UpTicksToMicroseconds( nowTicks - browse->startTicks ) );
30247 		if( err == kDuplicateErr ) err = kNoErr;
30248 		require_noerr( err, exit );
30249 	}
30250 	else
30251 	{
30252 		err = _ServiceBrowserRemoveServiceInstance( me, browse, inName, inInterfaceIndex );
30253 		if( err == kNotFoundErr ) err = kNoErr;
30254 		require_noerr( err, exit );
30255 	}
30256 
30257 exit:
30258 	return;
30259 }
30260 
30261 //===========================================================================================================================
30262 //	_ServiceBrowserResolveCallback
30263 //===========================================================================================================================
30264 
30265 static void DNSSD_API
_ServiceBrowserResolveCallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inFullName,const char * inHostname,uint16_t inPort,uint16_t inTXTLen,const unsigned char * inTXTPtr,void * inContext)30266 	_ServiceBrowserResolveCallback(
30267 		DNSServiceRef			inSDRef,
30268 		DNSServiceFlags			inFlags,
30269 		uint32_t				inInterfaceIndex,
30270 		DNSServiceErrorType		inError,
30271 		const char *			inFullName,
30272 		const char *			inHostname,
30273 		uint16_t				inPort,
30274 		uint16_t				inTXTLen,
30275 		const unsigned char *	inTXTPtr,
30276 		void *					inContext )
30277 {
30278 	OSStatus						err;
30279 	const uint64_t					nowTicks	= UpTicks();
30280 	SBServiceInstance * const		instance	= (SBServiceInstance *) inContext;
30281 	ServiceBrowserRef const			me			= (ServiceBrowserRef) instance->browser;
30282 
30283 	Unused( inSDRef );
30284 	Unused( inFlags );
30285 
30286 	sb_ulog( kLogLevelTrace, "Resolve result on interface %d for %s -> %s:%u %#{txt}%?{end} (error %#m)",
30287 		(int32_t) inInterfaceIndex, inFullName, inHostname, inPort, inTXTPtr, (size_t) inTXTLen, !inError, inError );
30288 
30289 	require_noerr( inError, exit );
30290 
30291 	if( !MemEqual( instance->txtPtr, instance->txtLen, inTXTPtr, inTXTLen ) )
30292 	{
30293 		FreeNullSafe( instance->txtPtr );
30294 		instance->txtPtr = _memdup( inTXTPtr, inTXTLen );
30295 		require_action( instance->txtPtr, exit, err = kNoMemoryErr );
30296 
30297 		instance->txtLen = inTXTLen;
30298 	}
30299 
30300 	instance->port = ntohs( inPort );
30301 
30302 	if( !instance->hostname || ( strcasecmp( instance->hostname, inHostname ) != 0 ) )
30303 	{
30304 		DNSServiceRef		sdRef;
30305 
30306 		if( !instance->hostname ) instance->resolveTimeUs = UpTicksToMicroseconds( nowTicks - instance->resolveStartTicks );
30307 
30308 		err = ReplaceString( &instance->hostname, NULL, inHostname, kSizeCString );
30309 		require_noerr( err, exit );
30310 
30311 		sb_ulog( kLogLevelTrace,
30312 			"Starting GetAddrInfo on interface %d for %s", (int32_t) instance->ifIndex, instance->hostname );
30313 
30314 		ForgetSBIPAddressList( &instance->ipaddrList );
30315 
30316 	#if( MDNSRESPONDER_PROJECT )
30317 		if( me->useNewGAI )
30318 		{
30319 			dnssd_getaddrinfo_t		gai;
30320 
30321 			dnssd_getaddrinfo_forget( &instance->newGAI );
30322 
30323 			gai = dnssd_getaddrinfo_create();
30324 			require_action( gai, exit, err = kNoResourcesErr );
30325 
30326 			dnssd_getaddrinfo_set_hostname( gai, instance->hostname );
30327 			dnssd_getaddrinfo_set_flags( gai, 0 );
30328 			dnssd_getaddrinfo_set_interface_index( gai, instance->ifIndex );
30329 			dnssd_getaddrinfo_set_protocols( gai, kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6 );
30330 			dnssd_getaddrinfo_set_queue( gai, me->queue );
30331 			_SBServiceInstanceRetain( instance );
30332 			CFRetain( me );
30333 			dnssd_getaddrinfo_set_result_handler( gai,
30334 			^( dnssd_getaddrinfo_result_t * const inResultArray, const size_t inResultCount )
30335 			{
30336 				if( instance->newGAI == gai )
30337 				{
30338 					_ServiceBrowserGAIResultHandler( me, instance, inResultArray, inResultCount );
30339 				}
30340 			} );
30341 			dnssd_getaddrinfo_set_event_handler( gai,
30342 			^( dnssd_event_t inEvent, DNSServiceErrorType inGAIError )
30343 			{
30344 				switch( inEvent )
30345 				{
30346 					case dnssd_event_invalidated:
30347 						dnssd_release( gai );
30348 						_SBServiceInstanceRelease( instance );
30349 						CFRelease( me );
30350 						break;
30351 
30352 					case dnssd_event_error:
30353 						if( instance->newGAI == gai )
30354 						{
30355 							sb_ulog( kLogLevelError, "dnssd_getaddrinfo error %#m\n", inGAIError );
30356 							dnssd_getaddrinfo_forget( &instance->newGAI );
30357 						}
30358 						break;
30359 
30360 					default:
30361 						break;
30362 				}
30363 			} );
30364 			instance->newGAI = gai;
30365 			dnssd_retain( instance->newGAI );
30366 			dnssd_getaddrinfo_activate( instance->newGAI );
30367 		}
30368 		else
30369 	#endif
30370 		{
30371 			DNSServiceForget( &instance->gai );
30372 
30373 			sdRef = me->connection;
30374 			instance->gaiStartTicks = UpTicks();
30375 			err = DNSServiceGetAddrInfo( &sdRef, kDNSServiceFlagsShareConnection, instance->ifIndex,
30376 				kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6, instance->hostname, _ServiceBrowserGAICallback,
30377 				instance );
30378 			require_noerr( err, exit );
30379 
30380 			instance->gai = sdRef;
30381 		}
30382 	}
30383 
30384 exit:
30385 	return;
30386 }
30387 
30388 #if( MDNSRESPONDER_PROJECT )
30389 //===========================================================================================================================
30390 //	_ServiceBrowserGAIResultHandler
30391 //===========================================================================================================================
30392 
30393 static void
_ServiceBrowserGAIResultHandler(ServiceBrowserRef me,SBServiceInstance * inInstance,dnssd_getaddrinfo_result_t * inResultArray,size_t inResultCount)30394 	_ServiceBrowserGAIResultHandler(
30395 		ServiceBrowserRef				me,
30396 		SBServiceInstance *				inInstance,
30397 		dnssd_getaddrinfo_result_t *	inResultArray,
30398 		size_t							inResultCount )
30399 {
30400 	OSStatus			err;
30401 	size_t				i;
30402 	const uint64_t		nowTicks = UpTicks();
30403 
30404 	for( i = 0; i < inResultCount; ++i )
30405 	{
30406 		const dnssd_getaddrinfo_result_t			result	= inResultArray[ i ];
30407 		const dnssd_getaddrinfo_result_type_t		type	= dnssd_getaddrinfo_result_get_type( result );
30408 
30409 		sb_ulog( kLogLevelTrace, "dnssd_getaddrinfo result: %@\n", result );
30410 
30411 		if( type == dnssd_getaddrinfo_result_type_add )
30412 		{
30413 			err = _ServiceBrowserAddIPAddress( me, inInstance, dnssd_getaddrinfo_result_get_address( result ),
30414 				UpTicksToMicroseconds( nowTicks - inInstance->gaiStartTicks ) );
30415 			if( err == kDuplicateErr ) err = kNoErr;
30416 			require_noerr( err, exit );
30417 		}
30418 		else if( type == dnssd_getaddrinfo_result_type_remove )
30419 		{
30420 			err = _ServiceBrowserRemoveIPAddress( me, inInstance, dnssd_getaddrinfo_result_get_address( result ) );
30421 			if( err == kNotFoundErr ) err = kNoErr;
30422 			require_noerr( err, exit );
30423 		}
30424 	}
30425 
30426 exit:
30427 	return;
30428 }
30429 #endif
30430 
30431 //===========================================================================================================================
30432 //	_ServiceBrowserGAICallback
30433 //===========================================================================================================================
30434 
30435 static void DNSSD_API
_ServiceBrowserGAICallback(DNSServiceRef inSDRef,DNSServiceFlags inFlags,uint32_t inInterfaceIndex,DNSServiceErrorType inError,const char * inHostname,const struct sockaddr * inSockAddr,uint32_t inTTL,void * inContext)30436 	_ServiceBrowserGAICallback(
30437 		DNSServiceRef			inSDRef,
30438 		DNSServiceFlags			inFlags,
30439 		uint32_t				inInterfaceIndex,
30440 		DNSServiceErrorType		inError,
30441 		const char *			inHostname,
30442 		const struct sockaddr *	inSockAddr,
30443 		uint32_t				inTTL,
30444 		void *					inContext )
30445 {
30446 	OSStatus						err;
30447 	const uint64_t					nowTicks	= UpTicks();
30448 	SBServiceInstance * const		instance	= (SBServiceInstance *) inContext;
30449 	ServiceBrowserRef const			me			= (ServiceBrowserRef) instance->browser;
30450 
30451 	Unused( inSDRef );
30452 	Unused( inTTL );
30453 
30454 	sb_ulog( kLogLevelTrace, "GetAddrInfo result: %s on interface %d for (%s ->) %s -> %##a%?{end} (error: %#m)",
30455 		DNSServiceFlagsToAddRmvStr( inFlags ), (int32_t) inInterfaceIndex, instance->fqdn, inHostname, inSockAddr,
30456 		!inError, inError );
30457 
30458 	require_noerr( inError, exit );
30459 
30460 	if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
30461 	{
30462 		dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
30463 		goto exit;
30464 	}
30465 
30466 	if( inFlags & kDNSServiceFlagsAdd )
30467 	{
30468 		err = _ServiceBrowserAddIPAddress( me, instance, inSockAddr,
30469 			UpTicksToMicroseconds( nowTicks - instance->gaiStartTicks ) );
30470 		if( err == kDuplicateErr ) err = kNoErr;
30471 		require_noerr( err, exit );
30472 	}
30473 	else
30474 	{
30475 		err = _ServiceBrowserRemoveIPAddress( me, instance, inSockAddr );
30476 		if( err == kNotFoundErr ) err = kNoErr;
30477 		require_noerr( err, exit );
30478 	}
30479 
30480 exit:
30481 	return;
30482 }
30483 
30484 //===========================================================================================================================
30485 //	_ServiceBrowserAddServiceType
30486 //===========================================================================================================================
30487 
30488 static OSStatus
_ServiceBrowserAddServiceType(ServiceBrowserRef me,SBDomain * inDomain,const char * inName,uint32_t inIfIndex)30489 	_ServiceBrowserAddServiceType(
30490 		ServiceBrowserRef	me,
30491 		SBDomain *			inDomain,
30492 		const char *		inName,
30493 		uint32_t			inIfIndex )
30494 {
30495 	OSStatus				err;
30496 	SBServiceType *			type;
30497 	SBServiceType **		typePtr;
30498 	SBServiceType *			newType		= NULL;
30499 	SBServiceBrowse *		browse;
30500 	SBServiceBrowse **		browsePtr;
30501 	SBServiceBrowse *		newBrowse	= NULL;
30502 	DNSServiceRef			sdRef;
30503 	DNSServiceFlags			flags;
30504 
30505 	for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
30506 	{
30507 		if( strcasecmp( type->name, inName ) == 0 ) break;
30508 	}
30509 	if( !type )
30510 	{
30511 		err = _SBServiceTypeCreate( inName, &newType );
30512 		require_noerr_quiet( err, exit );
30513 
30514 		type = newType;
30515 	}
30516 
30517 	for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
30518 	{
30519 		if( browse->ifIndex == inIfIndex ) break;
30520 	}
30521 	require_action_quiet( !browse, exit, err = kDuplicateErr );
30522 
30523 	err = _SBServiceBrowseCreate( inIfIndex, me, &newBrowse );
30524 	require_noerr_quiet( err, exit );
30525 
30526 	flags = kDNSServiceFlagsShareConnection;
30527 	if( ( newBrowse->ifIndex == kDNSServiceInterfaceIndexAny ) && me->includeAWDL ) flags |= kDNSServiceFlagsIncludeAWDL;
30528 
30529 	sb_ulog( kLogLevelTrace, "Starting Browse on interface %d for %s%s",
30530 		(int32_t) newBrowse->ifIndex, type->name, inDomain->name );
30531 
30532 	sdRef = me->connection;
30533 	newBrowse->startTicks = UpTicks();
30534 	err = DNSServiceBrowse( &sdRef, flags, newBrowse->ifIndex, type->name, inDomain->name, _ServiceBrowserBrowseCallback,
30535 		newBrowse );
30536 	require_noerr( err, exit );
30537 
30538 	newBrowse->browse = sdRef;
30539 	*browsePtr	= newBrowse;
30540 	newBrowse	= NULL;
30541 
30542 	if( newType )
30543 	{
30544 		*typePtr	= newType;
30545 		newType		= NULL;
30546 	}
30547 
30548 exit:
30549 	if( newBrowse )	_SBServiceBrowseFree( newBrowse );
30550 	if( newType )	_SBServiceTypeFree( newType );
30551 	return( err );
30552 }
30553 
30554 //===========================================================================================================================
30555 //	_ServiceBrowserRemoveServiceType
30556 //===========================================================================================================================
30557 
30558 static OSStatus
_ServiceBrowserRemoveServiceType(ServiceBrowserRef me,SBDomain * inDomain,const char * inName,uint32_t inIfIndex)30559 	_ServiceBrowserRemoveServiceType(
30560 		ServiceBrowserRef	me,
30561 		SBDomain *			inDomain,
30562 		const char *		inName,
30563 		uint32_t			inIfIndex )
30564 {
30565 	OSStatus				err;
30566 	SBServiceType *			type;
30567 	SBServiceType **		typePtr;
30568 	SBServiceBrowse *		browse;
30569 	SBServiceBrowse **		browsePtr;
30570 
30571 	Unused( me );
30572 
30573 	for( typePtr = &inDomain->typeList; ( type = *typePtr ) != NULL; typePtr = &type->next )
30574 	{
30575 		if( strcasecmp( type->name, inName ) == 0 ) break;
30576 	}
30577 	require_action_quiet( type, exit, err = kNotFoundErr );
30578 
30579 	for( browsePtr = &type->browseList; ( browse = *browsePtr ) != NULL; browsePtr = &browse->next )
30580 	{
30581 		if( browse->ifIndex == inIfIndex ) break;
30582 	}
30583 	require_action_quiet( browse, exit, err = kNotFoundErr );
30584 
30585 	*browsePtr = browse->next;
30586 	_SBServiceBrowseFree( browse );
30587 	if( !type->browseList )
30588 	{
30589 		*typePtr = type->next;
30590 		_SBServiceTypeFree( type );
30591 	}
30592 	err = kNoErr;
30593 
30594 exit:
30595 	return( err );
30596 }
30597 
30598 //===========================================================================================================================
30599 //	_ServiceBrowserAddServiceInstance
30600 //===========================================================================================================================
30601 
30602 static OSStatus
_ServiceBrowserAddServiceInstance(ServiceBrowserRef me,SBServiceBrowse * inBrowse,uint32_t inIfIndex,const char * inName,const char * inRegType,const char * inDomain,uint64_t inDiscoverTimeUs)30603 	_ServiceBrowserAddServiceInstance(
30604 		ServiceBrowserRef	me,
30605 		SBServiceBrowse *	inBrowse,
30606 		uint32_t			inIfIndex,
30607 		const char *		inName,
30608 		const char *		inRegType,
30609 		const char *		inDomain,
30610 		uint64_t			inDiscoverTimeUs )
30611 {
30612 	OSStatus					err;
30613 	DNSServiceRef				sdRef;
30614 	SBServiceInstance *			instance;
30615 	SBServiceInstance **		instancePtr;
30616 	SBServiceInstance *			newInstance	= NULL;
30617 
30618 	for( instancePtr = &inBrowse->instanceList; ( instance = *instancePtr ) != NULL; instancePtr = &instance->next )
30619 	{
30620 		if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
30621 	}
30622 	require_action_quiet( !instance, exit, err = kDuplicateErr );
30623 
30624 	err = _SBServiceInstanceCreate( inName, inRegType, inDomain, inIfIndex, inDiscoverTimeUs, me, &newInstance );
30625 	require_noerr_quiet( err, exit );
30626 
30627 	sb_ulog( kLogLevelTrace, "Starting Resolve on interface %d for %s.%s%s",
30628 		(int32_t) newInstance->ifIndex, inName, inRegType, inDomain );
30629 
30630 	sdRef = me->connection;
30631 	newInstance->resolveStartTicks = UpTicks();
30632 	err = DNSServiceResolve( &sdRef, kDNSServiceFlagsShareConnection, newInstance->ifIndex, inName, inRegType, inDomain,
30633 		_ServiceBrowserResolveCallback, newInstance );
30634 	require_noerr( err, exit );
30635 
30636 	newInstance->resolve = sdRef;
30637 	*instancePtr	= newInstance;
30638 	newInstance		= NULL;
30639 
30640 exit:
30641 	if( newInstance ) _SBServiceInstanceRelease( newInstance );
30642 	return( err );
30643 }
30644 
30645 //===========================================================================================================================
30646 //	_ServiceBrowserRemoveServiceInstance
30647 //===========================================================================================================================
30648 
30649 static OSStatus
_ServiceBrowserRemoveServiceInstance(ServiceBrowserRef me,SBServiceBrowse * inBrowse,const char * inName,uint32_t inIfIndex)30650 	_ServiceBrowserRemoveServiceInstance(
30651 		ServiceBrowserRef	me,
30652 		SBServiceBrowse *	inBrowse,
30653 		const char *		inName,
30654 		uint32_t			inIfIndex )
30655 {
30656 	OSStatus					err;
30657 	SBServiceInstance *			instance;
30658 	SBServiceInstance **		ptr;
30659 
30660 	Unused( me );
30661 
30662 	for( ptr = &inBrowse->instanceList; ( instance = *ptr ) != NULL; ptr = &instance->next )
30663 	{
30664 		if( ( instance->ifIndex == inIfIndex ) && ( strcasecmp( instance->name, inName ) == 0 ) ) break;
30665 	}
30666 	require_action_quiet( instance, exit, err = kNotFoundErr );
30667 
30668 	*ptr = instance->next;
30669 	_SBServiceInstanceForget( &instance );
30670 	err = kNoErr;
30671 
30672 exit:
30673 	return( err );
30674 }
30675 
30676 //===========================================================================================================================
30677 //	_ServiceBrowserAddIPAddress
30678 //===========================================================================================================================
30679 
30680 static OSStatus
_ServiceBrowserAddIPAddress(ServiceBrowserRef me,SBServiceInstance * inInstance,const struct sockaddr * inSockAddr,uint64_t inResolveTimeUs)30681 	_ServiceBrowserAddIPAddress(
30682 		ServiceBrowserRef		me,
30683 		SBServiceInstance *		inInstance,
30684 		const struct sockaddr *	inSockAddr,
30685 		uint64_t				inResolveTimeUs )
30686 {
30687 	OSStatus			err;
30688 	SBIPAddress *		ipaddr;
30689 	SBIPAddress **		ipaddrPtr;
30690 	SBIPAddress *		newIPAddr = NULL;
30691 
30692 	Unused( me );
30693 
30694 	if( ( inSockAddr->sa_family != AF_INET ) && ( inSockAddr->sa_family != AF_INET6 ) )
30695 	{
30696 		dlogassert( "Unexpected address family: %d", inSockAddr->sa_family );
30697 		err = kTypeErr;
30698 		goto exit;
30699 	}
30700 
30701 	for( ipaddrPtr = &inInstance->ipaddrList; ( ipaddr = *ipaddrPtr ) != NULL; ipaddrPtr = &ipaddr->next )
30702 	{
30703 		if( SockAddrCompareAddr( &ipaddr->sip, inSockAddr ) == 0 ) break;
30704 	}
30705 	require_action_quiet( !ipaddr, exit, err = kDuplicateErr );
30706 
30707 	err = _SBIPAddressCreate( inSockAddr, inResolveTimeUs, &newIPAddr );
30708 	require_noerr_quiet( err, exit );
30709 
30710 	*ipaddrPtr = newIPAddr;
30711 	newIPAddr = NULL;
30712 	err = kNoErr;
30713 
30714 exit:
30715 	if( newIPAddr ) _SBIPAddressFree( newIPAddr );
30716 	return( err );
30717 }
30718 
30719 //===========================================================================================================================
30720 //	_ServiceBrowserRemoveIPAddress
30721 //===========================================================================================================================
30722 
30723 static OSStatus
_ServiceBrowserRemoveIPAddress(ServiceBrowserRef me,SBServiceInstance * inInstance,const struct sockaddr * inSockAddr)30724 	_ServiceBrowserRemoveIPAddress(
30725 		ServiceBrowserRef		me,
30726 		SBServiceInstance *		inInstance,
30727 		const struct sockaddr *	inSockAddr )
30728 {
30729 	OSStatus			err;
30730 	SBIPAddress *		ipaddr;
30731 	SBIPAddress **		ipaddrPtr;
30732 
30733 	Unused( me );
30734 
30735 	for( ipaddrPtr = &inInstance->ipaddrList; ( ipaddr = *ipaddrPtr ) != NULL; ipaddrPtr = &ipaddr->next )
30736 	{
30737 		if( SockAddrCompareAddr( &ipaddr->sip.sa, inSockAddr ) == 0 ) break;
30738 	}
30739 	require_action_quiet( ipaddr, exit, err = kNotFoundErr );
30740 
30741 	*ipaddrPtr = ipaddr->next;
30742 	_SBIPAddressFree( ipaddr );
30743 	err = kNoErr;
30744 
30745 exit:
30746 	return( err );
30747 }
30748 
30749 //===========================================================================================================================
30750 //	_ServiceBrowserCreateResults
30751 //===========================================================================================================================
30752 
_ServiceBrowserCreateResults(ServiceBrowserRef me,ServiceBrowserResults ** outResults)30753 static OSStatus	_ServiceBrowserCreateResults( ServiceBrowserRef me, ServiceBrowserResults **outResults )
30754 {
30755 	OSStatus							err;
30756 	SBDomain *							d;
30757 	SBServiceType *						t;
30758 	SBServiceBrowse *					b;
30759 	SBServiceInstance *					i;
30760 	SBIPAddress *						a;
30761 	ServiceBrowserResultsPrivate *		results;
30762 	SBRDomain **						domainPtr;
30763 
30764 	results = (ServiceBrowserResultsPrivate *) calloc( 1, sizeof( *results ) );
30765 	require_action( results, exit, err = kNoMemoryErr );
30766 
30767 	results->refCount = 1;
30768 
30769 	domainPtr = &results->domainList;
30770 	for( d = me->domainList; d; d = d->next )
30771 	{
30772 		SBRDomain *				domain;
30773 		SBRServiceType **		typePtr;
30774 
30775 		err = _SBRDomainCreate( d->name, &domain );
30776 		require_noerr_quiet( err, exit );
30777 		*domainPtr = domain;
30778 		 domainPtr = &domain->next;
30779 
30780 		typePtr = &domain->typeList;
30781 		for( t = d->typeList; t; t = t->next )
30782 		{
30783 			SBRServiceType *			type;
30784 			SBRServiceInstance **		instancePtr;
30785 
30786 			err = _SBRServiceTypeCreate( t->name, &type );
30787 			require_noerr_quiet( err, exit );
30788 			*typePtr = type;
30789 			 typePtr = &type->next;
30790 
30791 			instancePtr = &type->instanceList;
30792 			for( b = t->browseList; b; b = b->next )
30793 			{
30794 				for( i = b->instanceList; i; i = i->next )
30795 				{
30796 					SBRServiceInstance *		instance;
30797 					SBRIPAddress **				ipaddrPtr;
30798 
30799 					err = _SBRServiceInstanceCreate( i->name, i->ifIndex, i->hostname, i->port, i->txtPtr, i->txtLen,
30800 						i->discoverTimeUs, i->resolveTimeUs, &instance );
30801 					require_noerr_quiet( err, exit );
30802 					*instancePtr = instance;
30803 					 instancePtr = &instance->next;
30804 
30805 					ipaddrPtr = &instance->ipaddrList;
30806 					for( a = i->ipaddrList; a; a = a->next )
30807 					{
30808 						SBRIPAddress *		ipaddr;
30809 
30810 						err = _SBRIPAddressCreate( &a->sip.sa, a->resolveTimeUs, &ipaddr );
30811 						require_noerr_quiet( err, exit );
30812 
30813 						*ipaddrPtr = ipaddr;
30814 						 ipaddrPtr = &ipaddr->next;
30815 					}
30816 				}
30817 			}
30818 		}
30819 	}
30820 
30821 	*outResults = (ServiceBrowserResults *) results;
30822 	results = NULL;
30823 	err = kNoErr;
30824 
30825 exit:
30826 	if( results ) ServiceBrowserResultsRelease( (ServiceBrowserResults *) results );
30827 	return( err );
30828 }
30829 
30830 //===========================================================================================================================
30831 //	_SBDomainCreate
30832 //===========================================================================================================================
30833 
_SBDomainCreate(const char * inName,ServiceBrowserRef inBrowser,SBDomain ** outDomain)30834 static OSStatus	_SBDomainCreate( const char *inName, ServiceBrowserRef inBrowser, SBDomain **outDomain )
30835 {
30836 	OSStatus		err;
30837 	SBDomain *		obj;
30838 
30839 	obj = (SBDomain *) calloc( 1, sizeof( *obj ) );
30840 	require_action( obj, exit, err = kNoMemoryErr );
30841 
30842 	obj->name = strdup( inName );
30843 	require_action( obj->name, exit, err = kNoMemoryErr );
30844 
30845 	obj->browser = inBrowser;
30846 
30847 	*outDomain = obj;
30848 	obj = NULL;
30849 	err = kNoErr;
30850 
30851 exit:
30852 	if( obj ) _SBDomainFree( obj );
30853 	return( err );
30854 }
30855 
30856 //===========================================================================================================================
30857 //	_SBDomainFree
30858 //===========================================================================================================================
30859 
_SBDomainFree(SBDomain * inDomain)30860 static void	_SBDomainFree( SBDomain *inDomain )
30861 {
30862 	SBServiceType *		type;
30863 
30864 	ForgetMem( &inDomain->name );
30865 	DNSServiceForget( &inDomain->servicesQuery );
30866 	while( ( type = inDomain->typeList ) != NULL )
30867 	{
30868 		inDomain->typeList = type->next;
30869 		_SBServiceTypeFree( type );
30870 	}
30871 	free( inDomain );
30872 }
30873 
30874 //===========================================================================================================================
30875 //	_SBServiceTypeCreate
30876 //===========================================================================================================================
30877 
_SBServiceTypeCreate(const char * inName,SBServiceType ** outType)30878 static OSStatus	_SBServiceTypeCreate( const char *inName, SBServiceType **outType )
30879 {
30880 	OSStatus			err;
30881 	SBServiceType *		obj;
30882 
30883 	obj = (SBServiceType *) calloc( 1, sizeof( *obj ) );
30884 	require_action( obj, exit, err = kNoMemoryErr );
30885 
30886 	obj->name = strdup( inName );
30887 	require_action( obj->name, exit, err = kNoMemoryErr );
30888 
30889 	*outType = obj;
30890 	obj = NULL;
30891 	err = kNoErr;
30892 
30893 exit:
30894 	if( obj ) _SBServiceTypeFree( obj );
30895 	return( err );
30896 }
30897 
30898 //===========================================================================================================================
30899 //	_SBServiceTypeFree
30900 //===========================================================================================================================
30901 
_SBServiceTypeFree(SBServiceType * inType)30902 static void	_SBServiceTypeFree( SBServiceType *inType )
30903 {
30904 	SBServiceBrowse *		browse;
30905 
30906 	ForgetMem( &inType->name );
30907 	while( ( browse = inType->browseList ) != NULL )
30908 	{
30909 		inType->browseList = browse->next;
30910 		_SBServiceBrowseFree( browse );
30911 	}
30912 	free( inType );
30913 }
30914 
30915 //===========================================================================================================================
30916 //	_SBServiceBrowseCreate
30917 //===========================================================================================================================
30918 
_SBServiceBrowseCreate(uint32_t inIfIndex,ServiceBrowserRef inBrowser,SBServiceBrowse ** outBrowse)30919 static OSStatus	_SBServiceBrowseCreate( uint32_t inIfIndex, ServiceBrowserRef inBrowser, SBServiceBrowse **outBrowse )
30920 {
30921 	OSStatus				err;
30922 	SBServiceBrowse *		obj;
30923 
30924 	obj = (SBServiceBrowse *) calloc( 1, sizeof( *obj ) );
30925 	require_action( obj, exit, err = kNoMemoryErr );
30926 
30927 	obj->ifIndex = inIfIndex;
30928 	obj->browser = inBrowser;
30929 	*outBrowse = obj;
30930 	err = kNoErr;
30931 
30932 exit:
30933 	return( err );
30934 }
30935 
30936 //===========================================================================================================================
30937 //	_SBServiceBrowseFree
30938 //===========================================================================================================================
30939 
_SBServiceBrowseFree(SBServiceBrowse * inBrowse)30940 static void	_SBServiceBrowseFree( SBServiceBrowse *inBrowse )
30941 {
30942 	SBServiceInstance *		instance;
30943 
30944 	DNSServiceForget( &inBrowse->browse );
30945 	while( ( instance = inBrowse->instanceList ) != NULL )
30946 	{
30947 		inBrowse->instanceList = instance->next;
30948 		_SBServiceInstanceForget( &instance );
30949 	}
30950 	free( inBrowse );
30951 }
30952 
30953 //===========================================================================================================================
30954 //	_SBServiceInstanceCreate
30955 //===========================================================================================================================
30956 
30957 static OSStatus
_SBServiceInstanceCreate(const char * inName,const char * inType,const char * inDomain,uint32_t inIfIndex,uint64_t inDiscoverTimeUs,ServiceBrowserRef inBrowser,SBServiceInstance ** outInstance)30958 	_SBServiceInstanceCreate(
30959 		const char *			inName,
30960 		const char *			inType,
30961 		const char *			inDomain,
30962 		uint32_t				inIfIndex,
30963 		uint64_t				inDiscoverTimeUs,
30964 		ServiceBrowserRef		inBrowser,
30965 		SBServiceInstance **	outInstance )
30966 {
30967 	OSStatus				err;
30968 	SBServiceInstance *		obj;
30969 
30970 	obj = (SBServiceInstance *) calloc( 1, sizeof( *obj ) );
30971 	require_action( obj, exit, err = kNoMemoryErr );
30972 
30973 	obj->refCount = 1;
30974 
30975 	obj->name = strdup( inName );
30976 	require_action( obj->name, exit, err = kNoMemoryErr );
30977 
30978 	ASPrintF( &obj->fqdn, "%s.%s%s", obj->name, inType, inDomain );
30979 	require_action( obj->fqdn, exit, err = kNoMemoryErr );
30980 
30981 	obj->ifIndex		= inIfIndex;
30982 	obj->discoverTimeUs	= inDiscoverTimeUs;
30983 	obj->browser		= inBrowser;
30984 
30985 	*outInstance = obj;
30986 	obj = NULL;
30987 	err = kNoErr;
30988 
30989 exit:
30990 	if( obj ) _SBServiceInstanceRelease( obj );
30991 	return( err );
30992 }
30993 
30994 #if( MDNSRESPONDER_PROJECT )
30995 //===========================================================================================================================
30996 //	_SBServiceInstanceRetain
30997 //===========================================================================================================================
30998 
_SBServiceInstanceRetain(SBServiceInstance * inInstance)30999 static void	_SBServiceInstanceRetain( SBServiceInstance *inInstance )
31000 {
31001 	atomic_add_32( &inInstance->refCount, 1 );
31002 }
31003 #endif
31004 
31005 //===========================================================================================================================
31006 //	_SBServiceInstanceStop
31007 //===========================================================================================================================
31008 
_SBServiceInstanceStop(SBServiceInstance * inInstance)31009 static void	_SBServiceInstanceStop( SBServiceInstance *inInstance )
31010 {
31011 	DNSServiceForget( &inInstance->resolve );
31012 	DNSServiceForget( &inInstance->gai );
31013 #if( MDNSRESPONDER_PROJECT )
31014 	dnssd_getaddrinfo_forget( &inInstance->newGAI );
31015 #endif
31016 }
31017 
31018 //===========================================================================================================================
31019 //	_SBServiceInstanceRelease
31020 //===========================================================================================================================
31021 
_SBServiceInstanceRelease(SBServiceInstance * inInstance)31022 static void	_SBServiceInstanceRelease( SBServiceInstance *inInstance )
31023 {
31024 	if( atomic_add_and_fetch_32( &inInstance->refCount, -1 ) == 0 )
31025 	{
31026 		check( !inInstance->resolve );
31027 		check( !inInstance->gai );
31028 	#if( MDNSRESPONDER_PROJECT )
31029 		check( !inInstance->newGAI );
31030 	#endif
31031 		ForgetMem( &inInstance->name );
31032 		ForgetMem( &inInstance->fqdn );
31033 		ForgetMem( &inInstance->hostname );
31034 		ForgetMem( &inInstance->txtPtr );
31035 		ForgetSBIPAddressList( &inInstance->ipaddrList );
31036 		free( inInstance );
31037 	}
31038 }
31039 
31040 //===========================================================================================================================
31041 //	_SBIPAddressCreate
31042 //===========================================================================================================================
31043 
_SBIPAddressCreate(const struct sockaddr * inSockAddr,uint64_t inResolveTimeUs,SBIPAddress ** outIPAddress)31044 static OSStatus	_SBIPAddressCreate( const struct sockaddr *inSockAddr, uint64_t inResolveTimeUs, SBIPAddress **outIPAddress )
31045 {
31046 	OSStatus			err;
31047 	SBIPAddress *		obj;
31048 
31049 	obj = (SBIPAddress *) calloc( 1, sizeof( *obj ) );
31050 	require_action( obj, exit, err = kNoMemoryErr );
31051 
31052 	SockAddrCopy( inSockAddr, &obj->sip );
31053 	obj->resolveTimeUs = inResolveTimeUs;
31054 
31055 	*outIPAddress = obj;
31056 	err = kNoErr;
31057 
31058 exit:
31059 	return( err );
31060 }
31061 
31062 //===========================================================================================================================
31063 //	_SBIPAddressFree
31064 //===========================================================================================================================
31065 
_SBIPAddressFree(SBIPAddress * inIPAddress)31066 static void _SBIPAddressFree( SBIPAddress *inIPAddress )
31067 {
31068 	free( inIPAddress );
31069 }
31070 
31071 //===========================================================================================================================
31072 //	_SBIPAddressFreeList
31073 //===========================================================================================================================
31074 
_SBIPAddressFreeList(SBIPAddress * inList)31075 static void	_SBIPAddressFreeList( SBIPAddress *inList )
31076 {
31077 	SBIPAddress *		ipaddr;
31078 
31079 	while( ( ipaddr = inList ) != NULL )
31080 	{
31081 		inList = ipaddr->next;
31082 		_SBIPAddressFree( ipaddr );
31083 	}
31084 }
31085 
31086 //===========================================================================================================================
31087 //	_SBRDomainCreate
31088 //===========================================================================================================================
31089 
_SBRDomainCreate(const char * inName,SBRDomain ** outDomain)31090 static OSStatus	_SBRDomainCreate( const char *inName, SBRDomain **outDomain )
31091 {
31092 	OSStatus		err;
31093 	SBRDomain *		obj;
31094 
31095 	obj = (SBRDomain *) calloc( 1, sizeof( *obj ) );
31096 	require_action( obj, exit, err = kNoMemoryErr );
31097 
31098 	obj->name = strdup( inName );
31099 	require_action( obj->name, exit, err = kNoMemoryErr );
31100 
31101 	*outDomain = obj;
31102 	obj = NULL;
31103 	err = kNoErr;
31104 
31105 exit:
31106 	if( obj ) _SBRDomainFree( obj );
31107 	return( err );
31108 }
31109 
31110 //===========================================================================================================================
31111 //	_SBRDomainFree
31112 //===========================================================================================================================
31113 
_SBRDomainFree(SBRDomain * inDomain)31114 static void	_SBRDomainFree( SBRDomain *inDomain )
31115 {
31116 	SBRServiceType *		type;
31117 
31118 	ForgetMem( &inDomain->name );
31119 	while( ( type = inDomain->typeList ) != NULL )
31120 	{
31121 		inDomain->typeList = type->next;
31122 		_SBRServiceTypeFree( type );
31123 	}
31124 	free( inDomain );
31125 }
31126 
31127 //===========================================================================================================================
31128 //	_SBRServiceTypeCreate
31129 //===========================================================================================================================
31130 
_SBRServiceTypeCreate(const char * inName,SBRServiceType ** outType)31131 static OSStatus	_SBRServiceTypeCreate( const char *inName, SBRServiceType **outType )
31132 {
31133 	OSStatus				err;
31134 	SBRServiceType *		obj;
31135 
31136 	obj = (SBRServiceType *) calloc( 1, sizeof( *obj ) );
31137 	require_action( obj, exit, err = kNoMemoryErr );
31138 
31139 	obj->name = strdup( inName );
31140 	require_action( obj->name, exit, err = kNoMemoryErr );
31141 
31142 	*outType = obj;
31143 	obj = NULL;
31144 	err = kNoErr;
31145 
31146 exit:
31147 	if( obj ) _SBRServiceTypeFree( obj );
31148 	return( err );
31149 }
31150 
31151 //===========================================================================================================================
31152 //	_SBRServiceTypeFree
31153 //===========================================================================================================================
31154 
_SBRServiceTypeFree(SBRServiceType * inType)31155 static void	_SBRServiceTypeFree( SBRServiceType *inType )
31156 {
31157 	SBRServiceInstance *		instance;
31158 
31159 	ForgetMem( &inType->name );
31160 	while( ( instance = inType->instanceList ) != NULL )
31161 	{
31162 		inType->instanceList = instance->next;
31163 		_SBRServiceInstanceFree( instance );
31164 	}
31165 	free( inType );
31166 }
31167 
31168 //===========================================================================================================================
31169 //	_SBRServiceInstanceCreate
31170 //===========================================================================================================================
31171 
31172 static OSStatus
_SBRServiceInstanceCreate(const char * inName,uint32_t inInterfaceIndex,const char * inHostname,uint16_t inPort,const uint8_t * inTXTPtr,size_t inTXTLen,uint64_t inDiscoverTimeUs,uint64_t inResolveTimeUs,SBRServiceInstance ** outInstance)31173 	_SBRServiceInstanceCreate(
31174 		const char *			inName,
31175 		uint32_t				inInterfaceIndex,
31176 		const char *			inHostname,
31177 		uint16_t				inPort,
31178 		const uint8_t *			inTXTPtr,
31179 		size_t					inTXTLen,
31180 		uint64_t				inDiscoverTimeUs,
31181 		uint64_t				inResolveTimeUs,
31182 		SBRServiceInstance **	outInstance )
31183 {
31184 	OSStatus					err;
31185 	SBRServiceInstance *		obj;
31186 
31187 	obj = (SBRServiceInstance *) calloc( 1, sizeof( *obj ) );
31188 	require_action( obj, exit, err = kNoMemoryErr );
31189 
31190 	obj->name = strdup( inName );
31191 	require_action( obj->name, exit, err = kNoMemoryErr );
31192 
31193 	if( inHostname )
31194 	{
31195 		obj->hostname = strdup( inHostname );
31196 		require_action( obj->hostname, exit, err = kNoMemoryErr );
31197 	}
31198 	if( inTXTLen > 0 )
31199 	{
31200 		obj->txtPtr = (uint8_t *) _memdup( inTXTPtr, inTXTLen );
31201 		require_action( obj->txtPtr, exit, err = kNoMemoryErr );
31202 		obj->txtLen = inTXTLen;
31203 	}
31204 	obj->discoverTimeUs	= inDiscoverTimeUs;
31205 	obj->resolveTimeUs	= inResolveTimeUs;
31206 	obj->ifIndex		= inInterfaceIndex;
31207 	obj->port			= inPort;
31208 
31209 	*outInstance = obj;
31210 	obj = NULL;
31211 	err = kNoErr;
31212 
31213 exit:
31214 	if( obj ) _SBRServiceInstanceFree( obj );
31215 	return( err );
31216 }
31217 
31218 //===========================================================================================================================
31219 //	_SBRServiceInstanceFree
31220 //===========================================================================================================================
31221 
_SBRServiceInstanceFree(SBRServiceInstance * inInstance)31222 static void	_SBRServiceInstanceFree( SBRServiceInstance *inInstance )
31223 {
31224 	SBRIPAddress *		ipaddr;
31225 
31226 	ForgetMem( &inInstance->name );
31227 	ForgetMem( &inInstance->hostname );
31228 	ForgetMem( &inInstance->txtPtr );
31229 	while( ( ipaddr = inInstance->ipaddrList ) != NULL )
31230 	{
31231 		inInstance->ipaddrList = ipaddr->next;
31232 		_SBRIPAddressFree( ipaddr );
31233 	}
31234 	free( inInstance );
31235 }
31236 
31237 //===========================================================================================================================
31238 //	_SBRIPAddressCreate
31239 //===========================================================================================================================
31240 
31241 static OSStatus
_SBRIPAddressCreate(const struct sockaddr * inSockAddr,uint64_t inResolveTimeUs,SBRIPAddress ** outIPAddress)31242 	_SBRIPAddressCreate(
31243 		const struct sockaddr *	inSockAddr,
31244 		uint64_t				inResolveTimeUs,
31245 		SBRIPAddress **			outIPAddress )
31246 {
31247 	OSStatus			err;
31248 	SBRIPAddress *		obj;
31249 
31250 	obj = (SBRIPAddress *) calloc( 1, sizeof( *obj ) );
31251 	require_action( obj, exit, err = kNoMemoryErr );
31252 
31253 	SockAddrCopy( inSockAddr, &obj->sip );
31254 	obj->resolveTimeUs = inResolveTimeUs;
31255 
31256 	*outIPAddress = obj;
31257 	err = kNoErr;
31258 
31259 exit:
31260 	return( err );
31261 }
31262 
31263 //===========================================================================================================================
31264 //	_SBRIPAddressFree
31265 //===========================================================================================================================
31266 
_SBRIPAddressFree(SBRIPAddress * inIPAddress)31267 static void	_SBRIPAddressFree( SBRIPAddress *inIPAddress )
31268 {
31269 	free( inIPAddress );
31270 }
31271 
31272 //===========================================================================================================================
31273 //	_SocketWriteAll
31274 //
31275 //	Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
31276 //===========================================================================================================================
31277 
_SocketWriteAll(SocketRef inSock,const void * inData,size_t inSize,int32_t inTimeoutSecs)31278 static OSStatus	_SocketWriteAll( SocketRef inSock, const void *inData, size_t inSize, int32_t inTimeoutSecs )
31279 {
31280 	OSStatus			err;
31281 	const uint8_t *		src;
31282 	const uint8_t *		end;
31283 	fd_set				writeSet;
31284 	struct timeval		timeout;
31285 	ssize_t				n;
31286 
31287 	FD_ZERO( &writeSet );
31288 	src = (const uint8_t *) inData;
31289 	end = src + inSize;
31290 	while( src < end )
31291 	{
31292 		FD_SET( inSock, &writeSet );
31293 		timeout.tv_sec 	= inTimeoutSecs;
31294 		timeout.tv_usec = 0;
31295 		n = select( (int)( inSock + 1 ), NULL, &writeSet, NULL, &timeout );
31296 		if( n == 0 ) { err = kTimeoutErr; goto exit; }
31297 		err = map_socket_value_errno( inSock, n > 0, n );
31298 		require_noerr( err, exit );
31299 
31300 		n = send( inSock, (char *) src, (size_t)( end - src ), 0 );
31301 		err = map_socket_value_errno( inSock, n >= 0, n );
31302 		if( err == EINTR ) continue;
31303 		require_noerr( err, exit );
31304 
31305 		src += n;
31306 	}
31307 	err = kNoErr;
31308 
31309 exit:
31310 	return( err );
31311 }
31312 
31313 //===========================================================================================================================
31314 //	_ParseIPv4Address
31315 //
31316 //	Warning: "inBuffer" may be modified even in error cases.
31317 //
31318 //	Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
31319 //===========================================================================================================================
31320 
_ParseIPv4Address(const char * inStr,uint8_t inBuffer[4],const char ** outStr)31321 static OSStatus	_ParseIPv4Address( const char *inStr, uint8_t inBuffer[ 4 ], const char **outStr )
31322 {
31323 	OSStatus		err;
31324 	uint8_t *		dst;
31325 	int				segments;
31326 	int				sawDigit;
31327 	int				c;
31328 	int				v;
31329 
31330 	check( inBuffer );
31331 	check( outStr );
31332 
31333 	dst		 = inBuffer;
31334 	*dst	 = 0;
31335 	sawDigit = 0;
31336 	segments = 0;
31337 	for( ; ( c = *inStr ) != '\0'; ++inStr )
31338 	{
31339 		if( isdigit_safe( c ) )
31340 		{
31341 			v = ( *dst * 10 ) + ( c - '0' );
31342 			require_action_quiet( v <= 255, exit, err = kRangeErr );
31343 			*dst = (uint8_t) v;
31344 			if( !sawDigit )
31345 			{
31346 				++segments;
31347 				require_action_quiet( segments <= 4, exit, err = kOverrunErr );
31348 				sawDigit = 1;
31349 			}
31350 		}
31351 		else if( ( c == '.' ) && sawDigit )
31352 		{
31353 			require_action_quiet( segments < 4, exit, err = kMalformedErr );
31354 			*++dst = 0;
31355 			sawDigit = 0;
31356 		}
31357 		else
31358 		{
31359 			break;
31360 		}
31361 	}
31362 	require_action_quiet( segments == 4, exit, err = kUnderrunErr );
31363 
31364 	*outStr = inStr;
31365 	err = kNoErr;
31366 
31367 exit:
31368 	return( err );
31369 }
31370 
31371 //===========================================================================================================================
31372 //	_StringToIPv4Address
31373 //
31374 //	Note: This was copied from CoreUtils because the StringToIPv4Address function is currently not exported in the framework.
31375 //===========================================================================================================================
31376 
31377 static OSStatus
_StringToIPv4Address(const char * inStr,StringToIPAddressFlags inFlags,uint32_t * outIP,int * outPort,uint32_t * outSubnet,uint32_t * outRouter,const char ** outStr)31378 	_StringToIPv4Address(
31379 		const char *			inStr,
31380 		StringToIPAddressFlags	inFlags,
31381 		uint32_t *				outIP,
31382 		int *					outPort,
31383 		uint32_t *				outSubnet,
31384 		uint32_t *				outRouter,
31385 		const char **			outStr )
31386 {
31387 	OSStatus			err;
31388 	uint8_t				buf[ 4 ];
31389 	int					c;
31390 	uint32_t			ip;
31391 	int					hasPort;
31392 	int					port;
31393 	int					hasPrefix;
31394 	int					prefix;
31395 	uint32_t			subnetMask;
31396 	uint32_t			router;
31397 
31398 	require_action( inStr, exit, err = kParamErr );
31399 
31400 	// Parse the address-only part of the address (e.g. "1.2.3.4").
31401 
31402 	err = _ParseIPv4Address( inStr, buf, &inStr );
31403 	require_noerr_quiet( err, exit );
31404 	ip = (uint32_t)( ( buf[ 0 ] << 24 ) | ( buf[ 1 ] << 16 ) | ( buf[ 2 ] << 8 ) | buf[ 3 ] );
31405 	c = *inStr;
31406 
31407 	// Parse the port (if any).
31408 
31409 	hasPort = 0;
31410 	port    = 0;
31411 	if( c == ':' )
31412 	{
31413 		require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
31414 		while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
31415 		require_action_quiet( port <= 65535, exit, err = kRangeErr );
31416 		hasPort = 1;
31417 	}
31418 
31419 	// Parse the prefix length (if any).
31420 
31421 	hasPrefix  = 0;
31422 	prefix     = 0;
31423 	subnetMask = 0;
31424 	router     = 0;
31425 	if( c == '/' )
31426 	{
31427 		require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
31428 		while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
31429 		require_action_quiet( ( prefix >= 0 ) && ( prefix <= 32 ), exit, err = kRangeErr );
31430 		hasPrefix = 1;
31431 
31432 		subnetMask = ( prefix > 0 ) ? ( UINT32_C( 0xFFFFFFFF ) << ( 32 - prefix ) ) : 0;
31433 		router	   = ( ip & subnetMask ) | 1;
31434 	}
31435 
31436 	// Return the results. Only fill in port/prefix/router results if the info was found to allow for defaults.
31437 
31438 	if( outIP )					 *outIP		= ip;
31439 	if( outPort   && hasPort )	 *outPort	= port;
31440 	if( outSubnet && hasPrefix ) *outSubnet	= subnetMask;
31441 	if( outRouter && hasPrefix ) *outRouter	= router;
31442 	if( outStr )				 *outStr	= inStr;
31443 	err = kNoErr;
31444 
31445 exit:
31446 	return( err );
31447 }
31448 
31449 //===========================================================================================================================
31450 //	_ParseIPv6Address
31451 //
31452 //	Note: Parsed according to the rules specified in RFC 3513.
31453 //	Warning: "inBuffer" may be modified even in error cases.
31454 //
31455 //	Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
31456 //===========================================================================================================================
31457 
_ParseIPv6Address(const char * inStr,int inAllowV4Mapped,uint8_t inBuffer[16],const char ** outStr)31458 static OSStatus	_ParseIPv6Address( const char *inStr, int inAllowV4Mapped, uint8_t inBuffer[ 16 ], const char **outStr )
31459 {
31460 													// Table to map uppercase hex characters - '0' to their numeric values.
31461 													// 0  1  2  3  4  5  6  7  8  9  :  ;  <  =  >  ?  @  A   B   C   D   E   F
31462 	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 };
31463 	OSStatus					err;
31464 	const char *				ptr;
31465 	uint8_t *					dst;
31466 	uint8_t *					lim;
31467 	uint8_t *					colonPtr;
31468 	int							c;
31469 	int							sawDigit;
31470 	unsigned int				v;
31471 	int							i;
31472 	int							n;
31473 
31474 	// Pre-zero the address to simplify handling of compressed addresses (e.g. "::1").
31475 
31476 	for( i = 0; i < 16; ++i ) inBuffer[ i ] = 0;
31477 
31478 	// Special case leading :: (e.g. "::1") to simplify processing later.
31479 
31480 	if( *inStr == ':' )
31481 	{
31482 		++inStr;
31483 		require_action_quiet( *inStr == ':', exit, err = kMalformedErr );
31484 	}
31485 
31486 	// Parse the address.
31487 
31488 	ptr		 = inStr;
31489 	dst		 = inBuffer;
31490 	lim		 = dst + 16;
31491 	colonPtr = NULL;
31492 	sawDigit = 0;
31493 	v		 = 0;
31494 	while( ( ( c = *inStr++ ) != '\0' ) && ( c != '%' ) && ( c != '/' ) && ( c != ']' ) )
31495 	{
31496 		if(   ( c >= 'a' ) && ( c <= 'f' ) ) c -= ( 'a' - 'A' );
31497 		if( ( ( c >= '0' ) && ( c <= '9' ) ) || ( ( c >= 'A' ) && ( c <= 'F' ) ) )
31498 		{
31499 			c -= '0';
31500 			check( c < (int) countof( kASCIItoHexTable ) );
31501 			v = ( v << 4 ) | kASCIItoHexTable[ c ];
31502 			require_action_quiet( v <= 0xFFFF, exit, err = kRangeErr );
31503 			sawDigit = 1;
31504 			continue;
31505 		}
31506 		if( c == ':' )
31507 		{
31508 			ptr = inStr;
31509 			if( !sawDigit )
31510 			{
31511 				require_action_quiet( !colonPtr, exit, err = kMalformedErr );
31512 				colonPtr = dst;
31513 				continue;
31514 			}
31515 			require_action_quiet( *inStr != '\0', exit, err = kUnderrunErr );
31516 			require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
31517 			*dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
31518 			*dst++ = (uint8_t)(   v        & 0xFF );
31519 			sawDigit = 0;
31520 			v = 0;
31521 			continue;
31522 		}
31523 
31524 		// Handle IPv4-mapped/compatible addresses (e.g. ::FFFF:1.2.3.4).
31525 
31526 		if( inAllowV4Mapped && ( c == '.' ) && ( ( dst + 4 ) <= lim ) )
31527 		{
31528 			err = _ParseIPv4Address( ptr, dst, &inStr );
31529 			require_noerr_quiet( err, exit );
31530 			dst += 4;
31531 			sawDigit = 0;
31532 			++inStr; // Increment because the code below expects the end to be at "inStr - 1".
31533 		}
31534 		break;
31535 	}
31536 	if( sawDigit )
31537 	{
31538 		require_action_quiet( ( dst + 2 ) <= lim, exit, err = kOverrunErr );
31539 		*dst++ = (uint8_t)( ( v >> 8 ) & 0xFF );
31540 		*dst++ = (uint8_t)(   v        & 0xFF );
31541 	}
31542 	check( dst <= lim );
31543 	if( colonPtr )
31544 	{
31545 		require_action_quiet( dst < lim, exit, err = kOverrunErr );
31546 		n = (int)( dst - colonPtr );
31547 		for( i = 1; i <= n; ++i )
31548 		{
31549 			lim[ -i ] = colonPtr[ n - i ];
31550 			colonPtr[ n - i ] = 0;
31551 		}
31552 		dst = lim;
31553 	}
31554 	require_action_quiet( dst == lim, exit, err = kUnderrunErr );
31555 
31556 	*outStr = inStr - 1;
31557 	err = kNoErr;
31558 
31559 exit:
31560 	return( err );
31561 }
31562 
31563 //===========================================================================================================================
31564 //	_ParseIPv6Scope
31565 //
31566 //	Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
31567 //===========================================================================================================================
31568 
_ParseIPv6Scope(const char * inStr,uint32_t * outScope,const char ** outStr)31569 static OSStatus	_ParseIPv6Scope( const char *inStr, uint32_t *outScope, const char **outStr )
31570 {
31571 #if( TARGET_OS_POSIX )
31572 	OSStatus			err;
31573 	char				scopeStr[ 64 ];
31574 	char *				dst;
31575 	char *				lim;
31576 	int					c;
31577 	uint32_t			scope;
31578 	const char *		ptr;
31579 
31580 	// Copy into a local NULL-terminated string since that is what if_nametoindex expects.
31581 
31582 	dst = scopeStr;
31583 	lim = dst + ( countof( scopeStr ) - 1 );
31584 	while( ( ( c = *inStr ) != '\0' ) && ( c != ':' ) && ( c != '/' ) && ( c != ']' ) && ( dst < lim ) )
31585 	{
31586 		*dst++ = *inStr++;
31587 	}
31588 	*dst = '\0';
31589 	check( dst <= lim );
31590 
31591 	// First try to map as a name and if that fails, treat it as a numeric scope.
31592 
31593 	scope = if_nametoindex( scopeStr );
31594 	if( scope == 0 )
31595 	{
31596 		for( ptr = scopeStr; ( ( c = *ptr ) >= '0' ) && ( c <= '9' ); ++ptr )
31597 		{
31598 			scope = ( scope * 10 ) + ( ( (uint8_t) c ) - '0' );
31599 		}
31600 		require_action_quiet( c == '\0', exit, err = kMalformedErr );
31601 		require_action_quiet( ( ptr != scopeStr ) && ( ( (int)( ptr - scopeStr ) ) <= 10 ), exit, err = kMalformedErr );
31602 	}
31603 
31604 	*outScope = scope;
31605 	*outStr   = inStr;
31606 	err = kNoErr;
31607 
31608 exit:
31609 	return( err );
31610 #else
31611 	OSStatus			err;
31612 	uint32_t			scope;
31613 	const char *		start;
31614 	int					c;
31615 
31616 	scope = 0;
31617 	for( start = inStr; ( ( c = *inStr ) >= '0' ) && ( c <= '9' ); ++inStr )
31618 	{
31619 		scope = ( scope * 10 ) + ( c - '0' );
31620 	}
31621 	require_action_quiet( ( inStr != start ) && ( ( (int)( inStr - start ) ) <= 10 ), exit, err = kMalformedErr );
31622 
31623 	*outScope = scope;
31624 	*outStr   = inStr;
31625 	err = kNoErr;
31626 
31627 exit:
31628 	return( err );
31629 #endif
31630 }
31631 
31632 //===========================================================================================================================
31633 //	_StringToIPv6Address
31634 //
31635 //	Note: This was copied from CoreUtils because the StringToIPv6Address function is currently not exported in the framework.
31636 //===========================================================================================================================
31637 
31638 static OSStatus
_StringToIPv6Address(const char * inStr,StringToIPAddressFlags inFlags,uint8_t outIPv6[16],uint32_t * outScope,int * outPort,int * outPrefix,const char ** outStr)31639 	_StringToIPv6Address(
31640 		const char *			inStr,
31641 		StringToIPAddressFlags	inFlags,
31642 		uint8_t					outIPv6[ 16 ],
31643 		uint32_t *				outScope,
31644 		int *					outPort,
31645 		int *					outPrefix,
31646 		const char **			outStr )
31647 {
31648 	OSStatus		err;
31649 	uint8_t			ipv6[ 16 ];
31650 	int				c;
31651 	int				hasScope;
31652 	uint32_t		scope;
31653 	int				hasPort;
31654 	int				port;
31655 	int				hasPrefix;
31656 	int				prefix;
31657 	int				hasBracket;
31658 	int				i;
31659 
31660 	require_action( inStr, exit, err = kParamErr );
31661 
31662 	if( *inStr == '[' ) ++inStr; // Skip a leading bracket for []-wrapped addresses (e.g. "[::1]:80").
31663 
31664 	// Parse the address-only part of the address (e.g. "1::1").
31665 
31666 	err = _ParseIPv6Address( inStr, !( inFlags & kStringToIPAddressFlagsNoIPv4Mapped ), ipv6, &inStr );
31667 	require_noerr_quiet( err, exit );
31668 	c = *inStr;
31669 
31670 	// Parse the scope, port, or prefix length.
31671 
31672 	hasScope	= 0;
31673 	scope		= 0;
31674 	hasPort		= 0;
31675 	port		= 0;
31676 	hasPrefix	= 0;
31677 	prefix		= 0;
31678 	hasBracket	= 0;
31679 	for( ;; )
31680 	{
31681 		if( c == '%' )		// Scope (e.g. "%en0" or "%5")
31682 		{
31683 			require_action_quiet( !hasScope, exit, err = kMalformedErr );
31684 			require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoScope ), exit, err = kUnexpectedErr );
31685 			++inStr;
31686 			err = _ParseIPv6Scope( inStr, &scope, &inStr );
31687 			require_noerr_quiet( err, exit );
31688 			hasScope = 1;
31689 			c = *inStr;
31690 		}
31691 		else if( c == ':' )	// Port (e.g. ":80")
31692 		{
31693 			require_action_quiet( !hasPort, exit, err = kMalformedErr );
31694 			require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPort ), exit, err = kUnexpectedErr );
31695 			while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) port = ( port * 10 ) + ( c - '0' );
31696 			require_action_quiet( port <= 65535, exit, err = kRangeErr );
31697 			hasPort = 1;
31698 		}
31699 		else if( c == '/' )	// Prefix Length (e.g. "/64")
31700 		{
31701 			require_action_quiet( !hasPrefix, exit, err = kMalformedErr );
31702 			require_action_quiet( !( inFlags & kStringToIPAddressFlagsNoPrefix ), exit, err = kUnexpectedErr );
31703 			while( ( ( c = *( ++inStr ) ) != '\0' ) && ( ( c >= '0' ) && ( c <= '9' ) ) ) prefix = ( prefix * 10 ) + ( c - '0' );
31704 			require_action_quiet( ( prefix >= 0 ) && ( prefix <= 128 ), exit, err = kRangeErr );
31705 			hasPrefix = 1;
31706 		}
31707 		else if( c == ']' )
31708 		{
31709 			require_action_quiet( !hasBracket, exit, err = kMalformedErr );
31710 			hasBracket = 1;
31711 			c = *( ++inStr );
31712 		}
31713 		else
31714 		{
31715 			break;
31716 		}
31717 	}
31718 
31719 	// Return the results. Only fill in scope/port/prefix results if the info was found to allow for defaults.
31720 
31721 	if( outIPv6 )				 for( i = 0; i < 16; ++i ) outIPv6[ i ] = ipv6[ i ];
31722 	if( outScope  && hasScope )  *outScope	= scope;
31723 	if( outPort   && hasPort )   *outPort	= port;
31724 	if( outPrefix && hasPrefix ) *outPrefix	= prefix;
31725 	if( outStr )				 *outStr	= inStr;
31726 	err = kNoErr;
31727 
31728 exit:
31729 	return( err );
31730 }
31731 
31732 //===========================================================================================================================
31733 //	_StringArray_Free
31734 //
31735 //	Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
31736 //===========================================================================================================================
31737 
_StringArray_Free(char ** inArray,size_t inCount)31738 static void	_StringArray_Free( char **inArray, size_t inCount )
31739 {
31740 	size_t		i;
31741 
31742 	for( i = 0; i < inCount; ++i )
31743 	{
31744 		free( inArray[ i ] );
31745 	}
31746 	if( inCount > 0 ) free( inArray );
31747 }
31748 
31749 //===========================================================================================================================
31750 //	_ParseQuotedEscapedString
31751 //
31752 //	Note: This was copied from CoreUtils because it's currently not exported in the framework.
31753 //===========================================================================================================================
31754 
31755 static Boolean
_ParseQuotedEscapedString(const char * inSrc,const char * inEnd,const char * inDelimiters,char * inBuf,size_t inMaxLen,size_t * outCopiedLen,size_t * outTotalLen,const char ** outSrc)31756 	_ParseQuotedEscapedString(
31757 		const char *	inSrc,
31758 		const char *	inEnd,
31759 		const char *	inDelimiters,
31760 		char *			inBuf,
31761 		size_t			inMaxLen,
31762 		size_t *		outCopiedLen,
31763 		size_t *		outTotalLen,
31764 		const char **	outSrc )
31765 {
31766 	const unsigned char *		src;
31767 	const unsigned char *		end;
31768 	unsigned char *				dst;
31769 	unsigned char *				lim;
31770 	unsigned char				c;
31771 	unsigned char				c2;
31772 	size_t						totalLen;
31773 	Boolean						singleQuote;
31774 	Boolean						doubleQuote;
31775 
31776 	if( inEnd == NULL ) inEnd = inSrc + strlen( inSrc );
31777 	src = (const unsigned char *) inSrc;
31778 	end = (const unsigned char *) inEnd;
31779 	dst = (unsigned char *) inBuf;
31780 	lim = dst + inMaxLen;
31781 	while( ( src < end ) && isspace_safe( *src ) ) ++src; // Skip leading spaces.
31782 	if( src >= end ) return( false );
31783 
31784 	// Parse each argument from the string.
31785 	//
31786 	// See <http://resources.mpi-inf.mpg.de/departments/rg1/teaching/unixffb-ss98/quoting-guide.html> for details.
31787 
31788 	totalLen = 0;
31789 	singleQuote = false;
31790 	doubleQuote = false;
31791 	while( src < end )
31792 	{
31793 		c = *src++;
31794 		if( singleQuote )
31795 		{
31796 			// Single quotes protect everything (even backslashes, newlines, etc.) except single quotes.
31797 
31798 			if( c == '\'' )
31799 			{
31800 				singleQuote = false;
31801 				continue;
31802 			}
31803 		}
31804 		else if( doubleQuote )
31805 		{
31806 			// Double quotes protect everything except double quotes and backslashes. A backslash can be
31807 			// used to protect " or \ within double quotes. A backslash-newline pair disappears completely.
31808 			// A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
31809 			// A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
31810 			// A backslash that does not precede ", \, x, X, or a newline is taken literally.
31811 
31812 			if( c == '"' )
31813 			{
31814 				doubleQuote = false;
31815 				continue;
31816 			}
31817 			else if( c == '\\' )
31818 			{
31819 				if( src < end )
31820 				{
31821 					c2 = *src;
31822 					if( ( c2 == '"' ) || ( c2 == '\\' ) )
31823 					{
31824 						++src;
31825 						c = c2;
31826 					}
31827 					else if( c2 == '\n' )
31828 					{
31829 						++src;
31830 						continue;
31831 					}
31832 					else if( ( c2 == 'x' ) || ( c2 == 'X' ) )
31833 					{
31834 						++src;
31835 						c = c2;
31836 						if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
31837 						{
31838 							c = HexPairToByte( src );
31839 							src += 2;
31840 						}
31841 					}
31842 					else if( isoctal_safe( c2 ) )
31843 					{
31844 						if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
31845 						{
31846 							c = OctalTripleToByte( src );
31847 							src += 3;
31848 						}
31849 					}
31850 				}
31851 			}
31852 		}
31853 		else if( strchr( inDelimiters, c ) )
31854 		{
31855 			break;
31856 		}
31857 		else if( c == '\\' )
31858 		{
31859 			// A backslash protects the next character, except a newline, x, X and 2 hex bytes or 3 octal bytes.
31860 			// A backslash followed by a newline disappears completely.
31861 			// A backslash followed by x or X and 2 hex digits (e.g. "\x1f") is stored as that hex byte.
31862 			// A backslash followed by 3 octal digits (e.g. "\377") is stored as that octal byte.
31863 
31864 			if( src < end )
31865 			{
31866 				c = *src;
31867 				if( c == '\n' )
31868 				{
31869 					++src;
31870 					continue;
31871 				}
31872 				else if( ( c == 'x' ) || ( c == 'X' ) )
31873 				{
31874 					++src;
31875 					if( ( ( end - src ) >= 2 ) && IsHexPair( src ) )
31876 					{
31877 						c = HexPairToByte( src );
31878 						src += 2;
31879 					}
31880 				}
31881 				else if( isoctal_safe( c ) )
31882 				{
31883 					if( ( ( end - src ) >= 3 ) && IsOctalTriple( src ) )
31884 					{
31885 						c = OctalTripleToByte( src );
31886 						src += 3;
31887 					}
31888 					else
31889 					{
31890 						++src;
31891 					}
31892 				}
31893 				else
31894 				{
31895 					++src;
31896 				}
31897 			}
31898 		}
31899 		else if( c == '\'' )
31900 		{
31901 			singleQuote = true;
31902 			continue;
31903 		}
31904 		else if( c == '"' )
31905 		{
31906 			doubleQuote = true;
31907 			continue;
31908 		}
31909 
31910 		if( dst < lim )
31911 		{
31912 			if( inBuf ) *dst = c;
31913 			++dst;
31914 		}
31915 		++totalLen;
31916 	}
31917 
31918 	if( outCopiedLen )	*outCopiedLen	= (size_t)( dst - ( (unsigned char *) inBuf ) );
31919 	if( outTotalLen )	*outTotalLen	= totalLen;
31920 	if( outSrc )		*outSrc			= (const char *) src;
31921 	return( true );
31922 }
31923 
31924 //===========================================================================================================================
31925 //	_ServerSocketOpenEx2
31926 //
31927 //	Note: Based on ServerSocketOpenEx() from CoreUtils. Added parameter to not use SO_REUSEPORT.
31928 //===========================================================================================================================
31929 
31930 static OSStatus
_ServerSocketOpenEx2(int inFamily,int inType,int inProtocol,const void * inAddr,int inPort,int * outPort,int inRcvBufSize,Boolean inNoPortReuse,SocketRef * outSock)31931 	_ServerSocketOpenEx2(
31932 		int				inFamily,
31933 		int				inType,
31934 		int				inProtocol,
31935 		const void *	inAddr,
31936 		int				inPort,
31937 		int *			outPort,
31938 		int				inRcvBufSize,
31939 		Boolean			inNoPortReuse,
31940 		SocketRef *		outSock )
31941 {
31942 	OSStatus		err;
31943 	int				port;
31944 	SocketRef		sock;
31945 	int				name;
31946 	int				option;
31947 	sockaddr_ip		sip;
31948 	socklen_t		len;
31949 
31950 	port = ( inPort < 0 ) ? -inPort : inPort; // Negated port number means "try this port, but allow dynamic".
31951 
31952 	sock = socket( inFamily, inType, inProtocol );
31953 	err = map_socket_creation_errno( sock );
31954 	require_noerr_quiet( err, exit );
31955 
31956 #if( defined( SO_NOSIGPIPE ) )
31957 	setsockopt( sock, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, (socklen_t) sizeof( int ) );
31958 #endif
31959 
31960 	err = SocketMakeNonBlocking( sock );
31961 	require_noerr( err, exit );
31962 
31963 	// Set receive buffer size. This has to be done on the listening socket *before* listen is called because
31964 	// accept does not return until after the window scale option is exchanged during the 3-way handshake.
31965 	// Since accept returns a new socket, the only way to use a larger window scale option is to set the buffer
31966 	// size on the listening socket since SO_RCVBUF is inherited by the accepted socket. See UNPv1e3 Section 7.5.
31967 
31968 	err = SocketSetBufferSize( sock, SO_RCVBUF, inRcvBufSize );
31969 	check_noerr( err );
31970 
31971 	// Allow port or address reuse because we may bind separate IPv4 and IPv6 sockets to the same port.
31972 
31973 	if( ( inType != SOCK_DGRAM ) || !inNoPortReuse )
31974 	{
31975 		option = 1;
31976 		name = ( inType == SOCK_DGRAM ) ? SO_REUSEPORT : SO_REUSEADDR;
31977 		err = setsockopt( sock, SOL_SOCKET, name, (char *) &option, (socklen_t) sizeof( option ) );
31978 		err = map_socket_noerr_errno( sock, err );
31979 		require_noerr( err, exit );
31980 	}
31981 
31982 	if( inFamily == AF_INET )
31983 	{
31984 		// Bind to the port. If it fails, retry with a dynamic port.
31985 
31986 		memset( &sip.v4, 0, sizeof( sip.v4 ) );
31987 		SIN_LEN_SET( &sip.v4 );
31988 		sip.v4.sin_family		= AF_INET;
31989 		sip.v4.sin_port			= htons( (uint16_t) port );
31990 		sip.v4.sin_addr.s_addr	= inAddr ? *( (const uint32_t *) inAddr ) : htonl( INADDR_ANY );
31991 		err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
31992 		err = map_socket_noerr_errno( sock, err );
31993 		if( err && ( inPort < 0 ) )
31994 		{
31995 			sip.v4.sin_port = 0;
31996 			err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
31997 			err = map_socket_noerr_errno( sock, err );
31998 		}
31999 		require_noerr( err, exit );
32000 	}
32001 #if( defined( AF_INET6 ) )
32002 	else if( inFamily == AF_INET6 )
32003 	{
32004 		// Restrict this socket to IPv6 only because we're going to use a separate socket for IPv4.
32005 
32006 		option = 1;
32007 		err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &option, (socklen_t) sizeof( option ) );
32008 		err = map_socket_noerr_errno( sock, err );
32009 		require_noerr( err, exit );
32010 
32011 		// Bind to the port. If it fails, retry with a dynamic port.
32012 
32013 		memset( &sip.v6, 0, sizeof( sip.v6 ) );
32014 		SIN6_LEN_SET( &sip.v6 );
32015 		sip.v6.sin6_family	= AF_INET6;
32016 		sip.v6.sin6_port	= htons( (uint16_t) port );
32017 		sip.v6.sin6_addr	= inAddr ? *( (const struct in6_addr *) inAddr ) : in6addr_any;
32018 		err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
32019 		err = map_socket_noerr_errno( sock, err );
32020 		if( err && ( inPort < 0 ) )
32021 		{
32022 			sip.v6.sin6_port = 0;
32023 			err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
32024 			err = map_socket_noerr_errno( sock, err );
32025 		}
32026 		require_noerr( err, exit );
32027 	}
32028 #endif
32029 	else
32030 	{
32031 		dlogassert( "Unsupported family: %d", inFamily );
32032 		err = kUnsupportedErr;
32033 		goto exit;
32034 	}
32035 
32036 	if( inType == SOCK_STREAM )
32037 	{
32038 		err = listen( sock, SOMAXCONN );
32039 		err = map_socket_noerr_errno( sock, err );
32040 		if( err )
32041 		{
32042 			err = listen( sock, 5 );
32043 			err = map_socket_noerr_errno( sock, err );
32044 			require_noerr( err, exit );
32045 		}
32046 	}
32047 
32048 	if( outPort )
32049 	{
32050 		len = (socklen_t) sizeof( sip );
32051 		err = getsockname( sock, &sip.sa, &len );
32052 		err = map_socket_noerr_errno( sock, err );
32053 		require_noerr( err, exit );
32054 
32055 		*outPort = SockAddrGetPort( &sip );
32056 	}
32057 	*outSock = sock;
32058 	sock = kInvalidSocketRef;
32059 
32060 exit:
32061 	ForgetSocket( &sock );
32062 	return( err );
32063 }
32064 
32065 //===========================================================================================================================
32066 //	_memdup
32067 //
32068 //	Note: This was copied from CoreUtils because it's currently not exported in the framework.
32069 //===========================================================================================================================
32070 
_memdup(const void * inPtr,size_t inLen)32071 static void *	_memdup( const void *inPtr, size_t inLen )
32072 {
32073 	void *		mem;
32074 
32075 	mem = malloc( ( inLen > 0 ) ? inLen : 1 ); // If inLen is 0, use 1 since malloc( 0 ) is not well defined.
32076 	require( mem, exit );
32077 	if( inLen > 0 ) memcpy( mem, inPtr, inLen );
32078 
32079 exit:
32080 	return( mem );
32081 }
32082 
32083 //===========================================================================================================================
32084 //	_memicmp
32085 //
32086 //	Note: This was copied from CoreUtils because it's currently not exported in the framework.
32087 //===========================================================================================================================
32088 
_memicmp(const void * inP1,const void * inP2,size_t inLen)32089 static int	_memicmp( const void *inP1, const void *inP2, size_t inLen )
32090 {
32091 	const unsigned char *		p1;
32092 	const unsigned char *		e1;
32093 	const unsigned char *		p2;
32094 	int							c1;
32095 	int							c2;
32096 
32097 	p1 = (const unsigned char *) inP1;
32098 	e1 = p1 + inLen;
32099 	p2 = (const unsigned char *) inP2;
32100 	while( p1 < e1 )
32101 	{
32102 		c1 = *p1++;
32103 		c2 = *p2++;
32104 		c1 = tolower( c1 );
32105 		c2 = tolower( c2 );
32106 		if( c1 < c2 ) return( -1 );
32107 		if( c1 > c2 ) return(  1 );
32108 	}
32109 	return( 0 );
32110 }
32111 
32112 //===========================================================================================================================
32113 //	_FNV1
32114 //
32115 //	Note: This was copied from CoreUtils because it's currently not exported in the framework.
32116 //===========================================================================================================================
32117 
_FNV1(const void * inData,size_t inSize)32118 static uint32_t	_FNV1( const void *inData, size_t inSize )
32119 {
32120 	const uint8_t *				src = (const uint8_t *) inData;
32121 	const uint8_t * const		end = src + inSize;
32122 	uint32_t					hash;
32123 
32124 	hash = 0x811c9dc5U;
32125 	while( src != end )
32126 	{
32127 		hash *= 0x01000193;
32128 		hash ^= *src++;
32129 	}
32130 	return( hash );
32131 }
32132